created register namespace, moved game_lexicom stuff into it

This commit is contained in:
Richard Harrington 2013-08-07 11:36:03 -04:00
parent dc5531b3b5
commit 382a70bd34
7 changed files with 134 additions and 139 deletions

View File

@ -1,7 +1,8 @@
(ns robotwar.brain
(:use [clojure.string :only [join]]
[clojure.pprint :only [pprint]])
(:require [robotwar.assembler :as assembler]))
(:require [robotwar.assembler :as assembler]
[robotwar.register :as register]))
(def op-map (into {} (for [op assembler/op-commands]
[op (case op
@ -9,20 +10,6 @@
"#" not=
(-> op read-string eval))])))
(defn read-register
"a function to query the robot housing this brain, for information
from the registers. takes a register and a world, and returns the
result of running the register's read function on the world."
[{read :read} world]
(read world))
(defn write-register
"a function to create a new world when the brain pushes data to a register.
takes a register, a world, and data, and returns the result of running the
register's write function on the data and the world."
[{write :write} world data]
(write world data))
(defn init-brain
"initialize the brain, meaning all the internal state variables that go along
with the robot program when it's running, except for the registers,
@ -39,7 +26,7 @@
(case arg-type
:label (labels arg-val)
:number arg-val
:register (read-register (registers arg-val) world)
:register (register/read-register (registers arg-val) world)
nil))
(defn step-brain
@ -74,7 +61,7 @@
("=" ">" "<" "#") (if ((op-map command) acc (resolve arg))
(assoc-world-brain {:instr-ptr (inc instr-ptr)})
(assoc-world-brain {:instr-ptr (+ instr-ptr 2)}))
"TO" (write-register
"TO" (register/write-register
(registers (:val arg))
(assoc-world-brain {:instr-ptr (inc instr-ptr)})
acc))))))

View File

@ -4,8 +4,8 @@
[robotwar.brain :as brain]
[robotwar.robot :as robot]
[robotwar.world :as world]
[robotwar.game-lexicon :as game-lexicon]
[robotwar.brain-test :as brain-test]))
[robotwar.brain-test :as brain-test]
[robotwar.register :as register]))
; this is a hacky place for messing with stuff. currently imports
; all the test data from brain-test.
@ -14,8 +14,8 @@
(def robots (:robots world))
(def robot (robots 0))
(def registers (:registers robot))
(def rr brain/read-register)
(def wr brain/write-register)
(def rr register/read-register)
(def wr register/write-register)
(defn rv [reg-name] (get-in registers [reg-name :val]))

View File

@ -1,15 +0,0 @@
(ns robotwar.game-lexicon)
; The reason that the reg-names vector is not composed from concatting
; the other two is that the order in reg-names is important for indexing,
; and the X and Y registers have to go in the right place.
(def storage-reg-names [ "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
"N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "Z"])
(def special-purpose-reg-names [ "DATA" "X" "Y" "AIM" "SHOT" "RADAR" "DAMAGE" "SPEEDX" "SPEEDY" "RANDOM" "INDEX" ])
(def reg-names [ "DATA"
"A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
"N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
"AIM" "SHOT" "RADAR" "DAMAGE" "SPEEDX" "SPEEDY" "RANDOM" "INDEX" ])

104
src/robotwar/register.clj Normal file
View File

@ -0,0 +1,104 @@
(ns robotwar.register)
(def storage-reg-names [ "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
"N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "Z"])
(def reg-names [ "DATA"
"A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
"N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
"AIM" "SHOT" "RADAR" "DAMAGE" "SPEEDX" "SPEEDY" "RANDOM" "INDEX" ])
(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 :registers 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 read-register
"a function to query the robot for information
from the registers. takes a register and a world, and returns the
result of running the register's read function on the world."
[{read :read} world]
(read world))
(defn write-register
"a function to create a new world when data is pushed to a register.
takes a register, a world, and data, and returns the result of running the
register's write function on the data and the world."
[{write :write} world data]
(write world data))
(defn get-robot [world path-to-val]
(get-in world (take 2 path-to-val)))
(defn get-registers [world path-to-val]
(get-in world (take 3 path-to-val)))
(defn init-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-read-only-register
"returns a register which has no effect (i.e. returns the world it was given)
when it is written to, but which returns a particular robot field when it is read."
[reg-name robot-idx field-name val]
(init-register reg-name robot-idx
(fn [world path-to-val]
(field-name (get-robot world path-to-val)))
(fn [world _ _] world)
val))
(defn init-registers
[robot-idx attributes]
(let [storage-registers (into {} (for [reg-name storage-reg-names]
(init-default-register reg-name robot-idx)))]
(into storage-registers
[
; AIM, INDEX, SPEEDX and SPEEDY.
; AIM and INDEX's specialized behaviors are only when they're used by
; SHOT and DATA, respectively. In themselves, they're only default registers.
; Likewise, SPEEDX and SPEEDY are used later in step-robot to determine
; the appropriate acceleration, which may have to applied over several ticks.
(init-default-register "AIM" robot-idx)
(init-default-register "INDEX" robot-idx)
(init-default-register "SPEEDX" robot-idx)
(init-default-register "SPEEDY" robot-idx)
; DATA
(letfn [(target-register [world path-to-val]
(let [registers (get-registers world path-to-val)
index-register (registers "INDEX")]
(registers (reg-names (:val index-register)))))]
(init-register "DATA" robot-idx
(fn [world path-to-val]
(read-register (target-register world path-to-val) world))
(fn [world path-to-val data]
(write-register (target-register world) world data))
0))
; RANDOM
(init-register "RANDOM" robot-idx
(fn [world path-to-val]
(rand-int (get-in world path-to-val)))
assoc-in
0)
; X and Y and DAMAGE
(init-read-only-register "X" robot-idx :pos-x (:pos-x attributes))
(init-read-only-register "Y" robot-idx :pos-y (:pos-y attributes))
(init-read-only-register "DAMAGE" robot-idx :damage (:damage attributes))])))
; TODO: SHOT AND RADAR

View File

@ -1,94 +1,12 @@
(ns robotwar.robot
(:use [clojure.string :only [join]]
[clojure.pprint :only [pprint]])
(:require [robotwar.brain :as brain]
[robotwar.game-lexicon :as game-lexicon]))
[robotwar.register :as register]))
; TICK_DURATION is in seconds. MAX_ACCEL is in decimeters per second per second.
; TODO: should be passed in from some higher level module, or a config module.
(def TICK_DURATION 1)
(def MAX_ACCEL 40)
(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 :registers 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 get-robot [world path-to-val]
(get-in world (take 2 path-to-val)))
(defn get-registers [world path-to-val]
(get-in world (take 3 path-to-val)))
(defn init-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-read-only-register
"returns a register which has no effect (i.e. returns the world it was given)
when it is written to, but which returns a particular robot field when it is read."
[reg-name robot-idx field-name val]
(init-register reg-name robot-idx
(fn [world path-to-val]
(field-name (get-robot world path-to-val)))
(fn [world _ _] world)
val))
(defn init-registers
[robot-idx attributes]
(let [storage-registers (into {} (for [reg-name game-lexicon/storage-reg-names]
(init-default-register reg-name robot-idx)))]
(into storage-registers
[
; AIM, INDEX, SPEEDX and SPEEDY.
; AIM and INDEX's specialized behaviors are only when they're used by
; SHOT and DATA, respectively. In themselves, they're only default registers.
; Likewise, SPEEDX and SPEEDY are used later in step-robot to determine
; the appropriate acceleration, which may have to applied over several ticks.
(init-default-register "AIM" robot-idx)
(init-default-register "INDEX" robot-idx)
(init-default-register "SPEEDX" robot-idx)
(init-default-register "SPEEDY" robot-idx)
; DATA
(letfn [(target-register [world path-to-val]
(let [registers (get-registers world path-to-val)
index-register (registers "INDEX")]
(registers (game-lexicon/reg-names (:val index-register)))))]
(init-register "DATA" robot-idx
(fn [world path-to-val]
(brain/read-register (target-register world path-to-val) world))
(fn [world path-to-val data]
(brain/write-register (target-register world) world data))
0))
; RANDOM
(init-register "RANDOM" robot-idx
(fn [world path-to-val]
(rand-int (get-in world path-to-val)))
assoc-in
0)
; X and Y and DAMAGE
(init-read-only-register "X" robot-idx :pos-x (:pos-x attributes))
(init-read-only-register "Y" robot-idx :pos-y (:pos-y attributes))
(init-read-only-register "DAMAGE" robot-idx :damage (:damage attributes))])))
; TODO: SHOT AND RADAR
; yay classical mechanics
(defn time-to-reach-desired-v
@ -144,8 +62,8 @@
:v-x 0
:v-y 0
:damage (:damage attributes)
:registers (init-registers idx attributes)
:brain (brain/init-brain src-code game-lexicon/reg-names)})
:registers (register/init-registers idx attributes)
:brain (brain/init-brain src-code register/reg-names)})
(defn step-robot
"takes a robot and a world and returns the new state of the world
@ -158,10 +76,10 @@
world
(let [new-world (brain/step-brain robot world)
new-robot (get-in new-world [:robots robot-idx])
desired-v-x (brain/read-register
desired-v-x (register/read-register
(get-in new-robot [:registers "SPEEDX"])
new-world)
desired-v-y (brain/read-register
desired-v-y (register/read-register
(get-in new-robot [:registers "SPEEDY"])
new-world)
[pos-x v-x] (d-and-v-given-desired-v (:v-x robot) desired-v-x

View File

@ -2,7 +2,7 @@
(:use (clojure [string :only [join]]
[test])
[robotwar.assembler])
(:require [robotwar.game-lexicon :as game-lexicon]))
(:require [robotwar.register :as register]))
(def line1 "IF DAMAGE # D GOTO MOVE ; comment or something")
(def line2 "AIM-17 TO AIM ; other comment")
@ -212,47 +212,47 @@
(deftest parse-token-register
(testing "parsing register token"
(is (= (parse-token {:token-str "AIM"} game-lexicon/reg-names)
(is (= (parse-token {:token-str "AIM"} register/reg-names)
{:val "AIM", :type :register}))))
(deftest parse-token-command-word
(testing "parsing command token (word)"
(is (= (parse-token {:token-str "GOTO"} game-lexicon/reg-names)
(is (= (parse-token {:token-str "GOTO"} register/reg-names)
{:val "GOTO", :type :command}))))
(deftest parse-token-command-operator
(testing "parsing command token (operator)"
(is (= (parse-token {:token-str "#"} game-lexicon/reg-names)
(is (= (parse-token {:token-str "#"} register/reg-names)
{:val "#", :type :command}))))
(deftest parse-token-number
(testing "parsing number token"
(is (= (parse-token {:token-str "-17"}game-lexicon/reg-names)
(is (= (parse-token {:token-str "-17"}register/reg-names)
{:val -17, :type :number}))))
(deftest parse-token-label
(testing "parsing label token"
(is (= (parse-token {:token-str "SCAN"} game-lexicon/reg-names)
(is (= (parse-token {:token-str "SCAN"} register/reg-names)
{:val "SCAN", :type :label}))))
(deftest parse-token-error
(testing "parsing error token"
(is (= (parse-token {:token-str "-GOTO"} game-lexicon/reg-names)
(is (= (parse-token {:token-str "-GOTO"} register/reg-names)
{:val "Invalid word or symbol", :type :error}))))
(deftest parse-tokens-minus-sign
(testing "parsing tokens with a binary minus sign"
(is (= (parse lexed-tokens2 game-lexicon/reg-names)
(is (= (parse lexed-tokens2 register/reg-names)
parsed-tokens2))))
(deftest parse-tokens-negative-sign
(testing "parsing tokens with a unary negative sign"
(is (= (parse lexed-tokens3 game-lexicon/reg-names)
(is (= (parse lexed-tokens3 register/reg-names)
parsed-tokens3))))
(deftest parse-tokens-error
(testing "parsing tokens with an invalid operator"
(is (= (parse lexed-tokens4 game-lexicon/reg-names)
(is (= (parse lexed-tokens4 register/reg-names)
parsed-tokens4))))
(def minus-sign-disambiguated-tokens2 parsed-tokens2)
@ -294,17 +294,17 @@
(deftest assemble-test-success
(testing "compiling successfully"
(is (= (assemble (join "\n" [line1 line2 line3]) game-lexicon/reg-names)
(is (= (assemble (join "\n" [line1 line2 line3]) register/reg-names)
multi-line-assembled))))
(deftest assemble-test-failure
(testing "assemble results in error"
(is (= (assemble (join "\n" [line1 line2 line3 line4]) game-lexicon/reg-names)
(is (= (assemble (join "\n" [line1 line2 line3 line4]) register/reg-names)
multi-line-assembled-error))))
(deftest preserving-line-and-pos-metadata-test
(testing "line and pos metadata preserved through assembly process"
(is (= (meta (get-in (assemble (join "\n" [line1 line2 line3]) game-lexicon/reg-names)
(is (= (meta (get-in (assemble (join "\n" [line1 line2 line3]) register/reg-names)
[:instrs 8 1]))
{:line 3, :pos 14}))))

View File

@ -1,7 +1,8 @@
(ns robotwar.brain-test
(:use [clojure.test]
[robotwar.brain])
(:require [robotwar.world :as world]))
(:require [robotwar.world :as world]
[robotwar.register :as register]))
(def src-codes [ ; program 0: multi-use program
" START
@ -69,8 +70,8 @@
(testing "push to random register and pull a series of numbers all different
from random register"
(let [random-register (get-in sample-robot [:registers "RANDOM"])
new-world (write-register random-register sample-world 1000)
random-nums (repeatedly 5 (partial read-register random-register new-world))]
new-world (register/write-register random-register sample-world 1000)
random-nums (repeatedly 5 (partial register/read-register random-register new-world))]
(is (= (get-in new-world [:robots 0 :registers "RANDOM" :val])
1000))
(is (every? #(< -1 % 1000) random-nums)))))