Rule language now working cleanly with no errors showing. Very occasional

null pointer exception during parsing, not yet diagnosed.
This commit is contained in:
Simon Brooke 2014-07-20 15:15:12 +01:00
parent a61ace1694
commit 91310af2d8
4 changed files with 27 additions and 27 deletions

View file

@ -19,8 +19,6 @@ if state is forest and fertility is more than 5 then state should be climax
if state is climax then 1 chance in 500 state should be fire if state is climax then 1 chance in 500 state should be fire
;; Climax forest neighbouring fires is likely to catch fire ;; Climax forest neighbouring fires is likely to catch fire
;; TODO: this rule, and specifically the 'some neighbours' clause, seems to give
;; rise to the 'Keyword cannot be cast to Number' bug.
if state is climax and some neighbours are fire then 1 chance in 3 state should be fire if state is climax and some neighbours are fire then 1 chance in 3 state should be fire
;; After fire we get waste ;; After fire we get waste
@ -35,24 +33,19 @@ if state is waste and some neighbours are forest then state should be heath
if state is waste and some neighbours are climax then state should be heath if state is waste and some neighbours are climax then state should be heath
if state is waste then state should be grassland if state is waste then state should be grassland
;; Forest increases soil fertility. TODO: this rule compiles to what looks like
;; the right code but it never fires.
if state is in forest or climax then fertility should be fertility + 1
## Herbivore rules ## Herbivore rules
;; rules describing the impact of herbivores on the environment ;; rules describing the impact of herbivores on the environment
;; if there are too many deer for the fertility of the area to sustain, ;; if there are too many deer for the fertility of the area to sustain,
;; some die or move on. ;; some die or move on.
if deer are more than fertility then deer should be fertility / 2 ;; if deer are more than fertility then deer should be fertility / 2
;; deer arrive occasionally at the edge of the map. ;; deer arrive occasionally at the edge of the map.
if x is 0 or y is 0 and deer are 0 then 1 chance in 50 deer should be 2 if x is 0 or y is 0 and deer are 0 then 1 chance in 50 deer should be 2
;; deer gradually spread through the world by breeding or migrating. ;; deer gradually spread through the world by breeding or migrating.
if fertility is more than 10 and deer is 0 and some neighbours have deer more than 2 then deer should be 2 if fertility is more than 10 and deer is 0 and some neighbours have deer more than 2 then deer should be 2
;; deer breed. ;; deer breed.
@ -68,7 +61,7 @@ if deer are more than wolves then deer should be deer - wolves
;; if there are not enough deer to sustain the population of wolves, ;; if there are not enough deer to sustain the population of wolves,
;; some wolves die or move on. ;; some wolves die or move on.
if wolves are more than deer then deer should be 0 and wolves should be deer if wolves are more than deer then deer should be 0 and wolves should be deer + 0
;; wolves arrive occasionally at the edge of the map. ;; wolves arrive occasionally at the edge of the map.
@ -82,6 +75,12 @@ if state is not water and wolves is 0 and some neighbours have wolves more than
if wolves are more than 1 then wolves should be wolves * 2 if wolves are more than 1 then wolves should be wolves * 2
## Potential blockers
;; Forest increases soil fertility.
if state is in forest or climax then fertility should be fertility + 1
## Initialisation rules ## Initialisation rules
;; Rules which deal with state 'new' will waste less time if they're near the ;; Rules which deal with state 'new' will waste less time if they're near the

View file

@ -30,22 +30,10 @@
"Compile each non-comment line of this `string` into an executable anonymous "Compile each non-comment line of this `string` into an executable anonymous
function, and return the sequence of such functions." function, and return the sequence of such functions."
[string] [string]
(map #(compile-rule % true) (remove comment? (split string #"\n")))) (map #(compile-rule % true) (remove comment? (trim (split string #"\n")))))
(defn compile-file (defn compile-file
"Compile each non-comment line of the file indicated by this `filename` into "Compile each non-comment line of the file indicated by this `filename` into
an executable anonymous function, and return the sequence of such functions." an executable anonymous function, and return the sequence of such functions."
[filename] [filename]
(compile-string (slurp filename))) (compile-string (slurp filename)))
;; (let [lines
;; (doall (with-open [rdr (reader filename)] (line-seq rdr)))]
;; (map parse-line lines)))
;;(defn parse-string
;; "Parse rules from successive lines in this `string`"
;; [string]
;; (parse-from-reader (BufferedReader. (StringReader. string))))

View file

@ -118,7 +118,7 @@
([tokens expect-int] ([tokens expect-int]
(or (or
(parse-disjunct-value tokens expect-int) (parse-disjunct-value tokens expect-int)
(parse-simple-value tokens))) (parse-simple-value tokens expect-int)))
([tokens] ([tokens]
(parse-value tokens false))) (parse-value tokens false)))

View file

@ -107,6 +107,14 @@
(is (nil? (apply afn (list {:altitude 200} nil))) (is (nil? (apply afn (list {:altitude 200} nil)))
"Rule does not fire when condition is not met"))) "Rule does not fire when condition is not met")))
(testing "Property is more than property"
(let [afn (compile-rule "if wolves are more than deer then deer should be 0")]
(is (= (apply afn (list {:deer 2 :wolves 3} nil))
{:deer 0 :wolves 3})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:deer 3 :wolves 2} nil)))
"Rule does not fire when condition is not met")))
(testing "Property is less than numeric-value" (testing "Property is less than numeric-value"
(let [afn (compile-rule "if altitude is less than 10 then state should be water")] (let [afn (compile-rule "if altitude is less than 10 then state should be water")]
(is (= (apply afn (list {:altitude 9} nil)) (is (= (apply afn (list {:altitude 9} nil))
@ -114,6 +122,14 @@
"Rule fires when condition is met") "Rule fires when condition is met")
(is (nil? (apply afn (list {:altitude 10} nil))) (is (nil? (apply afn (list {:altitude 10} nil)))
"Rule does not fire when condition is not met"))) "Rule does not fire when condition is not met")))
(testing "Property is less than property"
(let [afn (compile-rule "if wolves are less than deer then deer should be deer - wolves")]
(is (= (apply afn (list {:deer 3 :wolves 2} nil))
{:deer 1 :wolves 2})
"Rule fires when condition is met")
(is (nil? (apply afn (list {:deer 2 :wolves 3} nil)))
"Rule does not fire when condition is not met")))
(testing "Number neighbours have property equal to value" (testing "Number neighbours have property equal to value"
(let [afn (compile-rule "if 3 neighbours have state equal to new then state should be water") (let [afn (compile-rule "if 3 neighbours have state equal to new then state should be water")
@ -396,7 +412,4 @@
(is (= (:state (apply afn (list {:x 2 :y 2} world))) :beach) (is (= (:state (apply afn (list {:x 2 :y 2} world))) :beach)
"Rule fires when condition is met (strip of altitude 11 down right hand side)") "Rule fires when condition is met (strip of altitude 11 down right hand side)")
(is (nil? (apply afn (list {:x 0 :y 1} world))) (is (nil? (apply afn (list {:x 0 :y 1} world)))
"Middle cell of the strip has only two high neighbours, so rule should not fire."))) "Middle cell of the strip has only two high neighbours, so rule should not fire."))))
)