Updated documentation

This commit is contained in:
Simon Brooke 2014-07-10 11:33:23 +01:00
parent c0e5729c81
commit f3abd6276f
3 changed files with 307 additions and 116 deletions

View file

@ -3038,6 +3038,10 @@ net.brehaut.ClojureTools = (function (SH) {
fires, it returns a new cell, which should have the same values for :x and 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.</p> :y as the old cell. Anything else can be modified.</p>
<p>While any function of two arguments can be used as a rule, a special high
level rule language is provided by the <code>mw-parser</code> package, which compiles
rules expressed in a subset of English rules into suitable functions.</p>
<p>A cell is a map containing at least values for the keys :x, :y, and :state; <p>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 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 return a cell without a keyword as the value of :state. Anything else is
@ -3047,7 +3051,9 @@ legal.</p>
that every cell's :x and :y properties reflect its place in the matrix. that every cell's :x and :y properties reflect its place in the matrix.
See <code>world.clj</code>.</p> See <code>world.clj</code>.</p>
<p>Rules are applied in turn until one matches.</p> <p>Each time the world is transformed (see <code>transform-world</code>, for each cell,
rules are applied in turn until one matches. Once one rule has matched no
further rules can be applied.</p>
</td><td class="codes"></td></tr><tr><td class="docs"><p>Derive a cell from this cell of this world by applying these rules.</p> </td><td class="codes"></td></tr><tr><td class="docs"><p>Derive a cell from this cell of this world by applying these rules.</p>
</td><td class="codes"><pre class="brush: clojure">(defn- transform-cell </td><td class="codes"><pre class="brush: clojure">(defn- transform-cell
[cell world rules] [cell world rules]
@ -3080,21 +3086,7 @@ See <code>world.clj</code>.</p>
</td><td class="codes"><pre class="brush: clojure">(defn run-world </td><td class="codes"><pre class="brush: clojure">(defn run-world
[world init-rules rules generations] [world init-rules rules generations]
(let [state {:world (transform-world world init-rules) :rules rules}] (let [state {:world (transform-world world init-rules) :rules rules}]
(take generations (iterate transform-world-state state))))</pre></td></tr><tr><td class="docs"><p>(defn animate-world (take generations (iterate transform-world-state state))))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-engine.heightmap" name="mw-engine.heightmap"><h1 class="project-name">mw-engine.heightmap</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>Functions to apply a heightmap to a world.</p>
"Run this world with these rules for this number of generations, and return nil
to avoid cluttering the screen. Principally for debugging.</p>
</td><td class="codes"></td></tr><tr><td class="docs"><ul>
<li><code>world</code> a world as discussed above;</li>
<li><code>init-rules</code> a sequence of rules as defined above, to be run once to initialise the world;</li>
<li><code>rules</code> a sequence of rules as definied above, to be run iteratively for each generation;</li>
<li><code>generations</code> an (integer) number of generations."
[world init-rules rules generations]
(let [state (list (transform-world world init-rules) rules)]
(dorun
(take generations (iterate transform-world-state state)))
state))</li>
</ul>
</td><td class="codes"></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-engine.heightmap" name="mw-engine.heightmap"><h1 class="project-name">mw-engine.heightmap</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>Functions to apply a heightmap to a world.</p>
<p>Heightmaps are considered only as greyscale images, so colour is redundent (will be <p>Heightmaps are considered only as greyscale images, so colour is redundent (will be
ignored). Darker shades are higher.</p> ignored). Darker shades are higher.</p>
@ -3109,7 +3101,7 @@ ignored). Darker shades are higher.</p>
[fivetonine.collage.util]))</pre></td></tr><tr><td class="docs"><p>Surprisingly, Clojure doesn't seem to have an abs function, or else I've [fivetonine.collage.util]))</pre></td></tr><tr><td class="docs"><p>Surprisingly, Clojure doesn't seem to have an abs function, or else I've
missed it. So here's one of my own. Maps natural numbers onto themselves, missed it. So here's one of my own. Maps natural numbers onto themselves,
and negative integers onto natural numbers. Also maps negative real numbers and negative integers onto natural numbers. Also maps negative real numbers
onto positive real numbers, but I don't care so much about them.</p> onto positive real numbers.</p>
<ul> <ul>
<li><code>n</code> a number, on the set of real numbers.</li> <li><code>n</code> a number, on the set of real numbers.</li>
@ -3117,8 +3109,7 @@ ignored). Darker shades are higher.</p>
</td><td class="codes"><pre class="brush: clojure">(defn- abs </td><td class="codes"><pre class="brush: clojure">(defn- abs
[n] [n]
(cond (&lt; n 0) (- 0 n) true n))</pre></td></tr><tr><td class="docs"><p>Set the altitude of this cell from the corresponding pixel of this heightmap. (cond (&lt; n 0) (- 0 n) true n))</pre></td></tr><tr><td class="docs"><p>Set the altitude of this cell from the corresponding pixel of this heightmap.
If the heightmap you supply is smaller than the world, this will break and If the heightmap you supply is smaller than the world, this will break.</p>
it's ALL YOUR FAULT.</p>
<ul> <ul>
<li><code>cell</code> a cell, as discussed in world.clj, q.v. Alternatively, a map;</li> <li><code>cell</code> a cell, as discussed in world.clj, q.v. Alternatively, a map;</li>
@ -3137,8 +3128,7 @@ ignored). Darker shades are higher.</p>
(get-int cell :x) (get-int cell :x)
(get-int cell :y)) 256))))}))</pre></td></tr><tr><td class="docs"><p>Set the altitude of each cell in this sequence from the corresponding pixel (get-int cell :y)) 256))))}))</pre></td></tr><tr><td class="docs"><p>Set the altitude of each cell in this sequence from the corresponding pixel
of this heightmap. of this heightmap.
If the heightmap you supply is smaller than the world, this will break and If the heightmap you supply is smaller than the world, this will break.</p>
it's ALL YOUR FAULT.</p>
<ul> <ul>
<li><code>row</code> a row in a world, as discussed in world.clj, q.v. Alternatively, a <li><code>row</code> a row in a world, as discussed in world.clj, q.v. Alternatively, a
@ -3148,7 +3138,7 @@ ignored). Darker shades are higher.</p>
</ul> </ul>
</td><td class="codes"><pre class="brush: clojure">(defn- apply-heightmap-row </td><td class="codes"><pre class="brush: clojure">(defn- apply-heightmap-row
[row heightmap] [row heightmap]
(apply vector (map #(transform-altitude %1 heightmap) row)))</pre></td></tr><tr><td class="docs"><p>Apply the image file loaded from this path to this world, and return a world whose (apply vector (map #(transform-altitude % heightmap) row)))</pre></td></tr><tr><td class="docs"><p>Apply the image file loaded from this path to this world, and return a world whose
altitudes are modified (added to) by the altitudes in the heightmap. It is assumed that altitudes are modified (added to) by the altitudes in the heightmap. It is assumed that
the heightmap is at least as large in x and y dimensions as the world.</p> the heightmap is at least as large in x and y dimensions as the world.</p>
@ -3160,7 +3150,7 @@ ignored). Darker shades are higher.</p>
[world imagepath] [world imagepath]
;; bizarrely, the collage load-util is working for me, but the imagez version isn't. ;; bizarrely, the collage load-util is working for me, but the imagez version isn't.
(let [heightmap (filter-image (grayscale)(load-image imagepath))] (let [heightmap (filter-image (grayscale)(load-image imagepath))]
(apply vector (map #(apply-heightmap-row %1 heightmap) world))))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-engine.natural-rules" name="mw-engine.natural-rules"><h1 class="project-name">mw-engine.natural-rules</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>A set of MicroWorld rules describing a simplified natural ecosystem.</p> (apply vector (map #(apply-heightmap-row % heightmap) world))))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-engine.natural-rules" name="mw-engine.natural-rules"><h1 class="project-name">mw-engine.natural-rules</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>A set of MicroWorld rules describing a simplified natural ecosystem.</p>
</td><td class="codes"></td></tr><tr><td class="docs"> </td><td class="codes"></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-engine.natural-rules </td><td class="codes"><pre class="brush: clojure">(ns mw-engine.natural-rules
(:use mw-engine.utils (:use mw-engine.utils
@ -3343,33 +3333,52 @@ ignored). Darker shades are higher.</p>
</ul> </ul>
</td><td class="codes"><pre class="brush: clojure">(defn population </td><td class="codes"><pre class="brush: clojure">(defn population
[cell species] [cell species]
(get-int cell species))</pre></td></tr><tr><td class="docs"> (get-int cell species))</pre></td></tr><tr><td class="docs"><p>Get the neighbours to distance depth of the cell at x, y in this world.</p>
<pre><code>* `world` a world, as described in world.clj;
* `x` an integer representing an x coordinate in that world;
* `y` an integer representing an y coordinate in that world;
* `depth` an integer representing the distance from [x,y] that
should be searched.
</code></pre>
</td><td class="codes"><pre class="brush: clojure">(defn get-neighbours </td><td class="codes"><pre class="brush: clojure">(defn get-neighbours
([world x y depth] ([world x y depth]
&quot;Get the neighbours to distance depth of the cell at x, y in this world. (remove nil?
* `world` a world, as described in world.clj; (map #(get-cell world (first %) (first (rest %)))
* `x` an integer representing an x coordinate in that world; (remove #(= % (list x y))
* `y` an integer representing an y coordinate in that world; (combo/cartesian-product
* `depth` an integer representing the distance from [x,y] that (range (- x depth) (+ x depth 1))
should be searched.&quot; (range (- y depth) (+ y depth 1)))))))
(remove nil? ([world cell depth]
(map #(get-cell world (first %) (first (rest %))) &quot;Get the neighbours to distance depth of this cell in this world.
(remove #(= % (list x y)) * `world` a world, as described in world.clj;
(combo/cartesian-product * `cell` a cell within that world;
(range (- x depth) (+ x depth 1)) * `depth` an integer representing the distance from [x,y] that
(range (- y depth) (+ y depth 1))))))) should be searched.&quot;
([world cell depth] (get-neighbours world (:x cell) (:y cell) depth))
&quot;Get the neighbours to distance depth of this cell in this world. ([world cell]
* `world` a world, as described in world.clj; &quot;Get the immediate neighbours of this cell in this world
* `cell` a cell within that world; * `world` a world, as described in world.clj;
* `depth` an integer representing the distance from [x,y] that * `cell` a cell within that world.&quot;
should be searched.&quot; (get-neighbours world cell 1)))</pre></td></tr><tr><td class="docs"><p>Get the neighbours to distance depth of the cell at x, y in this world which
(get-neighbours world (:x cell) (:y cell) depth)) have this value for this property.</p>
([world cell]
&quot;Get the immediate neighbours of this cell in this world <pre><code>* `world` a world, as described in `world.clj`;
* `world` a world, as described in world.clj; * `cell` a cell within that world;
* `cell` a cell within that world.&quot; * `depth` an integer representing the distance from [x,y] that
(get-neighbours world cell 1)))</pre></td></tr><tr><td class="docs"><p>Get the neighbours to distance depth of the cell at x, y in this world which should be searched;
* `property` a keyword representing a property of the neighbours.
* `value` a value of that property
</code></pre>
</td><td class="codes"><pre class="brush: clojure">(defn get-neighbours-with-property-value
([world x y depth property value]
(filter #(= (get % property) value) (get-neighbours world x y depth)))
([world cell depth property value]
(get-neighbours-with-property-value world (:x cell) (:y cell) depth
property value))
([world cell property value]
(get-neighbours-with-property-value world cell 1
property value)))</pre></td></tr><tr><td class="docs"><p>Get the neighbours to distance depth of the cell at x, y in this world which
have this state.</p> have this state.</p>
<pre><code>* `world` a world, as described in `world.clj`; <pre><code>* `world` a world, as described in `world.clj`;
@ -3379,8 +3388,12 @@ ignored). Darker shades are higher.</p>
* `state` a keyword representing a state in the world. * `state` a keyword representing a state in the world.
</code></pre> </code></pre>
</td><td class="codes"><pre class="brush: clojure">(defn get-neighbours-with-state </td><td class="codes"><pre class="brush: clojure">(defn get-neighbours-with-state
[world x y depth state] ([world x y depth state]
(filter #(= (:state %) state) (get-neighbours world x y depth)))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-engine.world" name="mw-engine.world"><h1 class="project-name">mw-engine.world</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>Functions to create and to print two dimensional cellular automata. Nothing in this (filter #(= (:state %) state) (get-neighbours world x y depth)))
([world cell depth state]
(get-neighbours-with-state world (:x cell) (:y cell) depth state))
([world cell state]
(get-neighbours-with-state world cell 1 state)))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-engine.world" name="mw-engine.world"><h1 class="project-name">mw-engine.world</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>Functions to create and to print two dimensional cellular automata. Nothing in this
file should determine what states are possible within the automaton, except for the file should determine what states are possible within the automaton, except for the
initial state, :new.</p> initial state, :new.</p>

View file

@ -3037,16 +3037,24 @@ net.brehaut.ClojureTools = (function (SH) {
<li>"if altitude is 100 or fertility is 25 then state should be heath"</li> <li>"if altitude is 100 or fertility is 25 then state should be heath"</li>
<li>"if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"</li> <li>"if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"</li>
<li>"if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"</li> <li>"if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"</li>
<li>"if state is grassland and 4 neighbours have state equal to water then state should be village"</li>
<li>"if state is forest and fertility is between 55 and 75 then state should be climax"</li>
<li>"if 6 neighbours have state equal to water then state should be village"</li>
<li>"if state is in grassland or pasture or heath and 4 neighbours are water then state should be village"</li>
<li>"if state is forest or state is climax and some neighbours have state equal to fire then 3 in 5 chance that state should be fire"</li>
<li>"if state is pasture and more than 3 neighbours have state equal to scrub then state should be scrub"</li>
*
</ul> </ul>
<p>It should also but does not yet parse rules of the form:</p> <p>it generates rules in the form expected by <code>mw-engine.core</code>, q.v.</p>
</td><td class="codes"></td></tr><tr><td class="docs"><ul>
<li>"if 6 neighbours have state is water then state should be fishery"</li>
<li>"if state is forest or state is climax and some neighbours have state is fire then 3 in 5 chance that state should be fire"</li>
<li>"if state is pasture and more than 3 neighbours have state is scrub then state should be scrub"</li>
</ul>
<p>it generates rules in the form expected by mw-engine.core</p> <p>It is, as I say, very simple; it generates a complete rule, or it fails completely, returning nil.
Very occasionally it generates a wrong rule - one which is not a correct translation of the rule
semantics - but that is buggy behaviour, which I'll try to fix over the next few weeks, not a
design fault.</p>
<p>More significantly it does not generate useful error messages on failure. This is, I think, a much
more complex issue which I don't yet know how to address.</p>
</td><td class="codes"></td></tr><tr><td class="docs"> </td><td class="codes"></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-parser.core </td><td class="codes"><pre class="brush: clojure">(ns mw-parser.core
(:use mw-engine.utils (:use mw-engine.utils
@ -3054,62 +3062,198 @@ net.brehaut.ClojureTools = (function (SH) {
</td><td class="codes"><pre class="brush: clojure">(declare parse-conditions) </td><td class="codes"><pre class="brush: clojure">(declare parse-conditions)
(declare parse-not-condition) (declare parse-not-condition)
(declare parse-simple-condition)</pre></td></tr><tr><td class="docs"><p>a regular expression which matches string representation of numbers</p> (declare parse-simple-condition)</pre></td></tr><tr><td class="docs"><p>a regular expression which matches string representation of numbers</p>
</td><td class="codes"><pre class="brush: clojure">(def re-number #&quot;^[0-9.]*$&quot;)</pre></td></tr><tr><td class="docs"><p>Parse '[property] is less than [value]'.</p> </td><td class="codes"><pre class="brush: clojure">(def re-number #&quot;^[0-9.]*$&quot;)</pre></td></tr><tr><td class="docs"><p>If this token appears to represent an explicit number, return that number;
otherwise, make a keyword of it and return that.</p>
</td><td class="codes"><pre class="brush: clojure">(defn keyword-or-numeric
[token]
(cond
(re-matches re-number token) (read-string token)
(keyword? token) token
true (keyword token)))</pre></td></tr><tr><td class="docs"><p>Generally all functions in this file with names beginning 'parse-' take a
sequence of tokens (and in some cases other optional arguments) and return a
vector comprising</p>
<h1>A code fragment parsed from the front of the sequence of tokens, and</h1>
<h1>the remaining tokens which were not consumed in constructing that sequence.</h1>
<p>In every case if the function cannot parse the desired construct from the
front of the sequence of tokens it returns nil.</p>
</td><td class="codes"></td></tr><tr><td class="docs"><p>Parse a number.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-numeric-value
[[value &amp; remainder]]
(if (re-matches re-number value) [(read-string value) remainder]))</pre></td></tr><tr><td class="docs"><p>Parse a token assumed to be the name of a property of the current cell,
whose value is assumed to be an integer.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-property-int
[[value &amp; remainder]]
(if value [(list 'get-int 'cell (keyword value)) remainder]))</pre></td></tr><tr><td class="docs"><p>Parse a token assumed to be the name of a property of the current cell.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-property-value
[[value &amp; remainder]]
(if value [(list (keyword value) 'cell) remainder]))</pre></td></tr><tr><td class="docs"><p>Parse a value from the first of these <code>tokens</code>. If <code>expect-int</code> is true, return
an integer or something which will evaluate to an integer.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-simple-value
([tokens expect-int]
(or
(parse-numeric-value tokens)
(cond expect-int
(parse-property-int tokens)
true (parse-property-value tokens))))
([tokens]
(parse-simple-value tokens false)))</pre></td></tr><tr><td class="docs"><p>Parse a list of values from among these <code>tokens</code>. If <code>expect-int</code> is true, return
an integer or something which will evaluate to an integer.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-disjunct-value
[[OR token &amp; tokens] expect-int]
(cond (member? OR '(&quot;or&quot; &quot;in&quot;))
(let [[others remainder] (parse-disjunct-value tokens expect-int)]
[(cons
(cond
expect-int (first (parse-simple-value (list token) true))
true (keyword token))
others)
remainder])
true [nil (cons OR (cons token tokens))]))</pre></td></tr><tr><td class="docs"><p>Parse a value from among these <code>tokens</code>. If <code>expect-int</code> is true, return
an integer or something which will evaluate to an integer.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-value
([tokens expect-int]
(or
(parse-disjunct-value tokens expect-int)
(parse-simple-value tokens)))
([tokens]
(parse-value tokens false)))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn parse-member-condition
[[property IN &amp; rest]]
(if (= IN &quot;in&quot;)
(let [[l remainder] (parse-disjunct-value (cons &quot;in&quot; rest) false)]
[(list 'member? (keyword property) l) remainder])))</pre></td></tr><tr><td class="docs"><p>Parse '[property] less than [value]'.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-less-condition </td><td class="codes"><pre class="brush: clojure">(defn parse-less-condition
[[property is less than value &amp; rest]] [[property LESS THAN value &amp; rest]]
(cond (and (member? is '(&quot;is&quot; &quot;are&quot;)) (= less &quot;less&quot;) (= than &quot;than&quot;)) (cond (and (= LESS &quot;less&quot;) (= THAN &quot;than&quot;))
[(list '&lt; (list 'get-int 'cell (keyword property)) (read-string value)) rest]))</pre></td></tr><tr><td class="docs"><p>Parse '[property] is more than [value]'.</p> [(list '&lt; (list 'get-int 'cell (keyword property)) (read-string value)) rest]))</pre></td></tr><tr><td class="docs"><p>Parse '[property] more than [value]'.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-more-condition </td><td class="codes"><pre class="brush: clojure">(defn parse-more-condition
[[property is more than value &amp; rest]] [[property MORE THAN value &amp; rest]]
(cond (and (member? is '(&quot;is&quot; &quot;are&quot;)) (= more &quot;more&quot;) (= than &quot;than&quot;)) (cond (and (= MORE &quot;more&quot;) (= THAN &quot;than&quot;))
[(list '&gt; (list 'get-int 'cell (keyword property)) (read-string value)) rest]))</pre></td></tr><tr><td class="docs"><p>Parse clauses of the form 'x is y', but not 'x is more than y' or 'x is less than y'. [(list '&gt; (list 'get-int 'cell (keyword property)) (read-string value)) rest]))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn parse-between-condition
[[p BETWEEN v1 AND v2 &amp; rest]]
(cond (and (= BETWEEN &quot;between&quot;) (= AND &quot;and&quot;) (not (nil? v2)))
(let [property (first (parse-simple-value (list p) true))
value1 (first (parse-simple-value (list v1) true))
value2 (first (parse-simple-value (list v2) true))]
[(list 'or
(list '&lt; value1 property value2)
(list '&gt; value1 property value2)) rest])))</pre></td></tr><tr><td class="docs"><p>Parse clauses of the form 'x is y', 'x is in y or z...',
'x is between y and z', 'x is more than y' or 'x is less than y'.
It is necessary to disambiguate whether value is a numeric or keyword.</p> It is necessary to disambiguate whether value is a numeric or keyword.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-is-condition </td><td class="codes"><pre class="brush: clojure">(defn parse-is-condition
[[property is value &amp; rest]] [[property IS value &amp; rest]]
(cond (and (member? is '(&quot;is&quot; &quot;are&quot;)) (cond
(not (member? value '(&quot;more&quot; &quot;less&quot; &quot;exactly&quot; &quot;not&quot;)))) (member? IS '(&quot;is&quot; &quot;are&quot;))
[(cond (let [tokens (cons property (cons value rest))]
(re-matches re-number value)(list '= (list 'get-int 'cell (keyword property)) (read-string value)) (cond
true (list '= (list (keyword property) 'cell) (keyword value))) (= value &quot;in&quot;) (parse-member-condition tokens)
rest]))</pre></td></tr><tr><td class="docs"><p>Parse the negation of a simple condition.</p> (= value &quot;between&quot;) (parse-between-condition tokens)
(= value &quot;more&quot;) (parse-more-condition tokens)
(= value &quot;less&quot;) (parse-less-condition tokens)
(re-matches re-number value) [(list '= (list 'get-int 'cell (keyword property)) (read-string value)) rest]
value [(list '= (list (keyword property) 'cell) (keyword value)) rest]))))</pre></td></tr><tr><td class="docs"><p>Parse the negation of a simple condition.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-not-condition </td><td class="codes"><pre class="brush: clojure">(defn parse-not-condition
[[property is not &amp; rest]] [[property IS NOT &amp; rest]]
(cond (and (member? is '(&quot;is&quot; &quot;are&quot;)) (= not &quot;not&quot;)) (cond (and (member? IS '(&quot;is&quot; &quot;are&quot;)) (= NOT &quot;not&quot;))
(let [partial (parse-simple-condition (cons property (cons is rest)))] (let [partial (parse-simple-condition (cons property (cons &quot;is&quot; rest)))]
(cond partial (cond partial
(let [[condition remainder] partial] (let [[condition remainder] partial]
[(list 'not condition) remainder])))))</pre></td></tr><tr><td class="docs"><p>Parse conditions of the form '[property] [comparison] [value]'.</p> [(list 'not condition) remainder])))))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn- gen-neighbours-condition
[comparator quantity property value remainder]
[(list comparator
(list 'count
(list 'get-neighbours-with-property-value 'world 'cell
(keyword property) (keyword-or-numeric value)))
quantity)
remainder])</pre></td></tr><tr><td class="docs"><p>Parse conditions of the form '...more than 6 neighbours are [condition]'</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-comparator-neighbours-condition
[[MORE THAN n NEIGHBOURS have-or-are &amp; rest]]
(let [quantity (first (parse-numeric-value (list n)))
comparator (cond (= MORE &quot;more&quot;) '&gt;
(member? MORE '(&quot;fewer&quot; &quot;less&quot;)) '&lt;)]
(cond
(and quantity
comparator
(= THAN &quot;than&quot;)
(= NEIGHBOURS &quot;neighbours&quot;))
(cond
(= have-or-are &quot;are&quot;)
(let [[value &amp; remainder] rest]
(gen-neighbours-condition comparator quantity :state value remainder))
(= have-or-are &quot;have&quot;)
(let [[property comp1 comp2 value &amp; remainder] rest]
(cond (and (= comp1 &quot;equal&quot;) (= comp2 &quot;to&quot;))
(gen-neighbours-condition comparator quantity property value remainder)
;; (and (= comp1 &quot;more&quot;) (= comp2 &quot;than&quot;))
;; (gen-neighbours-condition '&gt; quantity property value remainder)
;; (and (= comp1 &quot;less&quot;) (= comp2 &quot;than&quot;))
;; (gen-neighbours-condition '&lt; quantity property value remainder)))))))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn parse-some-neighbours-condition
[[SOME NEIGHBOURS &amp; rest]]
(cond
(and (= SOME &quot;some&quot;) (= NEIGHBOURS &quot;neighbours&quot;))
(parse-comparator-neighbours-condition (concat '(&quot;more&quot; &quot;than&quot; &quot;0&quot; &quot;neighbours&quot;) rest))))</pre></td></tr><tr><td class="docs"><p>Parse conditions of the form '...6 neighbours are condition'</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-simple-neighbours-condition
[[n NEIGHBOURS have-or-are &amp; rest]]
(let [quantity (first (parse-numeric-value (list n)))]
(cond
(and quantity (= NEIGHBOURS &quot;neighbours&quot;))
(cond
(= have-or-are &quot;are&quot;)
(let [[value &amp; remainder] rest]
(gen-neighbours-condition '= quantity :state value remainder))
(= have-or-are &quot;have&quot;)
(let [[property comp1 comp2 value &amp; remainder] rest]
(cond (and (= comp1 &quot;equal&quot;) (= comp2 &quot;to&quot;))
(gen-neighbours-condition '= quantity property value remainder)
;; (and (= comp1 &quot;more&quot;) (= comp2 &quot;than&quot;))
;; (gen-neighbours-condition '&gt; quantity property value remainder)
;; (and (= comp1 &quot;less&quot;) (= comp2 &quot;than&quot;))
;; (gen-neighbours-condition '&lt; quantity property value remainder)))))))</pre></td></tr><tr><td class="docs"><p>Parse conditions referring to neighbours</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-neighbours-condition
[tokens]
(or
(parse-simple-neighbours-condition tokens)
(parse-comparator-neighbours-condition tokens)
(parse-some-neighbours-condition tokens)))</pre></td></tr><tr><td class="docs"><p>Parse conditions of the form '[property] [comparison] [value]'.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-simple-condition </td><td class="codes"><pre class="brush: clojure">(defn parse-simple-condition
[tokens] [tokens]
(or (parse-is-condition tokens) (or
(parse-not-condition tokens) (parse-neighbours-condition tokens)
(parse-less-condition tokens) (parse-member-condition tokens)
(parse-more-condition tokens)))</pre></td></tr><tr><td class="docs"><p>Parse '... or [condition]' from <code>tokens</code>, where <code>left</code> is the already parsed first disjunct.</p> (parse-not-condition tokens)
(parse-is-condition tokens)
(parse-less-condition tokens)
(parse-more-condition tokens)))</pre></td></tr><tr><td class="docs"><p>Parse '... or [condition]' from <code>tokens</code>, where <code>left</code> is the already parsed first disjunct.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-disjunction-condition </td><td class="codes"><pre class="brush: clojure">(defn parse-disjunction-condition
[left tokens] [left tokens]
(let [partial (parse-conditions tokens)] (let [partial (parse-conditions tokens)]
(if (if partial
partial (let [[right remainder] partial]
(let [[right remainder] partial] [(list 'or left right) remainder]))))</pre></td></tr><tr><td class="docs"><p>Parse '... and [condition]' from <code>tokens</code>, where <code>left</code> is the already parsed first conjunct.</p>
[(list 'or left right) remainder]))))</pre></td></tr><tr><td class="docs"><p>Parse '... and [condition]' from <code>tokens</code>, where <code>left</code> is the already parsed first conjunct.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-conjunction-condition </td><td class="codes"><pre class="brush: clojure">(defn parse-conjunction-condition
[left tokens] [left tokens]
(let [partial (parse-conditions tokens)] (let [partial (parse-conditions tokens)]
(if partial (if partial
(let [[right remainder] partial] (let [[right remainder] partial]
[(list 'and left right) remainder]))))</pre></td></tr><tr><td class="docs"><p>Parse conditions from <code>tokens</code>, where conditions may be linked by either 'and' or 'or'.</p> [(list 'and left right) remainder]))))</pre></td></tr><tr><td class="docs"><p>Parse conditions from <code>tokens</code>, where conditions may be linked by either 'and' or 'or'.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-conditions </td><td class="codes"><pre class="brush: clojure">(defn parse-conditions
[tokens] [tokens]
(let [partial (parse-simple-condition tokens)] (let [partial (parse-simple-condition tokens)]
(if partial (if partial
(let [[left [next &amp; remainder]] partial] (let [[left [next &amp; remainder]] partial]
(cond (cond
(= next &quot;and&quot;) (parse-conjunction-condition left remainder) (= next &quot;and&quot;) (parse-conjunction-condition left remainder)
(= next &quot;or&quot;) (parse-disjunction-condition left remainder) (= next &quot;or&quot;) (parse-disjunction-condition left remainder)
true partial)))))</pre></td></tr><tr><td class="docs"><p>Parse the left hand side ('if...') of a production rule.</p> true partial)))))</pre></td></tr><tr><td class="docs"><p>Parse the left hand side ('if...') of a production rule.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-left-hand-side </td><td class="codes"><pre class="brush: clojure">(defn parse-left-hand-side
[tokens] [tokens]
(if (if
(= (first tokens) &quot;if&quot;) (= (first tokens) &quot;if&quot;)
(parse-conditions (rest tokens))))</pre></td></tr><tr><td class="docs"><p>Parse actions of the form '[property] should be [property] [arithmetic-operator] [value]', (parse-conditions (rest tokens))))</pre></td></tr><tr><td class="docs"><p>Parse actions of the form '[property] should be [property] [arithmetic-operator] [value]',
e.g. 'fertility should be fertility + 1', or 'deer should be deer - wolves'.</p> e.g. 'fertility should be fertility + 1', or 'deer should be deer - wolves'.</p>
@ -3121,27 +3265,41 @@ net.brehaut.ClojureTools = (function (SH) {
[(list 'merge (or previous 'cell) [(list 'merge (or previous 'cell)
{(keyword prop1) (list (symbol operator) (list 'get-int 'cell (keyword prop2)) {(keyword prop1) (list (symbol operator) (list 'get-int 'cell (keyword prop2))
(cond (cond
(re-matches re-number value) (read-string value) (re-matches re-number value) (read-string value)
true (list 'get-int 'cell (keyword value))))}) rest]))</pre></td></tr><tr><td class="docs"><p>Parse actions of the form '[property] should be [value].'</p> true (list 'get-int 'cell (keyword value))))}) rest]))</pre></td></tr><tr><td class="docs"><p>Parse actions of the form '[property] should be [value].'</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-set-action </td><td class="codes"><pre class="brush: clojure">(defn parse-set-action
[previous [property should be value &amp; rest]] [previous [property should be value &amp; rest]]
(if (and (= should &quot;should&quot;) (= be &quot;be&quot;)) (if (and (= should &quot;should&quot;) (= be &quot;be&quot;))
[(list 'merge (or previous 'cell) [(list 'merge (or previous 'cell)
{(keyword property) (cond (re-matches re-number value) (read-string value) true (keyword value))}) rest]))</pre></td></tr><tr><td class="docs"> {(keyword property) (cond (re-matches re-number value) (read-string value) true (keyword value))}) rest]))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn parse-simple-action [previous tokens] </td><td class="codes"><pre class="brush: clojure">(defn parse-simple-action [previous tokens]
(or (parse-arithmetic-action previous tokens) (or (parse-arithmetic-action previous tokens)
(parse-set-action previous tokens)))</pre></td></tr><tr><td class="docs"><p>Parse actions from tokens.</p> (parse-set-action previous tokens)))</pre></td></tr><tr><td class="docs"><p>Parse actions from tokens.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-actions </td><td class="codes"><pre class="brush: clojure">(defn parse-actions
[previous tokens] [previous tokens]
(let [[left remainder] (parse-simple-action previous tokens)] (let [[left remainder] (parse-simple-action previous tokens)]
(cond left (cond left
(cond (= (first remainder) &quot;and&quot;) (cond (= (first remainder) &quot;and&quot;)
(parse-actions left (rest remainder)) (parse-actions left (rest remainder))
true (list left)))))</pre></td></tr><tr><td class="docs"><p>Parse the right hand side ('then...') of a production rule.</p> true (list left)))))</pre></td></tr><tr><td class="docs"><p>Parse a probability of an action from this collection of tokens</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-probability
[previous [n CHANCE IN m &amp; tokens]]
(cond
(and (= CHANCE &quot;chance&quot;)(= IN &quot;in&quot;))
(let [[action remainder] (parse-actions previous tokens)]
(cond action
[(list 'cond
(list '&lt;
(list 'rand
(first (parse-simple-value (list m) true)))
(first (parse-simple-value (list n) true)))
action) remainder])))) </pre></td></tr><tr><td class="docs"><p>Parse the right hand side ('then...') of a production rule.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-right-hand-side </td><td class="codes"><pre class="brush: clojure">(defn parse-right-hand-side
[tokens] [[THEN &amp; tokens]]
(if (= (first tokens) &quot;then&quot;) (if (= THEN &quot;then&quot;)
(parse-actions nil (rest tokens))))</pre></td></tr><tr><td class="docs"><p>Parse a complete rule from this string or sequence of string tokens.</p> (or
(parse-probability nil tokens)
(parse-actions nil tokens))))</pre></td></tr><tr><td class="docs"><p>Parse a complete rule from this string or sequence of string tokens.</p>
</td><td class="codes"><pre class="brush: clojure">(defn parse-rule </td><td class="codes"><pre class="brush: clojure">(defn parse-rule
[line] [line]
(cond (cond
@ -3149,5 +3307,13 @@ net.brehaut.ClojureTools = (function (SH) {
true (let [[left remainder] (parse-left-hand-side line) true (let [[left remainder] (parse-left-hand-side line)
[right junk] (parse-right-hand-side remainder)] [right junk] (parse-right-hand-side remainder)]
;; there shouldn't be any junk (should be null) ;; there shouldn't be any junk (should be null)
(list 'fn ['cell 'world] (list 'if left right)))))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr></table><div class="footer">Generated by <a href="https://github.com/fogus/marginalia">Marginalia</a>.&nbsp;&nbsp;Syntax highlighting provided by Alex Gorbatchev's <a href="http://alexgorbatchev.com/SyntaxHighlighter/">SyntaxHighlighter</a></div><script type="text/javascript">SyntaxHighlighter.defaults['gutter'] = false; (list 'fn ['cell 'world] (list 'if left right)))))</pre></td></tr><tr><td class="docs"><p>Parse this <code>rule-text</code>, a string conforming to the grammar of MicroWorld rules,
into Clojure source, and then compile it into an anonymous
function object, getting round the problem of binding mw-engine.utils in
the compiling environment.</p>
</td><td class="codes"><pre class="brush: clojure">(defn compile-rule
[rule-text]
(do
(use 'mw-engine.utils)
(eval (parse-rule rule-text)))) </pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr></table><div class="footer">Generated by <a href="https://github.com/fogus/marginalia">Marginalia</a>.&nbsp;&nbsp;Syntax highlighting provided by Alex Gorbatchev's <a href="http://alexgorbatchev.com/SyntaxHighlighter/">SyntaxHighlighter</a></div><script type="text/javascript">SyntaxHighlighter.defaults['gutter'] = false;
SyntaxHighlighter.all()</script></body></html> SyntaxHighlighter.all()</script></body></html>

View file

@ -3133,12 +3133,14 @@ net.brehaut.ClojureTools = (function (SH) {
world, into a path which should recover the corresponding image file.</p> world, into a path which should recover the corresponding image file.</p>
</td><td class="codes"><pre class="brush: clojure">(defn format-image-path </td><td class="codes"><pre class="brush: clojure">(defn format-image-path
[statekey] [statekey]
(format &quot;img/tiles/%s.png&quot; (format-css-class statekey)))</pre></td></tr><tr><td class="docs"><p>Render this world cell as a Hiccup table cell.</p> (format &quot;img/tiles/%s.png&quot; (format-css-class statekey)))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn format-mouseover [cell]
(str &quot;State &quot; (:state cell) &quot;; altitude: &quot; (:altitude cell) &quot;; fertility: &quot; (:fertility cell)))</pre></td></tr><tr><td class="docs"><p>Render this world cell as a Hiccup table cell.</p>
</td><td class="codes"><pre class="brush: clojure">(defn render-cell </td><td class="codes"><pre class="brush: clojure">(defn render-cell
[cell] [cell]
(let [state (:state cell)] (let [state (:state cell)]
[:td {:class (format-css-class state)} [:td {:class (format-css-class state) :title (format-mouseover cell)}
[:img {:alt (world/format-cell cell) :img (format-image-path state)}]]))</pre></td></tr><tr><td class="docs"><p>Render this world row as a Hiccup table row.</p> [:img {:alt (world/format-cell cell) :src (format-image-path state)}]]))</pre></td></tr><tr><td class="docs"><p>Render this world row as a Hiccup table row.</p>
</td><td class="codes"><pre class="brush: clojure">(defn render-world-row </td><td class="codes"><pre class="brush: clojure">(defn render-world-row
[row] [row]
(apply vector (cons :tr (map render-cell row))))</pre></td></tr><tr><td class="docs"><p>Render the world implied by the session as a complete HTML page.</p> (apply vector (cons :tr (map render-cell row))))</pre></td></tr><tr><td class="docs"><p>Render the world implied by the session as a complete HTML page.</p>
@ -3152,14 +3154,15 @@ net.brehaut.ClojureTools = (function (SH) {
rules/init-rules)) rules/init-rules))
rules (or (session/get :rules) rules/natural-rules) rules (or (session/get :rules) rules/natural-rules)
generation (+ (or (session/get :generation) 0) 1) generation (+ (or (session/get :generation) 0) 1)
w2 (engine/transform-world world rules)] w2 (engine/transform-world world rules)
]
(session/put! :world w2) (session/put! :world w2)
(session/put! :generation generation) (session/put! :generation generation)
[:div {:class &quot;world&quot;} [:div {:class &quot;world&quot;}
[:p (str &quot;Generation &quot; generation)]
(apply vector (apply vector
(cons :table (cons :table
(map render-world-row w2)))]))</pre></td></tr><tr><td class="docs"><p>Render the world implied by the session as a complete HTML page.</p> (map render-world-row w2)))
[:p (str &quot;Generation &quot; generation)]]))</pre></td></tr><tr><td class="docs"><p>Render the world implied by the session as a complete HTML page.</p>
</td><td class="codes"><pre class="brush: clojure">(defn render-world </td><td class="codes"><pre class="brush: clojure">(defn render-world
[] []
(html (html
@ -3173,7 +3176,6 @@ net.brehaut.ClojureTools = (function (SH) {
[:link {:href &quot;css/states.css&quot; :type &quot;text/css&quot; :rel &quot;stylesheet&quot;}] [:link {:href &quot;css/states.css&quot; :type &quot;text/css&quot; :rel &quot;stylesheet&quot;}]
[:meta {:http-equiv &quot;refresh&quot; :content &quot;5&quot;}]] [:meta {:http-equiv &quot;refresh&quot; :content &quot;5&quot;}]]
[:body [:body
[:h1 &quot;MicroWorld&quot;]
(render-world-table) (render-world-table)
]]))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-ui.repl" name="mw-ui.repl"><h1 class="project-name">mw-ui.repl</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"> ]]))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-ui.repl" name="mw-ui.repl"><h1 class="project-name">mw-ui.repl</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-ui.repl </td><td class="codes"><pre class="brush: clojure">(ns mw-ui.repl
@ -3213,16 +3215,26 @@ net.brehaut.ClojureTools = (function (SH) {
[mw-ui.render-world :as world] [mw-ui.render-world :as world]
[noir.session :as session]))</pre></td></tr><tr><td class="docs"> [noir.session :as session]))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn home-page [] </td><td class="codes"><pre class="brush: clojure">(defn home-page []
(layout/render (layout/render &quot;world.html&quot; {:title &quot;Watch your world grow&quot;
&quot;home.html&quot; {:title &quot;Welcome to MicroWorld&quot; :content (util/md-&gt;html &quot;/md/docs.md&quot;)}))</pre></td></tr><tr><td class="docs"> :content (html (world/render-world-table))
:seconds (or (session/get :seconds) 5)
:maybe-refresh &quot;refresh&quot;}))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn about-page [] </td><td class="codes"><pre class="brush: clojure">(defn about-page []
(layout/render &quot;about.html&quot; {:title &quot;About MicroWorld&quot; :content (util/md-&gt;html &quot;/md/about.md&quot;)}))</pre></td></tr><tr><td class="docs"> (layout/render &quot;about.html&quot; {:title &quot;About MicroWorld&quot; :content (util/md-&gt;html &quot;/md/about.md&quot;)}))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn world-page [] </td><td class="codes"><pre class="brush: clojure">(defn list-states []
(layout/render &quot;world.html&quot; {:title &quot;Watch your world grow&quot; :content (html (world/render-world-table)) :seconds (or (session/get :seconds) 5) :maybe-refresh &quot;refresh&quot;}))</pre></td></tr><tr><td class="docs"> (sort
(filter #(not (nil? %))
(map #(first (rest (re-matches #&quot;([0-9a-z-]+).png&quot; (.getName %))))
(file-seq (clojure.java.io/file &quot;resources/public/img/tiles&quot;))))))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn docs-page []
(layout/render &quot;docs.html&quot; {:title &quot;Documentation&quot;
:parser (util/md-&gt;html &quot;/md/parser.md&quot;)
:states (list-states)
:components [&quot;mw-engine&quot; &quot;mw-parser&quot; &quot;mw-ui&quot;]}))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defroutes home-routes </td><td class="codes"><pre class="brush: clojure">(defroutes home-routes
(GET &quot;/&quot; [] (home-page)) (GET &quot;/&quot; [] (home-page))
(GET &quot;/about&quot; [] (about-page)) (GET &quot;/about&quot; [] (about-page))
(GET &quot;/world&quot; [] (world-page)))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-ui.util" name="mw-ui.util"><h1 class="project-name">mw-ui.util</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"> (GET &quot;/docs&quot; [] (docs-page)))</pre></td></tr><tr><td class="spacer docs">&nbsp;</td><td class="codes" /></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-ui.util" name="mw-ui.util"><h1 class="project-name">mw-ui.util</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-ui.util </td><td class="codes"><pre class="brush: clojure">(ns mw-ui.util
(:require [noir.io :as io] (:require [noir.io :as io]
[markdown.core :as md]))</pre></td></tr><tr><td class="docs"><p>reads a markdown file from public/md and returns an HTML string</p> [markdown.core :as md]))</pre></td></tr><tr><td class="docs"><p>reads a markdown file from public/md and returns an HTML string</p>