Merge branch 'gnarly-refactoring'
This commit is contained in:
commit
e3c96eaa25
|
@ -0,0 +1,82 @@
|
|||
(ns robotwar.brain
|
||||
(:use [clojure.string :only [join]]
|
||||
[clojure.pprint :only [pprint]]
|
||||
[robotwar.kernel-lexicon]))
|
||||
|
||||
(def op-map (into {} (for [op robotwar.kernel-lexicon/op-commands]
|
||||
[op (case op
|
||||
"/" #(int (Math/round (float (/ %1 %2))))
|
||||
"#" not=
|
||||
(-> op read-string eval))])))
|
||||
|
||||
(defn read-register
|
||||
"a function to query the robot housing this brain, for information
|
||||
from the registers. takes a reg-name, a robot and a world,
|
||||
and returns the running of 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 reg-name, a robot, a world, and data,
|
||||
and returns the running of 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,
|
||||
which are queried from the world (or the robot -- haven't decided yet)."
|
||||
[program]
|
||||
{:acc 0
|
||||
:instr-ptr 0
|
||||
:call-stack []
|
||||
:program program})
|
||||
|
||||
(defn resolve-arg [{arg-val :val arg-type :type} registers labels world]
|
||||
"resolves an instruction argument to a numeric value
|
||||
(either an arithmetic or logical comparison operand, or an instruction pointer)."
|
||||
(case arg-type
|
||||
:label (labels arg-val)
|
||||
:number arg-val
|
||||
:register (read-register (registers arg-val) world)
|
||||
nil))
|
||||
|
||||
(defn step-brain
|
||||
"takes a `world` and a pathway to a brain in that world, called `brain-path`.
|
||||
|
||||
Only the brain (the internal state of the robot)
|
||||
will be different when we pass it back, for all of the operations
|
||||
except 'TO', which may also alter the external state of the robot, or the wider world.
|
||||
|
||||
(returns the current state of the world untouched if the instruction pointer
|
||||
has gone beyond the end of the program. TODO: maybe have an error for that."
|
||||
|
||||
[robot world]
|
||||
(let [{:keys [registers brain]} robot
|
||||
{:keys [program acc instr-ptr call-stack]} brain
|
||||
{:keys [instrs labels]} program]
|
||||
;(println acc instr-ptr call-stack instrs labels program brain robot)
|
||||
(if (>= instr-ptr (count instrs))
|
||||
world
|
||||
(let [[{command :val} arg] ((:instrs program) instr-ptr)
|
||||
resolve #(resolve-arg % registers labels world)
|
||||
assoc-world-brain #(assoc-in world [:robots (:idx robot) :brain] (into brain %))]
|
||||
(case command
|
||||
"GOTO" (assoc-world-brain {:instr-ptr (resolve arg)})
|
||||
"GOSUB" (assoc-world-brain {:instr-ptr (resolve arg)
|
||||
:call-stack (conj call-stack (inc instr-ptr))})
|
||||
"ENDSUB" (assoc-world-brain {:instr-ptr (peek call-stack)
|
||||
:call-stack (pop call-stack)})
|
||||
("IF", ",") (assoc-world-brain {:instr-ptr (inc instr-ptr)
|
||||
:acc (resolve arg)})
|
||||
("+" "-" "*" "/") (assoc-world-brain {:instr-ptr (inc instr-ptr)
|
||||
:acc ((op-map command) acc (resolve arg))})
|
||||
("=" ">" "<" "#") (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
|
||||
(registers (:val arg))
|
||||
(assoc-world-brain {:instr-ptr (inc instr-ptr)})
|
||||
acc))))))
|
||||
|
|
@ -1,35 +1,29 @@
|
|||
(ns robotwar.core
|
||||
(:use [clojure.pprint]
|
||||
(robotwar foundry robot world game-lexicon)))
|
||||
(robotwar foundry brain robot world game-lexicon brain-test)))
|
||||
|
||||
(def src-code1 " START
|
||||
0 TO A
|
||||
TEST
|
||||
IF A > 2 GOTO START
|
||||
GOSUB INCREMENT
|
||||
GOTO TEST
|
||||
100 TO A
|
||||
INCREMENT
|
||||
A + 1 TO A
|
||||
ENDSUB
|
||||
200 TO A ")
|
||||
|
||||
(def src-code2 "WAIT GOTO WAIT")
|
||||
(def src-code3 "500 TO RANDOM RANDOM RANDOM RANDOM")
|
||||
|
||||
(def world (init-world 30 30 (map #(assemble reg-names %) [src-code1 src-code2 src-code3])))
|
||||
|
||||
(def step (fn [initial-state n]
|
||||
(nth (iterate tick-robot initial-state) n)))
|
||||
|
||||
; pretty-prints a robot-state with line numbers,
|
||||
; this is a hacky place for messing with stuff. currently imports
|
||||
; all the test data from brain-test, and the function below uses
|
||||
; some of those variables to
|
||||
; pretty-print a robot-state with line numbers for the program instructions,
|
||||
; and only the registers you want. Very convenient.
|
||||
;
|
||||
; it takes a world-tick number and a robot index number, and prettyprints a robot
|
||||
; with line numbers for the program instructions, and only the registers specified.
|
||||
; (also it only prints the values of the registers, not the register-maps with
|
||||
; their ugly full system-names of the read and write functions.) Very convenient.
|
||||
|
||||
(def ppt (fn [program n & reg-keys]
|
||||
(let [state (step (init-robot-state program {}) n)]
|
||||
(pprint (into (assoc-in
|
||||
state
|
||||
[:program :instrs]
|
||||
(zipmap (range) (get-in state [:program :instrs])))
|
||||
{:registers (select-keys (:registers state) reg-keys)})))))
|
||||
(def get-robot (fn [world-tick-idx robot-idx]
|
||||
((:robots (get-world world-tick-idx robot-idx)) robot-idx)))
|
||||
|
||||
(def ppt (fn [world-tick-idx robot-idx & [reg-keys]]
|
||||
(let [{:keys [brain registers] :as robot} (get-robot world-tick-idx robot-idx)]
|
||||
(pprint
|
||||
(into robot
|
||||
{:brain (assoc-in
|
||||
brain
|
||||
[:program :instrs]
|
||||
(sort (zipmap (range) (get-in brain [:program :instrs]))))
|
||||
:registers (sort (into {} (for [[reg-name reg-map]
|
||||
(select-keys registers reg-keys)]
|
||||
{reg-name (:val reg-map)})))})))))
|
||||
|
|
|
@ -1,82 +1,79 @@
|
|||
(ns robotwar.robot
|
||||
(:use [clojure.string :only [join]]
|
||||
(robotwar kernel-lexicon game-lexicon)))
|
||||
(robotwar brain game-lexicon)))
|
||||
|
||||
; TODO: remove the game-lexicon dependency above, when it's no longer needed
|
||||
; (i.e. when we've moved the resolve-register logic out of this module)
|
||||
|
||||
(def op-map (zipmap op-commands
|
||||
(map (fn [op]
|
||||
(case op
|
||||
"/" #(int (Math/round (float (/ %1 %2))))
|
||||
"#" not=
|
||||
(-> op read-string eval)))
|
||||
op-commands)))
|
||||
|
||||
(defn resolve-register [registers reg]
|
||||
(case reg
|
||||
"RANDOM" (rand-int (registers reg))
|
||||
"DATA" (registers (reg-names (registers "INDEX")))
|
||||
(registers reg)))
|
||||
|
||||
(defn resolve-arg [{arg-val :val arg-type :type} registers labels]
|
||||
"resolves an instruction argument to a numeric value
|
||||
(either an arithmetic or logical comparison operand, or an instruction pointer)."
|
||||
(case arg-type
|
||||
:label (labels arg-val)
|
||||
:number arg-val
|
||||
:register (resolve-register registers arg-val)
|
||||
nil))
|
||||
|
||||
(def registers-with-effect-on-world #{"SHOT" "RADAR" "SPEEDX" "SPEEDY"})
|
||||
|
||||
(defn tick-robot
|
||||
"takes as input a data structure representing all that the robot's brain
|
||||
needs to know about the world:
|
||||
|
||||
1) The robot program, consisting of a vector of two-part instructions
|
||||
(a command, followed by an argument or nil) as well as a map of labels to
|
||||
instruction numbers
|
||||
2) The instruction pointer (an index number for the instruction vector)
|
||||
3) The value of the accumulator, or nil
|
||||
4) The call stack (a vector of instruction pointers to lines following
|
||||
GOSUB calls)
|
||||
5) The contents of all the registers
|
||||
|
||||
After executing one instruction, tick-robot returns the updated verion of all of the above,
|
||||
plus an optional :action field, to notify the world if the SHOT, SPEEDX, SPEEDY or RADAR
|
||||
registers have been pushed to."
|
||||
|
||||
[{:keys [acc instr-ptr call-stack registers program] :as state}]
|
||||
(let [[{command :val} {unresolved-arg-val :val :as arg}] ((program :instrs) instr-ptr)
|
||||
resolve #(resolve-arg % registers (program :labels))]
|
||||
(case command
|
||||
"GOTO" (into state {:instr-ptr (resolve arg)})
|
||||
"GOSUB" (into state {:instr-ptr (resolve arg)
|
||||
:call-stack (conj call-stack (inc instr-ptr))})
|
||||
"ENDSUB" (into state {:instr-ptr (peek call-stack)
|
||||
:call-stack (pop call-stack)})
|
||||
("IF", ",") (into state {:instr-ptr (inc instr-ptr)
|
||||
:acc (resolve arg)})
|
||||
("+" "-" "*" "/") (into state {:instr-ptr (inc instr-ptr)
|
||||
:acc ((op-map command) acc (resolve arg))})
|
||||
("=" ">" "<" "#") (if ((op-map command) acc (resolve arg))
|
||||
(into state {:instr-ptr (inc instr-ptr)})
|
||||
(into state {:instr-ptr (+ instr-ptr 2)}))
|
||||
"TO" (let [return-state (into state {:instr-ptr (inc instr-ptr)
|
||||
:registers (into registers {unresolved-arg-val acc})})]
|
||||
(if (registers-with-effect-on-world unresolved-arg-val)
|
||||
(conj return-state {:action unresolved-arg-val})
|
||||
return-state)))))
|
||||
|
||||
(defn init-robot-state
|
||||
"initialize all the state variables that go along
|
||||
with the robot program when it's running.
|
||||
(Optionally, pass in a hash-map of register names and values)."
|
||||
[program reg-names & [registers]]
|
||||
{:program program
|
||||
:acc 0
|
||||
:instr-ptr 0
|
||||
:registers (into (zipmap reg-names (repeat 0))
|
||||
registers)
|
||||
:call-stack []})
|
||||
; 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)]
|
||||
; ))
|
||||
|
|
|
@ -1,40 +1,47 @@
|
|||
(ns robotwar.world
|
||||
(:use [clojure.string :only [join]]
|
||||
(robotwar foundry robot)))
|
||||
|
||||
(defn init-world
|
||||
"initialize all the variables for a robot world"
|
||||
[width height programs]
|
||||
{:width width
|
||||
:height height
|
||||
:shells []
|
||||
:robots (map-indexed (fn [idx program]
|
||||
{:internal-state (init-robot-state program
|
||||
{"X" (rand-int width)
|
||||
"Y" (rand-int height)})
|
||||
:external-state {: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} :external-state}]
|
||||
(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)))
|
||||
(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)))
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
(ns robotwar.brain-test
|
||||
(:use [clojure.test]
|
||||
[robotwar.brain])
|
||||
(:require (robotwar foundry game-lexicon)))
|
||||
|
||||
(def src-codes [ ; program 0: multi-use program
|
||||
" START
|
||||
0 TO A
|
||||
TEST
|
||||
IF A > 2 GOTO START
|
||||
GOSUB INCREMENT
|
||||
GOTO TEST
|
||||
100 TO A
|
||||
INCREMENT
|
||||
A + 1 TO A
|
||||
ENDSUB
|
||||
200 TO A "
|
||||
|
||||
; program 1: to test RANDOM register
|
||||
" 1000 TO RANDOM
|
||||
RANDOM RANDOM RANDOM RANDOM RANDOM
|
||||
RANDOM RANDOM RANDOM RANDOM RANDOM "
|
||||
|
||||
; program 2: to test INDEX/DATA pair of registers
|
||||
" 300 TO A
|
||||
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 (comp init-brain (partial robotwar.foundry/assemble 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))))
|
||||
|
||||
(deftest branching-test
|
||||
(testing "comparison statement should cause jump in instr-ptr"
|
||||
(is (= (get-in (get-world 4 0) [:robots 0 :brain :instr-ptr])
|
||||
5))))
|
||||
|
||||
(deftest arithmetic-test
|
||||
(testing "addition"
|
||||
(is (= (get-in (get-world 7 0) [: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])]
|
||||
(= [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])]
|
||||
(= [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])
|
||||
1))))
|
||||
|
||||
;(deftest random-test
|
||||
; (testing "push to random register and pull from it to receive a number
|
||||
; of unequal numbers less than the number that was pushed"
|
||||
; (is (let [random-pairs (map (fn [n]
|
||||
; (let [{{random "RANDOM"} :registers, acc :acc}
|
||||
; (nth random-check-history n)]
|
||||
; [random acc]))
|
||||
; (range 3 13))]
|
||||
; (and (every? #{1000} (map first random-pairs))
|
||||
; (every? #(< -1 % 1000) (map second random-pairs))
|
||||
; (apply not= (map second random-pairs)))))))
|
||||
;
|
||||
;(deftest index-data-pair-test
|
||||
; (testing "registers whose index numbers are pushed to INDEX can
|
||||
; be referenced by accessing DATA"
|
||||
; (is (= (get-in (nth index-data-check-history 5) [:registers "A"])
|
||||
; 300))))
|
|
@ -1,8 +1,8 @@
|
|||
(ns robotwar.foundry-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[robotwar.foundry :refer :all]
|
||||
[robotwar.game-lexicon :refer :all])
|
||||
(:use [clojure.string :only [join]]))
|
||||
(:use (clojure [string :only [join]]
|
||||
[test])
|
||||
[robotwar.foundry])
|
||||
(:require [robotwar.game-lexicon]))
|
||||
|
||||
(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 reg-names {:token-str "AIM"})
|
||||
(is (= (parse-token robotwar.game-lexicon/reg-names {:token-str "AIM"})
|
||||
{:val "AIM", :type :register}))))
|
||||
|
||||
(deftest parse-token-command-word
|
||||
(testing "parsing command token (word)"
|
||||
(is (= (parse-token reg-names {:token-str "GOTO"})
|
||||
(is (= (parse-token robotwar.game-lexicon/reg-names {:token-str "GOTO"})
|
||||
{:val "GOTO", :type :command}))))
|
||||
|
||||
(deftest parse-token-command-operator
|
||||
(testing "parsing command token (operator)"
|
||||
(is (= (parse-token reg-names {:token-str "#"})
|
||||
(is (= (parse-token robotwar.game-lexicon/reg-names {:token-str "#"})
|
||||
{:val "#", :type :command}))))
|
||||
|
||||
(deftest parse-token-number
|
||||
(testing "parsing number token"
|
||||
(is (= (parse-token reg-names {:token-str "-17"}))
|
||||
(is (= (parse-token robotwar.game-lexicon/reg-names {:token-str "-17"}))
|
||||
{:val -17, :type :number})))
|
||||
|
||||
(deftest parse-token-label
|
||||
(testing "parsing label token"
|
||||
(is (= (parse-token reg-names {:token-str "SCAN"})
|
||||
(is (= (parse-token robotwar.game-lexicon/reg-names {:token-str "SCAN"})
|
||||
{:val "SCAN", :type :label}))))
|
||||
|
||||
(deftest parse-token-error
|
||||
(testing "parsing error token"
|
||||
(is (= (parse-token reg-names {:token-str "-GOTO"})
|
||||
(is (= (parse-token robotwar.game-lexicon/reg-names {:token-str "-GOTO"})
|
||||
{:val "Invalid word or symbol", :type :error}))))
|
||||
|
||||
(deftest parse-tokens-minus-sign
|
||||
(testing "parsing tokens with a binary minus sign"
|
||||
(is (= (parse reg-names lexed-tokens2)
|
||||
(is (= (parse robotwar.game-lexicon/reg-names lexed-tokens2)
|
||||
parsed-tokens2))))
|
||||
|
||||
(deftest parse-tokens-negative-sign
|
||||
(testing "parsing tokens with a unary negative sign"
|
||||
(is (= (parse reg-names lexed-tokens3)
|
||||
(is (= (parse robotwar.game-lexicon/reg-names lexed-tokens3)
|
||||
parsed-tokens3))))
|
||||
|
||||
(deftest parse-tokens-error
|
||||
(testing "parsing tokens with an invalid operator"
|
||||
(is (= (parse reg-names lexed-tokens4)
|
||||
(is (= (parse robotwar.game-lexicon/reg-names lexed-tokens4)
|
||||
parsed-tokens4))))
|
||||
|
||||
(def minus-sign-disambiguated-tokens2 parsed-tokens2)
|
||||
|
@ -294,17 +294,17 @@
|
|||
|
||||
(deftest assemble-test-success
|
||||
(testing "compiling successfully"
|
||||
(is (= (assemble reg-names (join "\n" [line1 line2 line3]))
|
||||
(is (= (assemble robotwar.game-lexicon/reg-names (join "\n" [line1 line2 line3]))
|
||||
multi-line-assembled))))
|
||||
|
||||
(deftest assemble-test-failure
|
||||
(testing "assemble results in error"
|
||||
(is (= (assemble reg-names (join "\n" [line1 line2 line3 line4]))
|
||||
(is (= (assemble robotwar.game-lexicon/reg-names (join "\n" [line1 line2 line3 line4]))
|
||||
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 reg-names (join "\n" [line1 line2 line3]))
|
||||
(is (= (meta (get-in (assemble robotwar.game-lexicon/reg-names (join "\n" [line1 line2 line3]))
|
||||
[:instrs 8 1]))
|
||||
{:line 3, :pos 14}))))
|
||||
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
(ns robotwar.robot-test
|
||||
(:use [clojure.test]
|
||||
(robotwar foundry robot game-lexicon)))
|
||||
|
||||
(def src-codes [ ; program 0: multi-use program
|
||||
" START
|
||||
0 TO A
|
||||
TEST
|
||||
IF A > 2 GOTO START
|
||||
GOSUB INCREMENT
|
||||
GOTO TEST
|
||||
100 TO A
|
||||
INCREMENT
|
||||
A + 1 TO A
|
||||
ENDSUB
|
||||
200 TO A "
|
||||
|
||||
; program 1: to test RANDOM register
|
||||
" 1000 TO RANDOM
|
||||
RANDOM
|
||||
RANDOM
|
||||
RANDOM
|
||||
RANDOM
|
||||
RANDOM
|
||||
RANDOM
|
||||
RANDOM
|
||||
RANDOM
|
||||
RANDOM
|
||||
RANDOM "
|
||||
|
||||
; program 2: to test INDEX/DATA pair of registers
|
||||
" 300 TO A
|
||||
1 TO INDEX
|
||||
DATA " ])
|
||||
|
||||
(def robot-history #(iterate tick-robot (init-robot-state (assemble reg-names %) {})))
|
||||
(def robot-histories (map robot-history src-codes))
|
||||
(def multi-use-history (nth robot-histories 0))
|
||||
(def random-check-history (nth robot-histories 1))
|
||||
(def index-data-check-history (nth robot-histories 2))
|
||||
|
||||
(deftest branching-test
|
||||
(testing "comparison statement should cause jump in instr-ptr"
|
||||
(is (= (:instr-ptr (nth multi-use-history 4))
|
||||
5))))
|
||||
|
||||
(deftest arithmetic-test
|
||||
(testing "addition"
|
||||
(is (= (:acc (nth multi-use-history 7))
|
||||
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]} (nth multi-use-history 5)]
|
||||
(= [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]} (nth multi-use-history 9)]
|
||||
(= [instr-ptr call-stack]
|
||||
[6 []])))))
|
||||
|
||||
(deftest push-test
|
||||
(testing "pushing number to register"
|
||||
(is (= (get-in (nth multi-use-history 8) [:registers "A"])
|
||||
1))))
|
||||
|
||||
(deftest random-test
|
||||
(testing "push to random register and pull from it to receive a number
|
||||
of unequal numbers less than the number that was pushed"
|
||||
(is (let [random-pairs (map (fn [n]
|
||||
(let [{{random "RANDOM"} :registers, acc :acc}
|
||||
(nth random-check-history n)]
|
||||
[random acc]))
|
||||
(range 3 13))]
|
||||
(and (every? #{1000} (map first random-pairs))
|
||||
(every? #(< -1 % 1000) (map second random-pairs))
|
||||
(apply not= (map second random-pairs)))))))
|
||||
|
||||
(deftest index-data-pair-test
|
||||
(testing "registers whose index numbers are pushed to INDEX can
|
||||
be referenced by accessing DATA"
|
||||
(is (= (get-in (nth index-data-check-history 5) [:registers "A"])
|
||||
300))))
|
||||
|
Loading…
Reference in New Issue