All core rules now compile into credible-looking code.

This commit is contained in:
Simon Brooke 2014-07-13 20:59:51 +01:00
parent ef3ec6cf18
commit e5b74a4a68
3 changed files with 241 additions and 8 deletions

182
resources/rules.txt Normal file
View file

@ -0,0 +1,182 @@
## Vegetation rules
;; rules which populate the world with plants
;; Occasionally, passing birds plant tree seeds into grassland
;; (fn [cell world] (cond (and (= (:state cell) :grassland)(< (rand 10) 1))(merge cell {:state :heath})))
if state is grassland then 1 chance in 10 state should be heath
;; heath below the treeline grows gradually into forest, providing browsing pressure is not to high
;; (fn [cell world]
;; (cond (and
;; (= (:state cell) :heath)
;; ;; browsing limit really ought to vary with soil fertility, but...
;; (< (+ (get-int cell :deer)(get-int cell :sheep)) 6)
;; (< (get-int cell :altitude) treeline))
;; (merge cell {:state :scrub})))
if state is heath and deer are fewer than 6 and altitude is less than 150 then state should be scrub
;; (fn [cell world] (cond (= (:state cell) :scrub) (merge cell {:state :forest})))
if state is scrub then 1 chance in 5 state should be forest
;; Forest on fertile land grows to climax
;; (fn [cell world]
;; (cond
;; (and
;; (= (:state cell) :forest)
;; (> (get-int cell :fertility) 10))
;; (merge cell {:state :climax})))
if state is forest and fertility is more than 5 then state should be climax
;; Climax forest occasionally catches fire (e.g. lightning strikes)
;; (fn [cell world] (cond (and (= (:state cell) :climax)(< (rand lightning-probability) 1)) (merge cell {:state :fire})))
if state is climax then 1 chance in 500 state should be fire
;; Climax forest neighbouring fires is likely to catch fire
;; (fn [cell world]
;; (cond
;; (and (= (:state cell) :climax)
;; (< (rand 3) 1)
;; (not (empty? (get-neighbours-with-state world (:x cell) (:y cell) 1 :fire))))
;; (merge cell {:state :fire})))
if state is climax and some neighbours are fire then 1 chance in 3 state should be fire
;; After fire we get waste
;; (fn [cell world] (cond (= (:state cell) :fire) (merge cell {:state :waste})))
if state is fire then state should be waste
;; And after waste we get pioneer species; if there's a woodland seed
;; source, it's going to be heath, otherwise grassland.
;; (fn [cell world]
;; (cond
;; (and (= (:state cell) :waste)
;; (not
;; (empty?
;; (flatten
;; (list
;; (get-neighbours-with-state world (:x cell) (:y cell) 1 :scrub)
;; (get-neighbours-with-state world (:x cell) (:y cell) 1 :forest)
;; (get-neighbours-with-state world (:x cell) (:y cell) 1 :climax))))))
;; (merge cell {:state :heath})))
if state is waste and some neighbours are scrub then state should be heath
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
;; (fn [cell world]
;; (cond (= (:state cell) :waste)
;; (merge cell {:state :grassland})))
if state is waste then state should be grassland
;; Forest increases soil fertility
;; (fn [cell world]
;; (cond (member? (:state cell) '(:forest :climax))
;; (merge cell {:fertility (+ (get-int cell :fertility) 1)})))
if state is in forest or climax then fertility should be fertility - 1
## Herbivore rules
;; rules describing the impact of herbivores on the environment
;; if there are too many deer for the fertility of the area to sustain,
;; some die or move on.
;; (fn [cell world]
;; (cond (> (get-int cell :deer) (get-int cell :fertility))
;; (merge cell {:deer (get-int cell :fertility)})))
if deer are more than fertility then deer should be fertility / 2
;; deer arrive occasionally at the edge of the map.
;; (fn [cell world]
;; (cond (and (< (count (get-neighbours world cell)) 8)
;; (< (rand 50) 1)
;; (> (get-int cell :fertility) 0)
;; (= (get-int cell :deer) 0))
;; (merge cell {:deer 2})))
if x is 0 or y is 0 and deer are 0 then 1 chance in 5 deer should be 2
;; deer gradually spread through the world by breeding or migrating.
;; (fn [cell world]
;; (let [n (apply + (map #(get-int % :deer) (get-neighbours world cell)))]
;; (cond (and
;; (> (get-int cell :fertility) 0)
;; (= (get-int cell :deer) 0)
;; (>= n 2))
;; (merge cell {:deer (int (/ n 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.
;; (fn [cell world]
;; (cond
;; (>= (get-int cell :deer) 2)
;; (merge cell {:deer (int (* (:deer cell) 2))})))))
if deer are more than 1 then deer should be deer * 2
## Predator rules
;; rules describing the impact of predator behaviour on the environment
;; wolves eat deer
;; (fn [cell world]
;; (cond
;; (>= (get-int cell :wolves) 1)
;; (merge cell {:deer (max 0 (- (get-int cell :deer) (get-int cell :wolves)))})))
if deer are more than wolves then deer should be deer - wolves
;; ;; not more than eight wolves in a pack, for now (hack because wolves are not dying)
;; (fn [cell world]
;; (cond (> (get-int cell :wolves) 8) (merge cell {:wolves 8})))
;; if there are not enough deer to sustain the population of wolves,
;; some wolves die or move on. (doesn't seem to be working?)
;; (fn [cell world]
;; (cond (> (get-int cell :wolves) (get-int cell :deer))
;; (merge cell {:wolves 0})))
if wolves are more than deer then deer should be 0 and wolves should be deer
;; wolves arrive occasionally at the edge of the map.
;; (fn [cell world]
;; (cond (and (< (count (get-neighbours world cell)) 8)
;; (< (rand 50) 1)
;; (not (= (:state cell) :water))
;; (= (get-int cell :wolves) 0))
;; (merge cell {:wolves 2})))
if x is 0 or y is 0 and wolves are 0 then 1 chance in 5 wolves should be 2
;; wolves gradually spread through the world by breeding or migrating.
;; (fn [cell world]
;; (let [n (apply + (map #(get-int % :wolves) (get-neighbours world cell)))]
;; (cond (and
;; (not (= (:state cell) :water))
;; (= (get-int cell :wolves) 0)
;; (>= n 2))
;; (merge cell {:wolves 2}))))
if state is not water and wolves is 0 and some neighbours have wolves more than 2 then 1 chance in 5 wolves should be 2
;; wolves breed.
;; (fn [cell world]
;; (cond
;; (>= (get-int cell :wolves) 2)
;; (merge cell {:wolves (int (* (:wolves cell) 2))})))
;; ))
if wolves are more than 1 then wolves should be wolves * 2
## Initialisation rules
;; Rules which deal with state 'new' will waste less time if they're near the
;; end of the file
if state is new and altitude is less than 10 then state should be water
if state is new and altitude is more than 200 then state should be snow
;; another comment
if state is new then state should be grassland

51
src/mw_parser/bulk.clj Normal file
View file

@ -0,0 +1,51 @@
;; parse multiple rules from a stream, possibly a file - although the real
;; objective is to parse rules out of a block of text from a textarea
(ns mw-parser.bulk
(:use mw-parser.core
mw-engine.utils
clojure.java.io
[clojure.string :only [split trim]])
(:import (java.io BufferedReader StringReader)))
(defn comment?
"Is this `line` a comment?"
[line]
(or (empty? (trim line)) (member? (first line) '(nil \# \;))))
(defn parse-string
"Parse rules from successive lines in this `string`, assumed to have multiple
lines delimited by the new-line character. Return a list of S-expressions."
[string]
;; TODO: tried to do this using with-open, but couldn't make it work.
(map parse-rule (remove comment? (split string #"\n"))))
(defn parse-file
"Parse rules from successive lines in the file loaded from this `filename`.
Return a list of S-expressions."
[filename]
(parse-string (slurp filename)))
(defn compile-string
"Compile each non-comment line of this `string` into an executable anonymous
function, and return the sequence of such functions."
[string]
(map compile-rule (remove comment? (split string #"\n"))))
(defn compile-file
"Compile each non-comment line of the file indicated by this `filename` into
an executable anonymous function, and return the sequence of such functions."
[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

@ -188,11 +188,11 @@
(= have-or-are "have") (= have-or-are "have")
(let [[property comp1 comp2 value & remainder] rest] (let [[property comp1 comp2 value & remainder] rest]
(cond (and (= comp1 "equal") (= comp2 "to")) (cond (and (= comp1 "equal") (= comp2 "to"))
(gen-neighbours-condition comparator quantity property value remainder =) (gen-neighbours-condition comparator quantity property value remainder '=)
(and (= comp1 "more") (= comp2 "than")) (and (= comp1 "more") (= comp2 "than"))
(gen-neighbours-condition '> quantity property value remainder >) (gen-neighbours-condition '> quantity property value remainder '>)
(and (= comp1 "less") (= comp2 "than")) (and (= comp1 "less") (= comp2 "than"))
(gen-neighbours-condition '< quantity property value remainder <) (gen-neighbours-condition '< quantity property value remainder '<)
)))))) ))))))
(defn parse-some-neighbours-condition (defn parse-some-neighbours-condition
@ -202,7 +202,7 @@
(parse-comparator-neighbours-condition (concat '("more" "than" "0" "neighbours") rest)))) (parse-comparator-neighbours-condition (concat '("more" "than" "0" "neighbours") rest))))
(defn parse-simple-neighbours-condition (defn parse-simple-neighbours-condition
"Parse conditions of the form '...6 neighbours are condition'" "Parse conditions of the form '...6 neighbours are [condition]'"
[[n NEIGHBOURS have-or-are & rest]] [[n NEIGHBOURS have-or-are & rest]]
(let [quantity (first (parse-numeric-value (list n)))] (let [quantity (first (parse-numeric-value (list n)))]
(cond (cond
@ -215,10 +215,10 @@
(let [[property comp1 comp2 value & remainder] rest] (let [[property comp1 comp2 value & remainder] rest]
(cond (and (= comp1 "equal") (= comp2 "to")) (cond (and (= comp1 "equal") (= comp2 "to"))
(gen-neighbours-condition '= quantity property value remainder) (gen-neighbours-condition '= quantity property value remainder)
;; (and (= comp1 "more") (= comp2 "than")) (and (= comp1 "more") (= comp2 "than"))
;; (gen-neighbours-condition '> quantity property value remainder) (gen-neighbours-condition '> quantity property value remainder '>)
;; (and (= comp1 "less") (= comp2 "than")) (and (= comp1 "less") (= comp2 "than"))
;; (gen-neighbours-condition '< quantity property value remainder) (gen-neighbours-condition '< quantity property value remainder '<)
)))))) ))))))
(defn parse-neighbours-condition (defn parse-neighbours-condition