mw-engine/src/mw_engine/natural_rules.clj

159 lines
6 KiB
Clojure

;; A set of MicroWorld rules describing a simplified natural ecosystem.
(ns mw-engine.natural-rules
(:use mw-engine.utils
mw-engine.world))
;; treeline at arbitrary altitude.
(def treeline 150)
;; waterline also at arbitrary altitude.
(def waterline 10)
;; and finally snowline is also arbitrary.
(def snowline 200)
;; Rare chance of lightning strikes
(def lightning-probability 500)
;; rules describing vegetation
(def vegetation-rules
(list
;; Randomly, birds plant tree seeds into grassland.
(fn [cell world] (cond (and (= (:state cell) :grassland)(< (rand 10) 1))(merge cell {:state :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})))
(fn [cell world] (cond (= (:state cell) :scrub) (merge cell {:state :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})))
;; 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})))
;; 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})))
;; After fire we get waste
(fn [cell world] (cond (= (:state cell) :fire) (merge cell {:state :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})))
(fn [cell world]
(cond (= (:state cell) :waste)
(merge cell {:state :grassland})))
;; Forest increases soil fertility
(fn [cell world]
(cond (member? (:state cell) '(:forest :climax))
(merge cell {:fertility (+ (get-int cell :fertility) 1)})))
))
;; rules describing herbivore behaviour
(def herbivore-rules
(list
;; 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)})))
;; 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})))
;; 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))}))))
;; deer breed.
(fn [cell world]
(cond
(>= (get-int cell :deer) 2)
(merge cell {:deer (int (* (:deer cell) 2))})))))
;; rules describing predator behaviour
(def predator-rules
(list
;; 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)))})))
;; ;; 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 get-int 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})))
;; 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})))
;; 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}))))
;; wolves breed.
(fn [cell world]
(cond
(>= (get-int cell :wolves) 2)
(merge cell {:wolves (int (* (:wolves cell) 2))})))
))
;; rules which initialise the world
(def init-rules
(list
;; below the waterline, we have water.
(fn [cell world]
(cond (and (= (:state cell) :new) (< (get-int cell :altitude) waterline)) (merge cell {:state :water})))
;; above the snowline, we have snow.
(fn [cell world]
(cond (and (= (:state cell) :new) (> (get-int cell :altitude) snowline)) (merge cell {:state :snow})))
;; in between, we have a wasteland.
(fn [cell world] (cond (= (:state cell) :new) (merge cell {:state :grassland}))
)))
(def natural-rules (flatten
(list
vegetation-rules
herbivore-rules
;; predator-rules
)))