mirror of
https://github.com/richardharrington/robotwar.git
synced 2024-05-28 23:41:31 +00:00
got basic skeletons of robot and world up and running, all tests but one passing in brain-test
This commit is contained in:
parent
82b998246a
commit
28efa906c2
|
@ -2,78 +2,48 @@
|
|||
(:use [clojure.string :only [join]]
|
||||
(robotwar brain game-lexicon)))
|
||||
|
||||
; TODO: Fill out this module.
|
||||
; Probably it will consist mostly of
|
||||
; 0) An init function, to initialize all the fields containing
|
||||
; the external robot information. I think this should be
|
||||
; SEPARATE FROM THE REGISTERS, even the ones that are similar.
|
||||
; 1) Specialty read and write functions for the registers
|
||||
; 2) Code to deal with the flag when the robot fires a shot (probably this
|
||||
; will just involve passing the flag up to the world)
|
||||
; 3) Something else I can't remember. Maybe put some of the init-register
|
||||
; and default-register and register-handling-in-general code in this
|
||||
; module, instead of in brain. Something to think about.
|
||||
; 4) GENERAL NOTES: A) CHANGE STRINGS TO KEYWORDS EARLY ON.
|
||||
; B) CHANGE SOME OF THESE MODULE LOADINGS FROM
|
||||
; "USE" TO "REFER", SO THAT THEY HAVE TO USE
|
||||
; FULLY-QUALIFIED NAMES. THAT MIGHT MAKE THINGS
|
||||
; A BIT CLEARER. THE NAMES CAN BE SHORTENED QUITE A BIT,
|
||||
; WHEN LOADED INTO THE MODULES.
|
||||
;
|
||||
;(defn make-default-read [register]
|
||||
; "takes a register and returns the default version of its :read function,
|
||||
; which ignores the `world` parameter and just returns
|
||||
; the :val field from the register."
|
||||
; (fn [_]
|
||||
; (:val register)))
|
||||
;
|
||||
;(defn make-default-write [robot-idx reg-name]
|
||||
; "takes a robot-idx and a reg-name to locate a register, and
|
||||
; returns the default version of that register's :write function,
|
||||
; which takes a world parameter and a data value and returns the
|
||||
; world with the data value assoc'd into it."
|
||||
; (fn [world data]
|
||||
; (assoc-in world [:robots robot-idx :registers reg-name :val] data)))
|
||||
;
|
||||
;(def default-data 0)
|
||||
;
|
||||
;(defn default-register [robot-idx reg-name]
|
||||
; (init-register
|
||||
; reg-name))
|
||||
;
|
||||
;
|
||||
;(defn init-robot
|
||||
; [program x y]
|
||||
; {:pos-x x
|
||||
; :pos-y y
|
||||
; :veloc-x 0
|
||||
; :veloc-y 0
|
||||
; :accel-x 0
|
||||
; :accel-y 0
|
||||
; :damage 100})
|
||||
;
|
||||
;(defn init-world
|
||||
; "initialize all the variables for a robot world"
|
||||
; [width height programs]
|
||||
; {:width width
|
||||
; :height height
|
||||
; :shells []
|
||||
; :robots (vec (map-indexed (fn [idx program]
|
||||
; {:brain (init-brain
|
||||
; program
|
||||
; reg-names
|
||||
; {(init-register "X"
|
||||
; default-read
|
||||
; default-write
|
||||
; (rand-int width))
|
||||
; (init-register "Y"
|
||||
; default-read
|
||||
; default-write
|
||||
; (rand-int height))})
|
||||
; :icon (str idx)})
|
||||
; programs))})
|
||||
;
|
||||
;(defn tick-robot
|
||||
; [robot world]
|
||||
; (let [ticked (tick-brain robot world)]
|
||||
; ))
|
||||
(defn init-register
|
||||
"takes a reg-name and a robot-idx (needed to locate the register in the world),
|
||||
as well as a read-func, a write-func, and a val. The read-func and write-func
|
||||
take the same set of arguments as get-in and assoc-in, respectively.
|
||||
They are meant to be invoked from inside of interface functions which pass in all
|
||||
the parameters except the vector path to the register val, which is provided by
|
||||
the let clojure."
|
||||
[reg-name robot-idx read-func write-func val]
|
||||
(let [path-to-val [:robots robot-idx :register reg-name :val]]
|
||||
{reg-name {:read (fn [world]
|
||||
(read-func world path-to-val))
|
||||
:write (fn [world data]
|
||||
(write-func world path-to-val data))
|
||||
:val val}}))
|
||||
|
||||
(defn default-register
|
||||
"takes a reg-name and robot-idx, and returns a register with initial :val 0,
|
||||
whose read function returns :val and whose write function returns a world
|
||||
with its data argument pushed to :val"
|
||||
[reg-name robot-idx]
|
||||
(init-register reg-name robot-idx get-in assoc-in 0))
|
||||
|
||||
(defn init-robot
|
||||
[idx pos-x pos-y src-code]
|
||||
{:idx idx
|
||||
:pos-x pos-x
|
||||
:pos-y pos-y
|
||||
:veloc-x 0
|
||||
:veloc-y 0
|
||||
:accel-x 0
|
||||
:accel-y 0
|
||||
:damage 100
|
||||
;TODO: make some custom registers
|
||||
:registers (into {} (for [reg-name robotwar.game-lexicon/reg-names]
|
||||
(default-register reg-name idx)))
|
||||
:brain (robotwar.brain/init-brain src-code robotwar.game-lexicon/reg-names)})
|
||||
|
||||
(defn step-robot
|
||||
"takes a robot and a world and returns the new state of the world
|
||||
after the robot has taken its turn"
|
||||
[robot world]
|
||||
(if (<= (:damage robot) 0)
|
||||
world
|
||||
(robotwar.brain/step-brain robot world)))
|
||||
|
||||
|
|
|
@ -1,47 +1,57 @@
|
|||
(ns robotwar.world
|
||||
(:use [clojure.string :only [join]]
|
||||
(robotwar foundry brain robot game-lexicon)))
|
||||
;
|
||||
;(defn init-world
|
||||
; "initialize all the variables for a robot world"
|
||||
; [width height programs]
|
||||
; {:width width
|
||||
; :height height
|
||||
; :shells []
|
||||
; :robots (vec (map-indexed (fn [idx program]
|
||||
; {:brain (init-brain
|
||||
; program
|
||||
; reg-names
|
||||
; {(init-register "X"
|
||||
; default-read
|
||||
; default-write
|
||||
; (rand-int width))
|
||||
; (init-register "Y"
|
||||
; default-read
|
||||
; default-write
|
||||
; (rand-int height))})
|
||||
; :icon (str idx)})
|
||||
; programs))})
|
||||
;
|
||||
;(defn tick-world
|
||||
; "TODO"
|
||||
; [world-state])
|
||||
;
|
||||
;(defn arena-text-grid
|
||||
; "outputs the arena, with borders"
|
||||
; [{:keys [width height robots]}]
|
||||
; (let [horiz-border-char "-"
|
||||
; vert-border-char "+"
|
||||
; header-footer (apply str (repeat (+ width 2) horiz-border-char))
|
||||
; field (for [y (range height), x (range width)]
|
||||
; (some (fn [{{{robot-x "X" robot-y "Y"} :registers} :internal-state, icon :icon}]
|
||||
; (if (= [x y] [robot-x robot-y])
|
||||
; icon
|
||||
; " "))
|
||||
; robots))]
|
||||
; (str header-footer
|
||||
; "\n"
|
||||
; (join "\n" (map #(join (apply str %) (repeat 2 vert-border-char))
|
||||
; (partition width field)))
|
||||
; "\n"
|
||||
; header-footer)))
|
||||
(:use [clojure.string :only [join]])
|
||||
(:require [robotwar.robot]))
|
||||
|
||||
(defn init-world
|
||||
"initialize all the variables for a robot world."
|
||||
[width height programs]
|
||||
{:width width
|
||||
:height height
|
||||
:shells []
|
||||
:robots (vec (map-indexed (fn [idx program]
|
||||
(robotwar.robot/init-robot
|
||||
idx
|
||||
(rand-int width)
|
||||
(rand-int height)
|
||||
program))
|
||||
programs))
|
||||
:robot-idx 0})
|
||||
|
||||
(defn tick-world
|
||||
"TODO: fill this out quite a bit. Dealing with shells, for instance.
|
||||
We might want to change this to a system where we count by whole rounds
|
||||
(where each robot gets to go) rather than just a stream of worlds, one for
|
||||
each robot. Because otherwise, do we step the shells after every
|
||||
single robot has their turn?"
|
||||
[{:keys [robots robot-idx] :as world}]
|
||||
(assoc (robotwar.robot/step-robot (robots robot-idx) world)
|
||||
:robot-idx
|
||||
(mod (inc robot-idx) (count robots))))
|
||||
|
||||
(defn get-world
|
||||
"convenience function for identifying a world in a sequence of worlds
|
||||
by its round idx (where one round means all the robots have stepped)
|
||||
and its robot idx."
|
||||
[round-idx robot-idx worlds]
|
||||
(let [num-robots (count (:robots (nth worlds 0)))
|
||||
world-idx (+ (* round-idx num-robots) robot-idx)]
|
||||
(nth worlds world-idx)))
|
||||
|
||||
(defn arena-text-grid
|
||||
"outputs the arena, with borders"
|
||||
[{:keys [width height robots]}]
|
||||
(let [horiz-border-char "-"
|
||||
vert-border-char "+"
|
||||
header-footer (apply str (repeat (+ width 2) horiz-border-char))
|
||||
field (for [y (range height), x (range width)]
|
||||
(some (fn [{{{robot-x "X" robot-y "Y"} :registers} :internal-state, icon :icon}]
|
||||
(if (= [x y] [robot-x robot-y])
|
||||
icon
|
||||
" "))
|
||||
robots))]
|
||||
(str header-footer
|
||||
"\n"
|
||||
(join "\n" (map #(join (apply str %) (repeat 2 vert-border-char))
|
||||
(partition width field)))
|
||||
"\n"
|
||||
header-footer)))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
(ns robotwar.brain-test
|
||||
(:use [clojure.test]
|
||||
[robotwar.brain])
|
||||
(:require [robotwar.game-lexicon]))
|
||||
(:require [robotwar.world]))
|
||||
|
||||
(def src-codes [ ; program 0: multi-use program
|
||||
" START
|
||||
|
@ -26,64 +26,36 @@
|
|||
1 TO INDEX
|
||||
DATA " ])
|
||||
|
||||
(def len (count src-codes))
|
||||
(def idx-range (range len))
|
||||
|
||||
(def robot-register-maps
|
||||
(for [idx idx-range]
|
||||
(into {} (for [reg-name robotwar.game-lexicon/reg-names]
|
||||
(let [path-to-val [:robots idx :registers reg-name :val]]
|
||||
{reg-name {:read (fn [world]
|
||||
(get-in world path-to-val))
|
||||
:write (fn [world data]
|
||||
(assoc-in world path-to-val data))
|
||||
:val 0}})))))
|
||||
|
||||
(def brains (map #(init-brain % robotwar.game-lexicon/reg-names) src-codes))
|
||||
|
||||
(def robots (vec (map (fn [idx brain robot-registers]
|
||||
{:idx idx
|
||||
:brain brain
|
||||
:registers robot-registers})
|
||||
idx-range
|
||||
brains
|
||||
robot-register-maps)))
|
||||
|
||||
(def initial-world {:robots robots})
|
||||
|
||||
(def worlds (map first (iterate (fn [[{robots :robots :as world} idx]]
|
||||
[(step-brain (robots idx) world) (mod (inc idx) len)])
|
||||
[initial-world 0])))
|
||||
|
||||
(def get-world (fn [world-tick-idx robot-idx]
|
||||
(let [world-idx (+ (* world-tick-idx len) robot-idx)]
|
||||
(nth worlds world-idx))))
|
||||
(def initial-world (robotwar.world/init-world 256 256 src-codes))
|
||||
(def worlds (iterate robotwar.world/tick-world initial-world))
|
||||
|
||||
(deftest branching-test
|
||||
(testing "comparison statement should cause jump in instr-ptr"
|
||||
(is (= (get-in (get-world 4 0) [:robots 0 :brain :instr-ptr])
|
||||
(is (= (get-in (robotwar.world/get-world 4 0 worlds) [:robots 0 :brain :instr-ptr])
|
||||
5))))
|
||||
|
||||
(deftest arithmetic-test
|
||||
(testing "addition"
|
||||
(is (= (get-in (get-world 7 0) [:robots 0 :brain :acc])
|
||||
(is (= (get-in (robotwar.world/get-world 7 0 worlds) [:robots 0 :brain :acc])
|
||||
1))))
|
||||
|
||||
(deftest gosub-test
|
||||
(testing "gosub should move instr-ptr and add the return-ptr to the call stack"
|
||||
(is (let [{:keys [instr-ptr call-stack]} (get-in (get-world 5 0) [:robots 0 :brain])]
|
||||
(is (let [{:keys [instr-ptr call-stack]}
|
||||
(get-in (robotwar.world/get-world 5 0 worlds) [:robots 0 :brain])]
|
||||
(= [instr-ptr call-stack]
|
||||
[9 [6]])))))
|
||||
|
||||
(deftest endsub-test
|
||||
(testing "endsub pops instr-ptr off call stack and goes there"
|
||||
(is (let [{:keys [instr-ptr call-stack]} (get-in (get-world 9 0) [:robots 0 :brain])]
|
||||
(is (let [{:keys [instr-ptr call-stack]}
|
||||
(get-in (robotwar.world/get-world 9 0 worlds) [:robots 0 :brain])]
|
||||
(= [instr-ptr call-stack]
|
||||
[6 []])))))
|
||||
|
||||
(deftest push-test
|
||||
(testing "pushing number to register"
|
||||
(is (= (get-in (get-world 8 0) [:robots 0 :registers "A" :val])
|
||||
(is (= (get-in (robotwar.world/get-world 8 0 worlds) [:robots 0 :registers "A" :val])
|
||||
1))))
|
||||
|
||||
;(deftest random-test
|
||||
|
|
Loading…
Reference in New Issue
Block a user