001 (ns ^{:doc "A set of MicroWorld rules describing a simplified natural ecosystem.
002
003 Since the completion of the rule language this is more or less obsolete -
004 there are still a few things that you can do with rules written in Clojure
005 that you can't do in the rule language, but not many and I doubt they're
006 important. "
007 :author " Simon Brooke "}
008 mw-engine.natural-rules
009 (:require [mw-engine.utils :refer [get-int get-neighbours get-neighbours-with-state member?]]))
010
011 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
012 ;;;;
013 ;;;; mw-engine: the state/transition engine of MicroWorld.
014 ;;;;
015 ;;;; This program is free software; you can redistribute it and/or
016 ;;;; modify it under the terms of the GNU General Public License
017 ;;;; as published by the Free Software Foundation; either version 2
018 ;;;; of the License, or (at your option) any later version.
019 ;;;;
020 ;;;; This program is distributed in the hope that it will be useful,
021 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
022 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023 ;;;; GNU General Public License for more details.
024 ;;;;
025 ;;;; You should have received a copy of the GNU General Public License
026 ;;;; along with this program; if not, write to the Free Software
027 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
028 ;;;; USA.
029 ;;;;
030 ;;;; Copyright (C) 2014 Simon Brooke
031 ;;;;
032 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
033
034 ;; treeline at arbitrary altitude.
035 (def treeline 150)
036
037 ;; waterline also at arbitrary altitude.
038 (def waterline 10)
039
040 ;; and finally snowline is also arbitrary.
041 (def snowline 200)
042
043 ;; Rare chance of lightning strikes
044 (def lightning-probability 500)
045
046 ;; rules describing vegetation
047 (def vegetation-rules
048 (list
049 ;; Randomly, birds plant tree seeds into grassland.
050 (fn [cell _] (cond (and (= (:state cell) :grassland)(< (rand 10) 1))(merge cell {:state :heath})))
051 ;; heath below the treeline grows gradually into forest, providing browsing pressure is not to high
052 (fn [cell _]
053 (cond (and
054 (= (:state cell) :heath)
055 ;; browsing limit really ought to vary with soil fertility, but...
056 (< (+ (get-int cell :deer)(get-int cell :sheep)) 6)
057 (< (get-int cell :altitude) treeline))
058 (merge cell {:state :scrub})))
059 (fn [cell _] (cond (= (:state cell) :scrub) (merge cell {:state :forest})))
060 ;; Forest on fertile land grows to climax
061 (fn [cell _]
062 (cond
063 (and
064 (= (:state cell) :forest)
065 (> (get-int cell :fertility) 10))
066 (merge cell {:state :climax})))
067 ;; Climax forest occasionally catches fire (e.g. lightning strikes)
068 (fn [cell _] (cond (and (= (:state cell) :climax)(< (rand lightning-probability) 1)) (merge cell {:state :fire})))
069 ;; Climax forest neighbouring fires is likely to catch fire
070 (fn [cell world]
071 (cond
072 (and (= (:state cell) :climax)
073 (< (rand 3) 1)
074 (not (empty? (get-neighbours-with-state world (:x cell) (:y cell) 1 :fire))))
075 (merge cell {:state :fire})))
076 ;; After fire we get waste
077 (fn [cell _] (cond (= (:state cell) :fire) (merge cell {:state :waste})))
078 ;; And after waste we get pioneer species; if there's a woodland seed
079 ;; source, it's going to be heath, otherwise grassland.
080 (fn [cell world]
081 (cond
082 (and (= (:state cell) :waste)
083 (not
084 (empty?
085 (flatten
086 (list
087 (get-neighbours-with-state world (:x cell) (:y cell) 1 :scrub)
088 (get-neighbours-with-state world (:x cell) (:y cell) 1 :forest)
089 (get-neighbours-with-state world (:x cell) (:y cell) 1 :climax))))))
090 (merge cell {:state :heath})))
091 (fn [cell _]
092 (cond (= (:state cell) :waste)
093 (merge cell {:state :grassland})))
094 ;; Forest increases soil fertility
095 (fn [cell _]
096 (cond (member? (:state cell) '(:forest :climax))
097 (merge cell {:fertility (+ (get-int cell :fertility) 1)})))))
098
099
100 ;; rules describing herbivore behaviour
101 (def herbivore-rules
102 (list
103 ;; if there are too many deer for the fertility of the area to sustain,
104 ;; some die or move on.
105 (fn [cell _]
106 (cond (> (get-int cell :deer) (get-int cell :fertility))
107 (merge cell {:deer (get-int cell :fertility)})))
108 ;; deer arrive occasionally at the edge of the map.
109 (fn [cell world]
110 (cond (and (< (count (get-neighbours world cell)) 8)
111 (< (rand 50) 1)
112 (> (get-int cell :fertility) 0)
113 (= (get-int cell :deer) 0))
114 (merge cell {:deer 2})))
115 ;; deer gradually spread through the world by breeding or migrating.
116 (fn [cell world]
117 (let [n (apply + (map #(get-int % :deer) (get-neighbours world cell)))]
118 (cond (and
119 (> (get-int cell :fertility) 0)
120 (= (get-int cell :deer) 0)
121 (>= n 2))
122 (merge cell {:deer (int (/ n 2))}))))
123 ;; deer breed.
124 (fn [cell _]
125 (cond
126 (>= (get-int cell :deer) 2)
127 (merge cell {:deer (int (* (:deer cell) 2))})))))
128
129 ;; rules describing predator behaviour
130 (def predator-rules
131 (list
132 ;; wolves eat deer
133 (fn [cell _]
134 (cond
135 (>= (get-int cell :wolves) 1)
136 (merge cell {:deer (max 0 (- (get-int cell :deer) (get-int cell :wolves)))})))
137 ;; ;; not more than eight wolves in a pack, for now (hack because wolves are not dying)
138 ;; (fn [cell world]
139 ;; (cond (> (get-int cell :wolves) 8) (merge cell {:wolves 8})))
140 ;; if there are not enough deer to sustain the get-int of wolves,
141 ;; some wolves die or move on. (doesn't seem to be working?)
142 (fn [cell _]
143 (cond (> (get-int cell :wolves) (get-int cell :deer))
144 (merge cell {:wolves 0})))
145 ;; wolves arrive occasionally at the edge of the map.
146 (fn [cell world]
147 (cond (and (< (count (get-neighbours world cell)) 8)
148 (< (rand 50) 1)
149 (not (= (:state cell) :water))
150 (= (get-int cell :wolves) 0))
151 (merge cell {:wolves 2})))
152 ;; wolves gradually spread through the world by breeding or migrating.
153 (fn [cell world]
154 (let [n (apply + (map #(get-int % :wolves) (get-neighbours world cell)))]
155 (cond (and
156 (not (= (:state cell) :water))
157 (= (get-int cell :wolves) 0)
158 (>= n 2))
159 (merge cell {:wolves 2}))))
160 ;; wolves breed.
161 (fn [cell _]
162 (cond
163 (>= (get-int cell :wolves) 2)
164 (merge cell {:wolves (int (* (:wolves cell) 2))})))))
165
166
167 ;; rules which initialise the world
168 (def init-rules
169 (list
170 ;; below the waterline, we have water.
171 (fn [cell _]
172 (cond (and (= (:state cell) :new) (< (get-int cell :altitude) waterline)) (merge cell {:state :water})))
173 ;; above the snowline, we have snow.
174 (fn [cell _]
175 (cond (and (= (:state cell) :new) (> (get-int cell :altitude) snowline)) (merge cell {:state :snow})))
176 ;; in between, we have a wasteland.
177 (fn [cell _] (cond (= (:state cell) :new) (merge cell {:state :grassland})))))
178
179
180 (def natural-rules (flatten
181 (list
182 vegetation-rules
183 herbivore-rules
184 predator-rules)))