Functions to transform a world and run rules.
+dependenciesorg.clojure/clojure |
| 1.8.0 | org.clojure/clojurescript |
| 1.10.764 | org.clojure/math.combinatorics |
| 0.0.7 | org.clojure/tools.trace |
| 0.7.8 | org.clojure/tools.namespace |
| 0.2.4 | com.taoensso/timbre |
| 4.10.0 | fivetonine/collage |
| 0.2.0 | hiccup |
| 1.0.5 | net.mikera/imagez |
| 0.3.1 |
|
(this space intentionally left almost blank) |
| |
| |
A set of MicroWorld rules describing a simplified natural ecosystem.
| (ns ^{:doc
- :author "Simon Brooke"}
- mw-engine.core
- (:require [clojure.core.reducers :as r]
- [mw-engine.world :as world]
- [mw-engine.utils :refer [get-int-or-zero map-world]])
- (:gen-class)) |
mw-engine: the state/transition engine of MicroWorld.
+ :author "Simon Brooke"}
+ mw-engine.natural-rules
+ (:require [mw-engine.utils :refer :all]
+ [mw-engine.world :refer :all])) |
mw-engine: the state/transition engine of MicroWorld.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -3055,136 +2890,143 @@ USA.
Copyright (C) 2014 Simon Brooke
-Every rule is a function of two arguments, a cell and a world. If the rule
-fires, it returns a new cell, which should have the same values for :x and
-:y as the old cell. Anything else can be modified.
-
-While any function of two arguments can be used as a rule, a special high
-level rule language is provided by the mw-parser package, which compiles
-rules expressed in a subset of English rules into suitable functions.
-
-A cell is a map containing at least values for the keys :x, :y, and :state;
-a transformation should not alter the values of :x or :y, and should not
-return a cell without a keyword as the value of :state. Anything else is
-legal.
-
-A world is a two dimensional matrix (sequence of sequences) of cells, such
-that every cell's :x and :y properties reflect its place in the matrix.
-See world.clj .
-
-Each time the world is transformed (see transform-world , for each cell,
-rules are applied in turn until one matches. Once one rule has matched no
-further rules can be applied.
- | |
Apply a single rule to a cell . What this is about is that I want to be able,
- for debugging purposes, to tag a cell with the rule text of the rule which
- fired (and especially so when an exception is thrown. So a rule may be either
- an ifn, or a list (ifn source-text). This function deals with despatching
- on those two possibilities. world is also passed in in order to be able
- to access neighbours.
- | (defn apply-rule
- ([world cell rule]
- (cond
- (ifn? rule) (apply-rule world cell rule nil)
- (seq? rule) (let [[afn src] rule] (apply-rule world cell afn src))))
- ([world cell rule source]
- (let [result (apply rule (list cell world))]
+Since the completion of the rule language this is more or less obsolete -
+there are still a few things that you can do with rules written in Clojure
+that you can't do in the rule language, but not many and I doubt they're
+important.
+ | |
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 result source) (merge result {:rule source})
- true result)))) |
Derive a cell from this cell of this world by applying these rules .
- | (defn- apply-rules
- [world cell rules]
- (cond (empty? rules) cell
- true (let [result (apply-rule world cell (first rules))]
- (cond result result
- true (apply-rules world cell (rest rules)))))) |
Derive a cell from this cell of this world by applying these rules . If an
- exception is thrown, cache its message on the cell and set it's state to error
- | (defn- transform-cell
- [world cell rules]
- (try
- (merge
- (apply-rules world cell rules)
- {:generation (+ (get-int-or-zero cell :generation) 1)})
- (catch Exception e
- (merge cell {:error
- (format "%s at generation %d when in state %s"
- (.getMessage e)
- (:generation cell)
- (:state cell))
- :stacktrace (map #(.toString %) (.getStackTrace e))
- :state :error})))) |
Return a world derived from this world by applying these rules to each cell.
- | (defn transform-world
- [world rules]
- (map-world world transform-cell (list rules))) |
Consider this single argument as a map of :world and :rules ; apply the rules
- to transform the world, and return a map of the new, transformed :world and
- these :rules . As a side effect, print the world.
- | (defn- transform-world-state
- [state]
- (let [world (transform-world (:world state) (:rules state))]
- ;;(world/print-world world)
- {:world world :rules (:rules state)})) |
Run this world with these rules for this number of generations.
-
-
- | (defn run-world
- [world init-rules rules generations]
- (reduce (fn [world _iteration]
- (transform-world world rules))
- (transform-world world init-rules)
- (range generations))) |
| |
| |
Simple functions to allow a world to be visualised.
- | (ns ^{:doc
- :author "Simon Brooke"}
- mw-engine.display
- (:require [hiccup.core :refer [html]]
- mw-engine.utils
- mw-engine.world)) |
mw-engine: the state/transition engine of MicroWorld.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-USA.
-
-Copyright (C) 2014 Simon Brooke
- | |
- | (defn format-css-class [state]
- "Format this `state`, assumed to be a keyword indicating a state in the
- world, into a CSS class"
- (subs (str state) 1)) |
Render this state , assumed to be a keyword indicating a state in the
- world, into a path which should recover the corresponding image file.
- | (defn format-image-path
- [state]
- (format "img/tiles/%s.png" (format-css-class state))) |
- | (defn format-mouseover [cell]
- (str cell)) |
Render this world cell as a Hiccup table cell.
- | (defn render-cell
- [cell]
- (let [state (:state cell)]
- [:td {:class (format-css-class state) :title (format-mouseover cell)}
- [:a {:href (format "inspect?x=%d&y=%d" (:x cell) (:y cell))}
- [:img {:alt (:state cell) :width 32 :height 32 :src (format-image-path state)}]]])) |
Render this world row as a Hiccup table row.
- | (defn render-world-row
- [row]
- (apply vector (cons :tr (map render-cell row)))) |
Render this world as a Hiccup table.
- | (defn render-world-table
- [world]
- (apply vector
- (cons :table
- (map render-world-row world)))) |
| |
| |
Experimental, probably of no interest to anyone else; attempt to
+ (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))) |
| |
| |
Experimental, probably of no interest to anyone else; attempt to
compute drainage on a world, assumed to have altitudes already set
from a heightmap.
| (ns ^{:doc
@@ -3352,7 +3194,116 @@ USA.
[hmap]
"Create a world from the heightmap `hmap`, rain on it, and then compute river
flows."
- (flow-world (rain-world (flood-hollows (heightmap/apply-heightmap hmap))))) |
| |
| |
Functions to apply a heightmap to a world.
+ (flow-world (rain-world (flood-hollows (heightmap/apply-heightmap hmap))))) |
| |
| |
Functions to transform a world and run rules.
+ | (ns ^{:doc
+ :author "Simon Brooke"}
+ mw-engine.core
+ (:require [clojure.core.reducers :as r]
+ [clojure.string :refer [join]]
+ [mw-engine.world :as world]
+ [mw-engine.utils :refer [get-int-or-zero map-world]]
+ [taoensso.timbre :as l])) |
mw-engine: the state/transition engine of MicroWorld.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+USA.
+
+Copyright (C) 2014 Simon Brooke
+
+Every rule is a function of two arguments, a cell and a world. If the rule
+fires, it returns a new cell, which should have the same values for :x and
+:y as the old cell. Anything else can be modified.
+
+While any function of two arguments can be used as a rule, a special high
+level rule language is provided by the mw-parser package, which compiles
+rules expressed in a subset of English rules into suitable functions.
+
+A cell is a map containing at least values for the keys :x, :y, and :state;
+a transformation should not alter the values of :x or :y, and should not
+return a cell without a keyword as the value of :state. Anything else is
+legal.
+
+A world is a two dimensional matrix (sequence of sequences) of cells, such
+that every cell's :x and :y properties reflect its place in the matrix.
+See world.clj .
+
+Each time the world is transformed (see transform-world , for each cell,
+rules are applied in turn until one matches. Once one rule has matched no
+further rules can be applied.
+ | |
Apply a single rule to a cell . What this is about is that I want to be able,
+ for debugging purposes, to tag a cell with the rule text of the rule which
+ fired (and especially so when an exception is thrown. So a rule may be either
+ an ifn, or a list (ifn source-text). This function deals with despatching
+ on those two possibilities. world is also passed in in order to be able
+ to access neighbours.
+ | (defn apply-rule
+ ([world cell rule]
+ (cond
+ (ifn? rule) (apply-rule world cell rule nil)
+ (seq? rule) (let [[afn src] rule] (apply-rule world cell afn src))))
+ ([world cell rule source]
+ (let [result (apply rule (list cell world))]
+ (cond
+ (and result source) (merge result {:rule source})
+ true result)))) |
Derive a cell from this cell of this world by applying these rules .
+ | (defn- apply-rules
+ [world cell rules]
+ (cond (empty? rules) cell
+ true (let [result (apply-rule world cell (first rules))]
+ (cond result result
+ true (apply-rules world cell (rest rules)))))) |
Derive a cell from this cell of this world by applying these rules . If an
+ exception is thrown, cache its message on the cell and set it's state to error
+ | (defn- transform-cell
+ [world cell rules]
+ (try
+ (merge
+ (apply-rules world cell rules)
+ {:generation (+ (get-int-or-zero cell :generation) 1)})
+ (catch Exception e
+ (merge cell {:error
+ (format "%s at generation %d when in state %s"
+ (.getMessage e)
+ (:generation cell)
+ (:state cell))
+ :stacktrace (map #(.toString %) (.getStackTrace e))
+ :state :error})))) |
Return a world derived from this world by applying these rules to each cell.
+ | (defn transform-world
+ [world rules]
+ (map-world world transform-cell (list rules))) |
Consider this single argument as a map of :world and :rules ; apply the rules
+ to transform the world, and return a map of the new, transformed :world and
+ these :rules . As a side effect, print the world.
+ | (defn- transform-world-state
+ [state]
+ (let [world (transform-world (:world state) (:rules state))]
+ ;;(world/print-world world)
+ {:world world :rules (:rules state)})) |
Run this world with these rules for this number of generations.
+
+
+ | (defn run-world
+ [world init-rules rules generations]
+ (reduce (fn [world iteration]
+ (l/info "Running iteration " iteration)
+ (transform-world world rules))
+ (transform-world world init-rules)
+ (range generations))) |
| |
| |
Functions to apply a heightmap to a world.
| (ns ^{:doc
:author "Simon Brooke"}
mw-engine.heightmap
@@ -3472,12 +3423,12 @@ a world the size of the heightmap will be created;
(let [heightmap (imagez/filter-image
(filters/grayscale)
(collage/load-image imagepath))]
- (map-world world tag-property (list property heightmap)))) |
| |
| |
A set of MicroWorld rules describing a simplified natural ecosystem.
+ (map-world world tag-property (list property heightmap)))) |
| |
| |
Functions to create and to print two dimensional cellular automata.
| (ns ^{:doc
:author "Simon Brooke"}
- mw-engine.natural-rules
- (:require [mw-engine.utils :refer :all]
- [mw-engine.world :refer :all])) |
mw-engine: the state/transition engine of MicroWorld.
+ mw-engine.world
+ (:require [clojure.string :as string :only [join]]
+ [mw-engine.utils :refer [population]])) |
mw-engine: the state/transition engine of MicroWorld.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -3496,143 +3447,132 @@ USA.
Copyright (C) 2014 Simon Brooke
-Since the completion of the rule language this is more or less obsolete -
-there are still a few things that you can do with rules written in Clojure
-that you can't do in the rule language, but not many and I doubt they're
-important.
- | |
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))) |
| |
| |
Utility functions needed by MicroWorld and, specifically, in the
+ Functions to create and to print two dimensional cellular automata.
+Nothing in this namespace should determine what states are possible within
+the automaton, except for the initial state, :new.
+
+A cell is a map containing at least values for the keys :x, :y, and :state.
+
+A world is a two dimensional matrix (sequence of sequences) of cells, such
+that every cell's :x and :y properties reflect its place in the matrix.
+ | |
Create a minimal default cell at x, y
+
+
+x the x coordinate at which this cell is created;
+y the y coordinate at which this cell is created.
+
+ | (defn- make-cell
+ [x y]
+ {:x x :y y :state :new}) |
Make the (remaining) cells in a row at this height in a world of this width.
+
+
+index x coordinate of the next cell to be created;
+width total width of the matrix, in cells;
+height y coordinate of the next cell to be created.
+
+ | (defn- make-world-row
+ [index width height]
+ (cond (= index width) nil
+ true (cons (make-cell index height)
+ (make-world-row (inc index) width height)))) |
Make the (remaining) rows in a world of this width and height, from this
+ index.
+
+
+index y coordinate of the next row to be created;
+width total width of the matrix, in cells;
+height total height of the matrix, in cells.
+
+ | (defn- make-world-rows
+ [index width height]
+ (cond (= index height) nil
+ true (cons (apply vector (make-world-row 0 width index))
+ (make-world-rows (inc index) width height)))) |
Make a world width cells from east to west, and height cells from north to
+ south.
+
+
+width a natural number representing the width of the matrix to be created;
+height a natural number representing the height of the matrix to be created.
+
+ | (defn make-world
+ [width height]
+ (apply vector (make-world-rows 0 width height))) |
Truncate the print name of the state of this cell to at most limit characters.
+ | (defn truncate-state
+ [cell limit]
+ (let [s (:state cell)]
+ (cond (> (count (str s)) limit) (subs s 0 limit)
+ true s))) |
Return a formatted string summarising the current state of this cell.
+ | (defn format-cell
+ [cell]
+ (format "%10s(%2d/%2d)"
+ (truncate-state cell 10)
+ (population cell :deer)
+ (population cell :wolves))) |
Format one row in the state of a world for printing.
+ | (defn- format-world-row
+ [row]
+ (string/join (map format-cell row))) |
Print the current state of this world, and return nil.
+
+
+world a world as defined above.
+
+ | (defn print-world
+ [world]
+ (println)
+ (dorun
+ (map
+ #(println
+ (format-world-row %))
+ world))
+ nil) |
| |
| |
Simple functions to allow a world to be visualised.
+ | (ns ^{:doc
+ :author "Simon Brooke"}
+ mw-engine.display
+ (:require [hiccup.core :refer [html]]
+ mw-engine.utils
+ mw-engine.world)) |
mw-engine: the state/transition engine of MicroWorld.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+USA.
+
+Copyright (C) 2014 Simon Brooke
+ | |
Base url (i.e., url of directory) from which to load tile images.
+ | (def ^:dynamic *image-base*
+ "img/tiles") |
+ | (defn format-css-class [state]
+ "Format this `state`, assumed to be a keyword indicating a state in the
+ world, into a CSS class"
+ (subs (str state) 1)) |
Render this state , assumed to be a keyword indicating a state in the
+ world, into a path which should recover the corresponding image file.
+ | (defn format-image-path
+ [state]
+ (format "%s/%s.png" *image-base* (format-css-class state))) |
+ | (defn format-mouseover [cell]
+ (str cell)) |
Render this world cell as a Hiccup table cell.
+ | (defn render-cell
+ [cell]
+ (let [state (:state cell)]
+ [:td {:class (format-css-class state) :title (format-mouseover cell)}
+ [:a {:href (format "inspect?x=%d&y=%d" (:x cell) (:y cell))}
+ [:img {:alt (:state cell) :width 32 :height 32 :src (format-image-path state)}]]])) |
Render this world row as a Hiccup table row.
+ | (defn render-world-row
+ [row]
+ (apply vector (cons :tr (map render-cell row)))) |
Render this world as a Hiccup table.
+ | (defn render-world-table
+ [world]
+ (apply vector
+ (cons :table
+ (map render-world-row world)))) |
| |
| |
Utility functions needed by MicroWorld and, specifically, in the
interpretation of MicroWorld rule.
| (ns ^{:doc
:author "Simon Brooke"}
@@ -3680,7 +3620,7 @@ USA.
| (defn init-generation
[world cell]
(merge cell {:generation (get-int-or-zero cell :generation)})) |
True if x, y are in bounds for this world (i.e., there is a cell at x, y)
- else false.
+ else false. DEPRECATED: it's a predicate, prefer in-bounds? .
world a world as defined above;
@@ -3688,46 +3628,58 @@ USA.
y a number which may or may not be a valid y coordinate within that world.
| (defn in-bounds
+ {:deprecated "1.1.7"}
+ [world x y]
+ (and (>= x 0)(>= y 0)(< y (count world))(< x (count (first world))))) |
True if x, y are in bounds for this world (i.e., there is a cell at x, y)
+ else false.
+
+
+world a world as defined above;
+x a number which may or may not be a valid x coordinate within that world;
+y a number which may or may not be a valid y coordinate within that world.
+
+ | (defn in-bounds?
+ {:added "1.1.7"}
[world x y]
(and (>= x 0)(>= y 0)(< y (count world))(< x (count (first world))))) |
Wholly non-parallel map world implementation; see documentation for map-world .
| (defn map-world-n-n
([world function]
- (map-world-n-n world function nil))
+ (map-world-n-n world function nil))
([world function additional-args]
- (into []
- (map (fn [row]
- (into [] (map
- #(apply function
- (cons world (cons % additional-args)))
- row)))
- world)))) |
Wholly parallel map-world implementation; see documentation for map-world .
+ (into []
+ (map (fn [row]
+ (into [] (map
+ #(apply function
+ (cons world (cons % additional-args)))
+ row)))
+ world)))) |
Wholly parallel map-world implementation; see documentation for map-world .
| (defn map-world-p-p
([world function]
- (map-world-p-p world function nil))
+ (map-world-p-p world function nil))
([world function additional-args]
- (into []
- (pmap (fn [row]
- (into [] (pmap
- #(apply function
- (cons world (cons % additional-args)))
- row)))
- world)))) |
Apply this function to each cell in this world to produce a new world.
- the arguments to the function will be the world, the cell, and any
- additional-args supplied. Note that we parallel map over rows but
- just map over cells within a row. That's because it isn't worth starting
- a new thread for each cell, but there may be efficiency gains in
- running rows in parallel.
+ (into []
+ (pmap (fn [row]
+ (into [] (pmap
+ #(apply function
+ (cons world (cons % additional-args)))
+ row)))
+ world)))) |
Apply this function to each cell in this world to produce a new world.
+ the arguments to the function will be the world, the cell, and any
+ additional-args supplied. Note that we parallel map over rows but
+ just map over cells within a row. That's because it isn't worth starting
+ a new thread for each cell, but there may be efficiency gains in
+ running rows in parallel.
| (defn map-world
([world function]
- (map-world world function nil))
+ (map-world world function nil))
([world function additional-args]
- (into []
- (pmap (fn [row]
- (into [] (map
- #(apply function
- (cons world (cons % additional-args)))
- row)))
- world)))) |
Return the cell a x, y in this world, if any.
+ (into []
+ (pmap (fn [row]
+ (into [] (map
+ #(apply function
+ (cons world (cons % additional-args)))
+ row)))
+ world)))) |
Return the cell a x, y in this world, if any.
world a world as defined above;
@@ -3736,7 +3688,7 @@ USA.
| (defn get-cell
[world x y]
- (cond (in-bounds world x y)
+ (when (in-bounds world x y)
(nth (nth world y) x))) |
Get the value of a property expected to be an integer from a map; if not present (or not an integer) return 0.
@@ -3745,11 +3697,11 @@ USA.
| (defn get-int
[map key]
- (cond (map? map)
+ (if (map? map)
(let [v (map key)]
(cond (and v (integer? v)) v
true 0))
- true (throw (Exception. "No map passed?")))) |
Return the population of this species in this cell. Currently a synonym for
+ (throw (Exception. "No map passed?")))) |
Return the population of this species in this cell. Currently a synonym for
get-int , but may not always be (depending whether species are later
implemented as actors)
@@ -3894,104 +3846,81 @@ coordinates [x,y] in this world.
(= (:y cell)(:y %2)))
(merge %2 cell)
%2))
- world)) |
| |
| |
Functions to create and to print two dimensional cellular automata.
- | (ns ^{:doc
- :author "Simon Brooke"}
- mw-engine.world
- (:require [clojure.string :as string :only [join]]
- [mw-engine.utils :refer [population]])) |
mw-engine: the state/transition engine of MicroWorld.
+ world)) |
| |