mirror of
https://github.com/richardharrington/robotwar.git
synced 2024-07-15 01:29:06 +00:00
making progress on refactoring, especially in brain (formerly robot). made new file called robot to represent external robot state
This commit is contained in:
parent
6e04c076a6
commit
9112c82dc6
100
src/robotwar/brain.clj
Normal file
100
src/robotwar/brain.clj
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
(ns robotwar.brain
|
||||||
|
(:use [clojure.string :only [join]]
|
||||||
|
[robotwar.kernel-lexicon]))
|
||||||
|
|
||||||
|
(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 init-register [reg-name read write data]
|
||||||
|
{reg-name {:read read
|
||||||
|
:write write
|
||||||
|
:data data}})
|
||||||
|
|
||||||
|
(defn default-read [data world]
|
||||||
|
data)
|
||||||
|
|
||||||
|
(defn default-write [robot data]
|
||||||
|
(assoc-in robot [:brain :registers reg-name] data))
|
||||||
|
|
||||||
|
(def default-data 0)
|
||||||
|
|
||||||
|
(defn default-register [reg-name]
|
||||||
|
(init-register reg-name default-read default-write default-data))
|
||||||
|
|
||||||
|
(defn init-brain
|
||||||
|
"initialize the brain (meaning all the internal state variables that go along
|
||||||
|
with the robot program when it's running).
|
||||||
|
(Optionally, also pass in a hash-map of register names and values,
|
||||||
|
which will override the defaults)."
|
||||||
|
[program reg-names & [registers]]
|
||||||
|
{:acc 0
|
||||||
|
:instr-ptr 0
|
||||||
|
:call-stack []
|
||||||
|
:program program
|
||||||
|
:registers (into {} (concat (map default-register reg-names) registers))})
|
||||||
|
|
||||||
|
(defn read-register
|
||||||
|
"wrapper for the read function in each register. takes a register
|
||||||
|
and also takes world state, because
|
||||||
|
some of those functions may need access to the world state.
|
||||||
|
returns a numeric value."
|
||||||
|
[{:keys [read data] :as register} world]
|
||||||
|
(read data world))
|
||||||
|
|
||||||
|
(defn write-register
|
||||||
|
"wrapper for the write function in each register.
|
||||||
|
takes a robot, a register, and the data to write.
|
||||||
|
returns a full robot (brain and external properties as well).
|
||||||
|
TODO: implement extra flag to indicate if we've fired a shot."
|
||||||
|
[robot {write :write :as register} data]
|
||||||
|
(write robot data))
|
||||||
|
|
||||||
|
(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 tick-brain
|
||||||
|
"takes a full robot. Only the internal state (the brain) will be
|
||||||
|
different when we pass it back, for all of the operations except 'TO',
|
||||||
|
which may alter the external state of the robot as well. (And for the time
|
||||||
|
being, shots fired will be indicated with a flag in the robot.)
|
||||||
|
|
||||||
|
Also takes a 'world' parameter, which may contain information that some of the
|
||||||
|
registers' read functions may need. Will not be passed out in the return value.
|
||||||
|
|
||||||
|
TODO: Figure out a way to have this function not know about the external robot stuff,
|
||||||
|
like that the name of the field leading to the brain is :brain."
|
||||||
|
|
||||||
|
[robot world]
|
||||||
|
(let [brain (:brain robot)
|
||||||
|
{:keys [acc instr-ptr call-stack registers program]} brain
|
||||||
|
[{command :val} arg] ((:instrs program) instr-ptr)
|
||||||
|
resolve #(resolve-arg % registers (:labels program) world)
|
||||||
|
return-robot #(assoc robot :brain (into brain %))]
|
||||||
|
(case command
|
||||||
|
"GOTO" (return-robot {:instr-ptr (resolve arg)})
|
||||||
|
"GOSUB" (return-robot {:instr-ptr (resolve arg)
|
||||||
|
:call-stack (conj call-stack (inc instr-ptr))})
|
||||||
|
"ENDSUB" (return-robot {:instr-ptr (peek call-stack)
|
||||||
|
:call-stack (pop call-stack)})
|
||||||
|
("IF", ",") (return-robot {:instr-ptr (inc instr-ptr)
|
||||||
|
:acc (resolve arg)})
|
||||||
|
("+" "-" "*" "/") (return-robot {:instr-ptr (inc instr-ptr)
|
||||||
|
:acc ((op-map command) acc (resolve arg))})
|
||||||
|
("=" ">" "<" "#") (if ((op-map command) acc (resolve arg))
|
||||||
|
(return-robot {:instr-ptr (inc instr-ptr)})
|
||||||
|
(return-robot {:instr-ptr (+ instr-ptr 2)}))
|
||||||
|
"TO" (write-register (return-robot {:instr-ptr (inc instr-ptr)})
|
||||||
|
(registers (:val arg))
|
||||||
|
acc))))
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
(ns robotwar.core
|
(ns robotwar.core
|
||||||
(:use [clojure.pprint]
|
(:use [clojure.pprint]
|
||||||
(robotwar foundry robot world game-lexicon)))
|
(robotwar foundry brain robot world game-lexicon)))
|
||||||
|
|
||||||
(def src-code1 " START
|
(def src-code1 " START
|
||||||
0 TO A
|
0 TO A
|
||||||
|
@ -1,112 +1,26 @@
|
|||||||
(ns robotwar.robot
|
(ns robotwar.robot
|
||||||
(:use [clojure.string :only [join]]
|
(:use [clojure.string :only [join]]
|
||||||
[clojure.pprint :only [pprint]]
|
(robotwar brain game-lexicon)))
|
||||||
(robotwar kernel-lexicon game-lexicon)))
|
|
||||||
|
|
||||||
; TODO: remove the game-lexicon dependency above, when it's no longer needed
|
; TODO: Fill out this module.
|
||||||
; (i.e. when we've moved the resolve-register logic out of this module)
|
; Probably it will consist mostly of
|
||||||
|
; 0) An init function, to initialize all the fields containing
|
||||||
(def op-map (zipmap op-commands
|
; the external robot information. I think this should be
|
||||||
(map (fn [op]
|
; SEPARATE FROM THE REGISTERS, even the ones that are similar.
|
||||||
(case op
|
; 1) Specialty read and write functions for the registers
|
||||||
"/" #(int (Math/round (float (/ %1 %2))))
|
; 2) Code to deal with the flag when the robot fires a shot (probably this
|
||||||
"#" not=
|
; will just involve passing the flag up to the world)
|
||||||
(-> op read-string eval)))
|
; 3) Something else I can't remember. Maybe put some of the init-register
|
||||||
op-commands)))
|
; and default-register and register-handling-in-general code in this
|
||||||
|
; module, instead of in brain. Something to think about.
|
||||||
(defn init-internal-state
|
; 4) GENERAL NOTES: A) CHANGE STRINGS TO KEYWORDS EARLY ON.
|
||||||
"initialize all the internal state variables that go along
|
; B) CHANGE SOME OF THESE MODULE LOADINGS FROM
|
||||||
with the robot program when it's running.
|
; "USE" TO "REFER", SO THAT THEY HAVE TO USE
|
||||||
(Optionally, also pass in a hash-map of register names and values,
|
; FULLY-QUALIFIED NAMES. THAT MIGHT MAKE THINGS
|
||||||
which will override the defaults)."
|
; A BIT CLEARER. THE NAMES CAN BE SHORTENED QUITE A BIT,
|
||||||
[program reg-names & [registers]]
|
; WHEN LOADED INTO THE MODULES.
|
||||||
(pprint program)
|
|
||||||
(pprint reg-names)
|
|
||||||
(pprint registers)
|
|
||||||
(let [identity-with-throwaway-args (fn [x & args] x)]
|
|
||||||
{:registers (into {} (concat
|
|
||||||
(for [reg-name reg-names]
|
|
||||||
; default values for :read, :write and :data.
|
|
||||||
; (TODO: move these into a higher-level module)
|
|
||||||
; NOTE: the default version of the :read function
|
|
||||||
; does not need the world-state parameter.
|
|
||||||
[reg-name {:read identity-with-throwaway-args
|
|
||||||
:write (fn [robot data]
|
|
||||||
(assoc-in
|
|
||||||
robot
|
|
||||||
[:internal-state :registers reg-name]
|
|
||||||
data))
|
|
||||||
:data 0}])
|
|
||||||
registers))
|
|
||||||
:program program
|
|
||||||
:acc 0
|
|
||||||
:instr-ptr 0
|
|
||||||
:call-stack []}))
|
|
||||||
|
|
||||||
(defn read-register
|
|
||||||
"returns a numeric value"
|
|
||||||
[{:keys [read data] :as register} world]
|
|
||||||
(read data world))
|
|
||||||
|
|
||||||
(defn write-register
|
|
||||||
"returns a full robot state, including its external state and its internal brain state.
|
|
||||||
TODO: implement extra flag to indicate if we've fired a shot."
|
|
||||||
[robot {write :write :as register} data]
|
|
||||||
(write robot data))
|
|
||||||
|
|
||||||
;(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 world]
|
|
||||||
"resolves an instruction argument to a numeric value
|
|
||||||
(either an arithmetic or logical comparison operand, or an instruction pointer).
|
|
||||||
If it's reading from a register, uses that register's :read function."
|
|
||||||
(case arg-type
|
|
||||||
:label (labels arg-val)
|
|
||||||
:number arg-val
|
|
||||||
:register (read-register (registers arg-val) world)
|
|
||||||
nil))
|
|
||||||
|
|
||||||
(defn tick-robot
|
(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."
|
|
||||||
|
|
||||||
[robot world]
|
[robot world]
|
||||||
(let [internal-state (:internal-state robot)
|
(let [ticked (tick-brain robot world)]
|
||||||
{:keys [acc instr-ptr call-stack registers program]} internal-state
|
))
|
||||||
[{command :val} arg] ((program :instrs) instr-ptr)
|
|
||||||
resolve #(resolve-arg % registers (program :labels) world)
|
|
||||||
return-robot #(assoc robot :internal-state (into internal-state %))]
|
|
||||||
(case command
|
|
||||||
"GOTO" (return-robot {:instr-ptr (resolve arg)})
|
|
||||||
"GOSUB" (return-robot {:instr-ptr (resolve arg)
|
|
||||||
:call-stack (conj call-stack (inc instr-ptr))})
|
|
||||||
"ENDSUB" (return-robot {:instr-ptr (peek call-stack)
|
|
||||||
:call-stack (pop call-stack)})
|
|
||||||
("IF", ",") (return-robot {:instr-ptr (inc instr-ptr)
|
|
||||||
:acc (resolve arg)})
|
|
||||||
("+" "-" "*" "/") (return-robot {:instr-ptr (inc instr-ptr)
|
|
||||||
:acc ((op-map command) acc (resolve arg))})
|
|
||||||
("=" ">" "<" "#") (if ((op-map command) acc (resolve arg))
|
|
||||||
(return-robot {:instr-ptr (inc instr-ptr)})
|
|
||||||
(return-robot {:instr-ptr (+ instr-ptr 2)}))
|
|
||||||
"TO" (write-register (return-robot {:instr-ptr (inc instr-ptr)})
|
|
||||||
(registers (:val arg))
|
|
||||||
acc))))
|
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
(ns robotwar.world
|
(ns robotwar.world
|
||||||
(:use [clojure.string :only [join]]
|
(:use [clojure.string :only [join]]
|
||||||
(robotwar foundry robot game-lexicon)))
|
(robotwar foundry brain robot game-lexicon)))
|
||||||
|
|
||||||
; TODO: Write init-register function, in robot probably, and init those
|
|
||||||
; X and Y registers down below.
|
|
||||||
|
|
||||||
(defn init-world
|
(defn init-world
|
||||||
"initialize all the variables for a robot world"
|
"initialize all the variables for a robot world"
|
||||||
@ -12,11 +9,19 @@
|
|||||||
:height height
|
:height height
|
||||||
:shells []
|
:shells []
|
||||||
:robots (vec (map-indexed (fn [idx program]
|
:robots (vec (map-indexed (fn [idx program]
|
||||||
{:internal-state (init-internal-state program reg-names
|
{:brain (init-brain
|
||||||
{"X" (rand-int width)
|
program
|
||||||
"Y" (rand-int height)})
|
reg-names
|
||||||
:icon (str idx)})
|
{(init-register "X"
|
||||||
programs))})
|
default-read
|
||||||
|
default-write
|
||||||
|
(rand-int width))
|
||||||
|
(init-register "Y"
|
||||||
|
default-read
|
||||||
|
default-write
|
||||||
|
(rand-int height))})
|
||||||
|
:icon (str idx)})
|
||||||
|
programs))})
|
||||||
|
|
||||||
(defn tick-world
|
(defn tick-world
|
||||||
"TODO"
|
"TODO"
|
||||||
|
Loading…
Reference in New Issue
Block a user