Gone back (hopefully temporarily) to using collage load-image function

while I try to sort out the resources issue. This isn't very satisfactory,
as the collage distribution is compiled using Java 7, which is going to
break for some people, and because I'm bloating my code with two separate
image libraries.
This commit is contained in:
Simon Brooke 2014-07-30 12:38:13 +01:00
parent 8e5b526c14
commit 7c0a64a6a9
4 changed files with 11 additions and 561 deletions

View file

@ -3029,548 +3029,5 @@ net.brehaut.ClojureTools = (function (SH) {
}; };
})(SyntaxHighlighter); })(SyntaxHighlighter);
</script><title>mw-engine -- Marginalia</title></head><body><table><tr><td class="docs"><div class="header"><h1 class="project-name">mw-engine</h1><h2 class="project-version">0.1.2-SNAPSHOT</h2><br /><p>Cellular automaton world builder.</p> </script><title>mw-engine -- Marginalia</title></head><body><table><tr><td class="docs"><div class="header"><h1 class="project-name">mw-engine</h1><h2 class="project-version">0.1.2-SNAPSHOT</h2><br /><p>Cellular automaton world builder.</p>
</div><div class="dependencies"><h3>dependencies</h3><table><tr><td class="dep-name">org.clojure/clojure</td><td class="dotted"><hr /></td><td class="dep-version">1.5.1</td></tr><tr><td class="dep-name">org.clojure/math.combinatorics</td><td class="dotted"><hr /></td><td class="dep-version">0.0.7</td></tr><tr><td class="dep-name">org.clojure/tools.trace</td><td class="dotted"><hr /></td><td class="dep-version">0.7.8</td></tr><tr><td class="dep-name">net.mikera/imagez</td><td class="dotted"><hr /></td><td class="dep-version">0.3.1</td></tr></table></div></td><td class="codes" style="text-align: center; vertical-align: middle;color: #666;padding-right:20px"><br /><br /><br />(this space intentionally left almost blank)</td></tr><tr><td class="docs"><div class="toc"><a name="toc"><h3>namespaces</h3></a><ul><li><a href="#mw-engine.core">mw-engine.core</a></li><li><a href="#mw-engine.heightmap">mw-engine.heightmap</a></li><li><a href="#mw-engine.manifest">mw-engine.manifest</a></li><li><a href="#mw-engine.natural-rules">mw-engine.natural-rules</a></li><li><a href="#mw-engine.utils">mw-engine.utils</a></li><li><a href="#mw-engine.version">mw-engine.version</a></li><li><a href="#mw-engine.world">mw-engine.world</a></li></ul></div></td><td class="codes">&nbsp;</td></tr><tr><td class="docs"><div class="docs-header"><a class="anchor" href="#mw-engine.core" name="mw-engine.core"><h1 class="project-name">mw-engine.core</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>Functions to transform a world and run rules.</p> </div><div class="dependencies"><h3>dependencies</h3><table><tr><td class="dep-name">org.clojure/clojure</td><td class="dotted"><hr /></td><td class="dep-version">1.5.1</td></tr><tr><td class="dep-name">org.clojure/math.combinatorics</td><td class="dotted"><hr /></td><td class="dep-version">0.0.7</td></tr><tr><td class="dep-name">org.clojure/tools.trace</td><td class="dotted"><hr /></td><td class="dep-version">0.7.8</td></tr><tr><td class="dep-name">net.mikera/imagez</td><td class="dotted"><hr /></td><td class="dep-version">0.3.1</td></tr></table></div></td><td class="codes" style="text-align: center; vertical-align: middle;color: #666;padding-right:20px"><br /><br /><br />(this space intentionally left almost blank)</td></tr><tr><td class="docs"><div class="toc"><a name="toc"><h3>namespaces</h3></a><ul></ul></div></td><td class="codes">&nbsp;</td></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;
</td><td class="codes"></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-engine.core
(:use mw-engine.utils)
(:require [mw-engine.world :as world]))</pre></td></tr><tr><td class="docs"><p>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.</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;
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.</p>
<p>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 <code>world.clj</code>.</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>Apply a single <code>rule</code> to a <code>cell</code>. 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. <code>world</code> is also passed in in order to be able
to access neighbours.</p>
</td><td class="codes"><pre class="brush: clojure">(defn apply-rule
([world cell rule]
(cond
(ifn? rule) (apply-rule cell world rule nil)
(seq? rule) (let [[afn src] rule] (apply-rule cell world afn src))))
([cell world rule source]
(let [result (apply rule (list cell world))]
(cond
(and result source) (merge result {:rule source})
true result))))</pre></td></tr><tr><td class="docs"><p>Derive a cell from this <code>cell</code> of this <code>world</code> by applying these <code>rules</code>.</p>
</td><td class="codes"><pre class="brush: clojure">(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))))))</pre></td></tr><tr><td class="docs"><p>Derive a cell from this <code>cell</code> of this <code>world</code> by applying these <code>rules</code>. If an
exception is thrown, cache its message on the cell and set it's state to error</p>
</td><td class="codes"><pre class="brush: clojure">(defn- transform-cell
[world cell rules]
(try
(merge
(apply-rules world cell rules)
{:generation (+ (or (:generation cell) 0) 1)})
(catch Exception e
(merge cell {:error
(format &quot;%s at generation %d when in state %s&quot;
(.getMessage e)
(:generation cell)
(:state cell))
:state :error}))))</pre></td></tr><tr><td class="docs"><p>Return a world derived from this <code>world</code> by applying these <code>rules</code> to each cell.</p>
</td><td class="codes"><pre class="brush: clojure">(defn transform-world
[world rules]
(map-world world transform-cell (list rules)))</pre></td></tr><tr><td class="docs"><p>Consider this single argument as a map of <code>:world</code> and <code>:rules</code>; apply the rules
to transform the world, and return a map of the new, transformed <code>:world</code> and
these <code>:rules</code>. As a side effect, print the world.</p>
</td><td class="codes"><pre class="brush: clojure">(defn- transform-world-state
[state]
(let [world (transform-world (:world state) (:rules state))]
(world/print-world world)
{:world world :rules (:rules state)}))</pre></td></tr><tr><td class="docs"><p>Run this world with these rules for this number of generations.</p>
<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.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn run-world
[world init-rules rules generations]
(let [state {:world (transform-world world init-rules) :rules rules}]
(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>
<p>Heightmaps are considered only as greyscale images, so colour is redundent (will be
ignored). Darker shades are higher.</p>
</td><td class="codes"></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-engine.heightmap
(:import [java.awt.image BufferedImage])
(:use mw-engine.utils
mw-engine.world
[mikera.image.core :only [load-image filter-image get-pixels]]
[mikera.image.filters]))</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,
and negative integers onto natural numbers. Also maps negative real numbers
onto positive real numbers.</p>
<ul>
<li><code>n</code> a number, on the set of real numbers.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn- abs
[n]
(cond (&lt; n 0) (- 0 n) true n))</pre></td></tr><tr><td class="docs"><p>Set the <code>gradient</code> property of this <code>cell</code> of this <code>world</code> to the difference in
altitude between its highest and lowest neghbours.</p>
</td><td class="codes"><pre class="brush: clojure">(defn tag-gradient
[world cell]
(let [heights (remove nil? (map #(:altitude %) (get-neighbours world cell)))
highest (cond (empty? heights) 0 ;; shouldn't happen
true (apply max heights))
lowest (cond (empty? heights) 0 ;; shouldn't
true (apply min heights))
gradient (- highest lowest)]
(merge cell {:gradient gradient})))</pre></td></tr><tr><td class="docs"><p>Set the <code>gradient</code> property of each cell in this <code>world</code> to the difference in
altitude between its highest and lowest neghbours.</p>
</td><td class="codes"><pre class="brush: clojure">(defn tag-gradients
[world]
(map-world world tag-gradient))</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.</p>
<ul>
<li><code>world</code> not actually used, but present to enable this function to be
passed as an argument to <code>mw-engine.utils/map-world</code>, q.v.</li>
<li><code>cell</code> a cell, as discussed in world.clj, q.v. Alternatively, a map;</li>
<li><code>heightmap</code> an (ideally) greyscale image, whose x and y dimensions should
exceed those of the world of which the <code>cell</code> forms part.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn tag-altitude
([world cell heightmap]
(tag-altitude cell heightmap))
([cell heightmap]
(merge cell
{:altitude
(+ (get-int cell :altitude)
(- 256
(abs
(mod
(.getRGB heightmap
(get-int cell :x)
(get-int cell :y)) 256))))})))</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
the heightmap is at least as large in x and y dimensions as the world.</p>
<ul>
<li><code>world</code> a world, as defined in <code>world.clj</code>, q.v.; if world is not supplied,
a world the size of the heightmap will be created.</li>
<li><code>imagepath</code> a file path or URL which indicates an image file.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn apply-heightmap
([world imagepath]
(let [heightmap (filter-image (grayscale)(load-image imagepath))]
(map-world
(map-world world tag-altitude (list heightmap))
tag-gradient)))
([imagepath]
(let [heightmap (filter-image (grayscale)(load-image imagepath))
world (make-world (.getWidth heightmap) (.getHeight heightmap))]
(map-world
(map-world world tag-altitude (list heightmap))
tag-gradient))))</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.manifest" name="mw-engine.manifest"><h1 class="project-name">mw-engine.manifest</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>manifest.clj: a mechanism </p>
</td><td class="codes"><pre class="brush: clojure">(ns mw-engine.manifest)</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(def manifest {
:build-signature {
:build-signture-version &quot;0.1.2-SNAPSHOT&quot;
:build-signature-user &quot;unset&quot;
:build-signature-email &quot;unset&quot;
:build-signature-timestamp &quot;unset&quot;
;; NOTE: it's important that the closing brace not be on the same
;; line as the build-signature fields, because of how the buildall
;; script constructs the build signature.
}
})</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>
<p>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.</p>
</td><td class="codes"></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-engine.natural-rules
(:use mw-engine.utils
mw-engine.world))</pre></td></tr><tr><td class="docs"><p>treeline at arbitrary altitude.</p>
</td><td class="codes"><pre class="brush: clojure">(def treeline 150)</pre></td></tr><tr><td class="docs"><p>waterline also at arbitrary altitude.</p>
</td><td class="codes"><pre class="brush: clojure">(def waterline 10)</pre></td></tr><tr><td class="docs"><p>and finally snowline is also arbitrary.</p>
</td><td class="codes"><pre class="brush: clojure">(def snowline 200)</pre></td></tr><tr><td class="docs"><p>Rare chance of lightning strikes</p>
</td><td class="codes"><pre class="brush: clojure">(def lightning-probability 500)</pre></td></tr><tr><td class="docs"><p>rules describing vegetation</p>
</td><td class="codes"><pre class="brush: clojure">(def vegetation-rules
(list
;; Randomly, birds plant tree seeds into grassland.
(fn [cell world] (cond (and (= (:state cell) :grassland)(&lt; (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...
(&lt; (+ (get-int cell :deer)(get-int cell :sheep)) 6)
(&lt; (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)
(&gt; (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)(&lt; (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)
(&lt; (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)})))))</pre></td></tr><tr><td class="docs"><p>rules describing herbivore behaviour</p>
</td><td class="codes"><pre class="brush: clojure">(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 (&gt; (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 (&lt; (count (get-neighbours world cell)) 8)
(&lt; (rand 50) 1)
(&gt; (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
(&gt; (get-int cell :fertility) 0)
(= (get-int cell :deer) 0)
(&gt;= n 2))
(merge cell {:deer (int (/ n 2))}))))
;; deer breed.
(fn [cell world]
(cond
(&gt;= (get-int cell :deer) 2)
(merge cell {:deer (int (* (:deer cell) 2))})))))</pre></td></tr><tr><td class="docs"><p>rules describing predator behaviour</p>
</td><td class="codes"><pre class="brush: clojure"> (def predator-rules
(list
;; wolves eat deer
(fn [cell world]
(cond
(&gt;= (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 (&gt; (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 (&gt; (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 (&lt; (count (get-neighbours world cell)) 8)
(&lt; (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)
(&gt;= n 2))
(merge cell {:wolves 2}))))
;; wolves breed.
(fn [cell world]
(cond
(&gt;= (get-int cell :wolves) 2)
(merge cell {:wolves (int (* (:wolves cell) 2))})))))</pre></td></tr><tr><td class="docs"><p>rules which initialise the world</p>
</td><td class="codes"><pre class="brush: clojure"> (def init-rules
(list
;; below the waterline, we have water.
(fn [cell world]
(cond (and (= (:state cell) :new) (&lt; (get-int cell :altitude) waterline)) (merge cell {:state :water})))
;; above the snowline, we have snow.
(fn [cell world]
(cond (and (= (:state cell) :new) (&gt; (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})))))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(def natural-rules (flatten
(list
vegetation-rules
herbivore-rules
;; predator-rules)))</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.utils" name="mw-engine.utils"><h1 class="project-name">mw-engine.utils</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"><p>Utility functions needed by MicroWorld and, specifically, in the interpretation of MicroWorld rule.</p>
</td><td class="codes"></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-engine.utils
(:require [clojure.math.combinatorics :as combo]))</pre></td></tr><tr><td class="docs"><p>True if elt is a member of col.</p>
</td><td class="codes"><pre class="brush: clojure">(defn member?
[elt col] (some #(= elt %) col))</pre></td></tr><tr><td class="docs"><p>True if x, y are in bounds for this world (i.e., there is a cell at x, y)
else false.</p>
<ul>
<li><code>world</code> a world as defined above;</li>
<li><code>x</code> a number which may or may not be a valid x coordinate within that world;</li>
<li><code>y</code> a number which may or may not be a valid y coordinate within that world.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn in-bounds
[world x y]
(and (&gt;= x 0)(&gt;= y 0)(&lt; y (count world))(&lt; x (count (first world)))))</pre></td></tr><tr><td class="docs"><p>Apply this <code>function</code> to each cell in this <code>world</code> to produce a new world.
the arguments to the function will be the world, the cell, and any
<code>additional-args</code> supplied</p>
</td><td class="codes"><pre class="brush: clojure">(defn map-world
([world function]
(map-world world function nil))
([world function additional-args]
(apply vector ;; vectors are more efficient for scanning, which we do a lot.
(for [row world]
(apply vector
(map #(apply function (cons world (cons % additional-args)))
row))))))</pre></td></tr><tr><td class="docs"><p>Return the cell a x, y in this world, if any.</p>
<ul>
<li><code>world</code> a world as defined above;</li>
<li><code>x</code> a number which may or may not be a valid x coordinate within that world;</li>
<li><code>y</code> a number which may or may not be a valid y coordinate within that world.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn get-cell
[world x y]
(cond (in-bounds world x y)
(nth (nth world y) x)))</pre></td></tr><tr><td class="docs"><p>Get the value of a property expected to be an integer from a map; if not present (or not an integer) return 0.</p>
<ul>
<li><code>map</code> a map;</li>
<li><code>key</code> a symbol or keyword, presumed to be a key into the <code>map</code>.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn get-int
[map key]
(cond (map? map)
(let [v (map key)]
(cond (and v (integer? v)) v
true 0))
true (throw (Exception. &quot;No map passed?&quot;))))</pre></td></tr><tr><td class="docs"><p>Return the population of this species in this cell. Currently a synonym for
<code>get-int</code>, but may not always be (depending whether species are later
implemented as actors)</p>
<ul>
<li><code>cell</code> a map;</li>
<li><code>species</code> a keyword representing a species which may populate that cell.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn population
[cell species]
(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
([world x y depth]
(remove nil?
(map #(get-cell world (first %) (first (rest %)))
(remove #(= % (list x y))
(combo/cartesian-product
(range (- x depth) (+ x depth 1))
(range (- y depth) (+ y depth 1)))))))
([world cell depth]
&quot;Get the neighbours to distance depth of this cell in this world.
* `world` a world, as described in world.clj;
* `cell` a cell within that world;
* `depth` an integer representing the distance from [x,y] that
should be searched.&quot;
(get-neighbours world (:x cell) (:y cell) depth))
([world cell]
&quot;Get the immediate neighbours of this cell in this world
* `world` a world, as described in world.clj;
* `cell` a cell within that world.&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
have this value for this property.</p>
<pre><code>* `world` a world, as described in `world.clj`;
* `cell` a cell within that world;
* `depth` an integer representing the distance from [x,y] that
should be searched;
* `property` a keyword representing a property of the neighbours;
* `value` a value of that property (or, possibly, the name of another);
* `op` a comparator function to use in place of `=`.
</code></pre>
<p> It gets messy.</p>
</td><td class="codes"><pre class="brush: clojure">(defn get-neighbours-with-property-value
([world x y depth property value op]
(filter
#(eval
(list op
(or (get % property) (get-int % property))
value))
(get-neighbours world x y depth)))
([world x y depth property value]
(get-neighbours-with-property-value world x y depth property value =))
([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>
<pre><code>* `world` a world, as described in `world.clj`;
* `cell` a cell within that world;
* `depth` an integer representing the distance from [x,y] that
should be searched;
* `state` a keyword representing a state in the world.
</code></pre>
</td><td class="codes"><pre class="brush: clojure">(defn get-neighbours-with-state
([world x y depth state]
(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="docs"><p>If this <code>cell</code>s x and y properties are equal to these <code>x</code> and <code>y</code> values,
return a cell like this cell but with the value of this <code>property</code> set to
this <code>value</code>. Otherwise, just return this <code>cell</code>.</p>
</td><td class="codes"><pre class="brush: clojure">(defn- set-cell-property
[cell x y property value]
(cond
(and (= x (:x cell)) (= y (:y cell)))
(merge cell {property value :rule &quot;Set by user&quot;})
true
cell))</pre></td></tr><tr><td class="docs"><p>Return a world like this <code>world</code> but with the value of exactly one <code>property</code>
of one <code>cell</code> changed to this <code>value</code></p>
</td><td class="codes"><pre class="brush: clojure">(defn set-property
([world cell property value]
(set-property world (:x cell) (:y cell) property value))
([world x y property value]
(apply
vector ;; we want a vector of vectors, not a list of lists, for efficiency
(map
(fn [row]
(apply
vector
(map #(set-cell-property % x y property value)
row)))
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.version" name="mw-engine.version"><h1 class="project-name">mw-engine.version</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-engine.version
(:gen-class))</pre></td></tr><tr><td class="docs"><p>Get the implementation version from the package of this namespace, which must
be compiled into a class (see clojure.java.interop)</p>
</td><td class="codes"><pre class="brush: clojure">(defn get-implementation-version
[namespace-class]
(.getImplementationVersion (.getPackage namespace-class)))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn -main []
(get-implementation-version (eval 'mw-engine.version)))</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
initial state, :new.</p>
<p>A cell is a map containing at least values for the keys :x, :y, and :state.</p>
<p>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.</p>
</td><td class="codes"></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(ns mw-engine.world
(:use mw-engine.utils))</pre></td></tr><tr><td class="docs"><p>Create a minimal default cell at x, y</p>
<ul>
<li><code>x</code> the x coordinate at which this cell is created;</li>
<li><code>y</code> the y coordinate at which this cell is created.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn- make-cell
[x y]
{:x x :y y :state :new})</pre></td></tr><tr><td class="docs"><p>Make the (remaining) cells in a row at this height in a world of this width.</p>
<ul>
<li><code>index</code> x coordinate of the next cell to be created;</li>
<li><code>width</code> total width of the matrix, in cells;</li>
<li><code>height</code> y coordinate of the next cell to be created.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn- make-world-row
[index width height]
(cond (= index width) nil
true (cons (make-cell index height)
(make-world-row (+ index 1) width height))))</pre></td></tr><tr><td class="docs">
</td><td class="codes"><pre class="brush: clojure">(defn- make-world-rows [index width height]
&quot;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.&quot;
(cond (= index height) nil
true (cons (apply vector (make-world-row 0 width index))
(make-world-rows (+ index 1) width height))))</pre></td></tr><tr><td class="docs"><p>Make a world width cells from east to west, and height cells from north to
south.</p>
<ul>
<li><code>width</code> a natural number representing the width of the matrix to be created;</li>
<li><code>height</code> a natural number representing the height of the matrix to be created.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn make-world
[width height]
(apply vector (make-world-rows 0 width height)))</pre></td></tr><tr><td class="docs"><p>Truncate the print name of the state of this cell to at most limit characters.</p>
</td><td class="codes"><pre class="brush: clojure">(defn truncate-state
[cell limit]
(let [s (:state cell)]
(cond (&gt; (count (.toString s)) 10) (subs s 0 10)
true s)))</pre></td></tr><tr><td class="docs"><p>Return a formatted string summarising the current state of this cell.</p>
</td><td class="codes"><pre class="brush: clojure">(defn format-cell
[cell]
(format &quot;%10s(%2d/%2d)&quot;
(truncate-state cell 10)
(population cell :deer)
(population cell :wolves)))</pre></td></tr><tr><td class="docs"><p>Format one row in the state of a world for printing.</p>
</td><td class="codes"><pre class="brush: clojure">(defn- format-world-row
[row]
(apply str
(map format-cell row)))</pre></td></tr><tr><td class="docs"><p>Print the current state of this world, and return nil.</p>
<ul>
<li><code>world</code> a world as defined above.</li>
</ul>
</td><td class="codes"><pre class="brush: clojure">(defn print-world
[world]
(println)
(dorun
(map
#(println
(format-world-row %))
world))
nil)</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

@ -3120,10 +3120,10 @@ net.brehaut.ClojureTools = (function (SH) {
(concat (when (env :dev) development-middleware) (concat (when (env :dev) development-middleware)
production-middleware))</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.render-world" name="mw-ui.render-world"><h1 class="project-name">mw-ui.render-world</h1><a class="toc-link" href="#toc">toc</a></a></div></td><td class="codes" /></tr><tr><td class="docs"> production-middleware))</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.render-world" name="mw-ui.render-world"><h1 class="project-name">mw-ui.render-world</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.render-world </td><td class="codes"><pre class="brush: clojure">(ns mw-ui.render-world
(:require [mw-engine.core :as engine] (:require [clojure.java.io :as jio]
[mw-engine.core :as engine]
[mw-engine.world :as world] [mw-engine.world :as world]
[mw-engine.heightmap :as heightmap] [mw-engine.heightmap :as heightmap]
[mw-engine.natural-rules :as rules]
[mw-parser.bulk :as compiler] [mw-parser.bulk :as compiler]
[hiccup.core :refer [html]] [hiccup.core :refer [html]]
[noir.io :as io] [noir.io :as io]
@ -3150,11 +3150,8 @@ net.brehaut.ClojureTools = (function (SH) {
</td><td class="codes"><pre class="brush: clojure">(defn render-world-table </td><td class="codes"><pre class="brush: clojure">(defn render-world-table
[] []
(let [world (or (session/get :world) (let [world (or (session/get :world)
(engine/transform-world (heightmap/apply-heightmap
(heightmap/apply-heightmap (io/get-resource &quot;/img/heightmaps/small_hill.png&quot;)))
(io/get-resource &quot;/img/heightmaps/small_hill.png&quot;)
;; &quot;resources/public/img/heightmaps/great_britain_and_ireland_small.png&quot;)
rules/init-rules))
rules (or (session/get :rules) rules (or (session/get :rules)
(do (session/put! :rules (do (session/put! :rules
(compiler/compile-file (compiler/compile-file
@ -3324,7 +3321,7 @@ net.brehaut.ClojureTools = (function (SH) {
(if (not (= map &quot;&quot;)) (if (not (= map &quot;&quot;))
(session/put! :world (session/put! :world
(heightmap/apply-heightmap (heightmap/apply-heightmap
(io/get-resource (str &quot;/img/heightmaps/&quot; map &quot;.png&quot;))))) (as-file (str &quot;/img/heightmaps/&quot; map &quot;.png&quot;)))))
(if (not (= rulefile &quot;&quot;)) (if (not (= rulefile &quot;&quot;))
(do (do
(session/put! :rule-text (io/slurp-resource rulepath)) (session/put! :rule-text (io/slurp-resource rulepath))

View file

@ -1,8 +1,8 @@
(ns mw-ui.render-world (ns mw-ui.render-world
(:require [mw-engine.core :as engine] (:require [clojure.java.io :as jio]
[mw-engine.core :as engine]
[mw-engine.world :as world] [mw-engine.world :as world]
[mw-engine.heightmap :as heightmap] [mw-engine.heightmap :as heightmap]
[mw-engine.natural-rules :as rules]
[mw-parser.bulk :as compiler] [mw-parser.bulk :as compiler]
[hiccup.core :refer [html]] [hiccup.core :refer [html]]
[noir.io :as io] [noir.io :as io]
@ -40,12 +40,8 @@
"Render the world implied by the current session as a complete HTML table in a DIV." "Render the world implied by the current session as a complete HTML table in a DIV."
[] []
(let [world (or (session/get :world) (let [world (or (session/get :world)
(engine/transform-world (heightmap/apply-heightmap
(heightmap/apply-heightmap (io/get-resource "/img/heightmaps/small_hill.png")))
(io/get-resource "/img/heightmaps/small_hill.png")
;; "resources/public/img/heightmaps/great_britain_and_ireland_small.png"
)
rules/init-rules))
rules (or (session/get :rules) rules (or (session/get :rules)
(do (session/put! :rules (do (session/put! :rules
(compiler/compile-file (compiler/compile-file

View file

@ -34,7 +34,7 @@
(if (not (= map "")) (if (not (= map ""))
(session/put! :world (session/put! :world
(heightmap/apply-heightmap (heightmap/apply-heightmap
(io/get-resource (str "/img/heightmaps/" map ".png"))))) (as-file (str "/img/heightmaps/" map ".png")))))
(if (not (= rulefile "")) (if (not (= rulefile ""))
(do (do
(session/put! :rule-text (io/slurp-resource rulepath)) (session/put! :rule-text (io/slurp-resource rulepath))