mirror of
https://github.com/richardharrington/robotwar.git
synced 2024-05-28 23:41:31 +00:00
created register namespace, moved game_lexicom stuff into it
This commit is contained in:
parent
dc5531b3b5
commit
382a70bd34
|
@ -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))))))
|
||||
|
|
|
@ -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]))
|
||||
|
||||
|
|
|
@ -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
104
src/robotwar/register.clj
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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}))))
|
||||
|
||||
|
|
|
@ -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)))))
|
||||
|
|
Loading…
Reference in New Issue
Block a user