mirror of
https://github.com/richardharrington/robotwar.git
synced 2024-07-07 12:28:55 +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
|
||||
(:use [clojure.pprint]
|
||||
(robotwar foundry robot world game-lexicon)))
|
||||
(robotwar foundry brain robot world game-lexicon)))
|
||||
|
||||
(def src-code1 " START
|
||||
0 TO A
|
||||
|
@ -1,112 +1,26 @@
|
||||
(ns robotwar.robot
|
||||
(:use [clojure.string :only [join]]
|
||||
[clojure.pprint :only [pprint]]
|
||||
(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 init-internal-state
|
||||
"initialize 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]]
|
||||
(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))
|
||||
; 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 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]
|
||||
(let [internal-state (:internal-state robot)
|
||||
{: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))))
|
||||
|
||||
(let [ticked (tick-brain robot world)]
|
||||
))
|
||||
|
@ -1,9 +1,6 @@
|
||||
(ns robotwar.world
|
||||
(:use [clojure.string :only [join]]
|
||||
(robotwar foundry robot game-lexicon)))
|
||||
|
||||
; TODO: Write init-register function, in robot probably, and init those
|
||||
; X and Y registers down below.
|
||||
(robotwar foundry brain robot game-lexicon)))
|
||||
|
||||
(defn init-world
|
||||
"initialize all the variables for a robot world"
|
||||
@ -12,11 +9,19 @@
|
||||
:height height
|
||||
:shells []
|
||||
:robots (vec (map-indexed (fn [idx program]
|
||||
{:internal-state (init-internal-state program reg-names
|
||||
{"X" (rand-int width)
|
||||
"Y" (rand-int height)})
|
||||
:icon (str idx)})
|
||||
programs))})
|
||||
{: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"
|
||||
|
Loading…
Reference in New Issue
Block a user