got basic skeletons of robot and world up and running, all tests but one passing in brain-test

This commit is contained in:
Richard Harrington 2013-08-05 00:26:05 -04:00
parent 82b998246a
commit 28efa906c2
3 changed files with 111 additions and 159 deletions

View File

@ -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)))

View File

@ -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)))

View File

@ -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