Upversioning whole system to 0.2.0, for flow feature
This commit is contained in:
parent
8b3639edd5
commit
4f35557b38
|
@ -1,4 +1,4 @@
|
||||||
(defproject mw-engine "0.1.6-SNAPSHOT"
|
(defproject mw-engine "0.2.0-SNAPSHOT"
|
||||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||||
[org.clojure/clojurescript "1.11.60" :scope "provided"]
|
[org.clojure/clojurescript "1.11.60" :scope "provided"]
|
||||||
[org.clojure/math.combinatorics "0.2.0"]
|
[org.clojure/math.combinatorics "0.2.0"]
|
||||||
|
|
|
@ -1,6 +1,27 @@
|
||||||
(ns ^{:doc "Functions to transform a world and run rules."
|
(ns ^{:doc "Functions to transform a world and run rules.
|
||||||
|
|
||||||
|
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 to that cell."
|
||||||
:author "Simon Brooke"}
|
:author "Simon Brooke"}
|
||||||
mw-engine.core
|
mw-engine.core
|
||||||
(:require [mw-engine.utils :refer [get-int-or-zero map-world]]
|
(:require [mw-engine.utils :refer [get-int-or-zero map-world]]
|
||||||
[taoensso.timbre :as l]))
|
[taoensso.timbre :as l]))
|
||||||
|
|
||||||
|
@ -26,29 +47,6 @@
|
||||||
;;;; Copyright (C) 2014 Simon Brooke
|
;;;; 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.
|
|
||||||
;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(defn apply-rule
|
(defn apply-rule
|
||||||
"Apply a single `rule` to a `cell`. What this is about is that I want to be able,
|
"Apply a single `rule` to a `cell`. What this is about is that I want to be able,
|
||||||
|
@ -62,19 +60,18 @@
|
||||||
(ifn? rule) (apply-rule world cell rule nil)
|
(ifn? rule) (apply-rule world cell rule nil)
|
||||||
(seq? rule) (let [[afn src] rule] (apply-rule world cell afn src))))
|
(seq? rule) (let [[afn src] rule] (apply-rule world cell afn src))))
|
||||||
([world cell rule source]
|
([world cell rule source]
|
||||||
(let [result (apply rule (list cell world))]
|
(let [result (apply rule (list cell world))]
|
||||||
(cond
|
(cond
|
||||||
(and result source) (merge result {:rule source})
|
(and result source) (merge result {:rule source})
|
||||||
:else result))))
|
:else result))))
|
||||||
|
|
||||||
(defn- apply-rules
|
(defn- apply-rules
|
||||||
"Derive a cell from this `cell` of this `world` by applying these `rules`."
|
"Derive a cell from this `cell` of this `world` by applying these `rules`."
|
||||||
[world cell rules]
|
[world cell rules]
|
||||||
(cond (empty? rules) cell
|
(cond (empty? rules) cell
|
||||||
:else (let [result (apply-rule world cell (first rules))]
|
:else (let [result (apply-rule world cell (first rules))]
|
||||||
(cond result result
|
(cond result result
|
||||||
:else (apply-rules world cell (rest rules))))))
|
:else (apply-rules world cell (rest rules))))))
|
||||||
|
|
||||||
|
|
||||||
(defn- transform-cell
|
(defn- transform-cell
|
||||||
"Derive a cell from this `cell` of this `world` by applying these `rules`. If an
|
"Derive a cell from this `cell` of this `world` by applying these `rules`. If an
|
||||||
|
@ -82,8 +79,8 @@
|
||||||
[world cell rules]
|
[world cell rules]
|
||||||
(try
|
(try
|
||||||
(merge
|
(merge
|
||||||
(apply-rules world cell rules)
|
(apply-rules world cell rules)
|
||||||
{:generation (+ (get-int-or-zero cell :generation) 1)})
|
{:generation (+ (get-int-or-zero cell :generation) 1)})
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(merge cell {:error
|
(merge cell {:error
|
||||||
(format "%s at generation %d when in state %s"
|
(format "%s at generation %d when in state %s"
|
||||||
|
@ -96,7 +93,7 @@
|
||||||
(defn transform-world
|
(defn transform-world
|
||||||
"Return a world derived from this `world` by applying these `rules` to each cell."
|
"Return a world derived from this `world` by applying these `rules` to each cell."
|
||||||
([world rules]
|
([world rules]
|
||||||
(map-world world transform-cell (list rules))))
|
(map-world world transform-cell (list rules))))
|
||||||
|
|
||||||
(defn run-world
|
(defn run-world
|
||||||
"Run this world with these rules for this number of generations.
|
"Run this world with these rules for this number of generations.
|
||||||
|
@ -111,8 +108,8 @@
|
||||||
(reduce (fn [world iteration]
|
(reduce (fn [world iteration]
|
||||||
(l/info "Running iteration " iteration)
|
(l/info "Running iteration " iteration)
|
||||||
(transform-world world rules))
|
(transform-world world rules))
|
||||||
(transform-world world init-rules)
|
(transform-world world init-rules)
|
||||||
(range generations)))
|
(range generations)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,18 +35,15 @@
|
||||||
[state]
|
[state]
|
||||||
(subs (str state) 1))
|
(subs (str state) 1))
|
||||||
|
|
||||||
|
|
||||||
(defn format-image-path
|
(defn format-image-path
|
||||||
"Render this `state`, assumed to be a keyword indicating a state in the
|
"Render this `state`, assumed to be a keyword indicating a state in the
|
||||||
world, into a path which should recover the corresponding image file."
|
world, into a path which should recover the corresponding image file."
|
||||||
[state]
|
[state]
|
||||||
(format "%s/%s.png" *image-base* (format-css-class state)))
|
(format "%s/%s.png" *image-base* (format-css-class state)))
|
||||||
|
|
||||||
|
|
||||||
(defn format-mouseover [cell]
|
(defn format-mouseover [cell]
|
||||||
(str cell))
|
(str cell))
|
||||||
|
|
||||||
|
|
||||||
(defn render-cell
|
(defn render-cell
|
||||||
"Render this world cell as a Hiccup table cell."
|
"Render this world cell as a Hiccup table cell."
|
||||||
[cell]
|
[cell]
|
||||||
|
@ -55,13 +52,11 @@
|
||||||
[:a {:href (format "inspect?x=%d&y=%d" (:x cell) (:y 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)}]]]))
|
[:img {:alt (:state cell) :width 32 :height 32 :src (format-image-path state)}]]]))
|
||||||
|
|
||||||
|
|
||||||
(defn render-world-row
|
(defn render-world-row
|
||||||
"Render this world `row` as a Hiccup table row."
|
"Render this world `row` as a Hiccup table row."
|
||||||
[row]
|
[row]
|
||||||
(apply vector (cons :tr (map render-cell row))))
|
(apply vector (cons :tr (map render-cell row))))
|
||||||
|
|
||||||
|
|
||||||
(defn render-world-table
|
(defn render-world-table
|
||||||
"Render this `world` as a Hiccup table."
|
"Render this `world` as a Hiccup table."
|
||||||
[world]
|
[world]
|
||||||
|
|
|
@ -1,6 +1,26 @@
|
||||||
(ns mw-engine.flow
|
(ns mw-engine.flow
|
||||||
"Allow flows of values between cells in the world."
|
"Allow flows of values between cells in the world.
|
||||||
(:require [mw-engine.utils :refer [get-cell get-num merge-cell]]
|
|
||||||
|
The design here is: a flow object is a map with the following properties:
|
||||||
|
1. :source, whose value is a location;
|
||||||
|
2. :destination, whose value is a location;
|
||||||
|
3. :property, whose value is a keyword;
|
||||||
|
4. :quantity, whose value is a positive real number.
|
||||||
|
|
||||||
|
A location object is a map with the following properties:
|
||||||
|
1. :x, whose value is a natural number not greater than the extent of the world;
|
||||||
|
2. :y, whose value is a natural number not greater than the extent of the world.
|
||||||
|
|
||||||
|
To execute a flow is transfer the quantity specified of the property specified
|
||||||
|
from the cell at the source specified to the cell at the destination specified;
|
||||||
|
if the source doesn't have sufficient of the property, then all it has should
|
||||||
|
be transferred, but no more: properties to be flowed cannot be pulled negative.
|
||||||
|
|
||||||
|
Flowing values through the world is consequently a two stage process: firstly
|
||||||
|
there's a planning stage, in which all the flows to be executed are computed
|
||||||
|
without changing the world, and then an execution stage, where they're all
|
||||||
|
executed. This namespace deals with mainly with execution."
|
||||||
|
(:require [mw-engine.utils :refer [get-cell get-num in-bounds? merge-cell]]
|
||||||
[taoensso.timbre :refer [info warn]]))
|
[taoensso.timbre :refer [info warn]]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -25,32 +45,6 @@
|
||||||
;;;; Copyright (C) 2014 Simon Brooke
|
;;;; Copyright (C) 2014 Simon Brooke
|
||||||
;;;;
|
;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;
|
|
||||||
;;;; 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.
|
|
||||||
;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
;; OK, the design here is: a flow object is a map with the following properties:
|
|
||||||
;; 1. :source, whose value is a location;
|
|
||||||
;; 2. :destination, whose value is a location;
|
|
||||||
;; 3. :property, whose value is a keyword;
|
|
||||||
;; 4. :quantity, whose value is a positive real number.
|
|
||||||
;;
|
|
||||||
;; A location object is a map with the following properties:
|
|
||||||
;; 1. :x, whose value is a natural number not greater than the extent of the world;
|
|
||||||
;; 2. :y, whose value is a natural number not greater than the extent of the world.
|
|
||||||
;;
|
|
||||||
;; to execute a flow is transfer the quantity specified of the property specified
|
|
||||||
;; from the cell at the source specified to the cell at the destination specified;
|
|
||||||
;; if the source doesn't have sufficient of the property, then all it has should
|
|
||||||
;; be transferred, but no more.
|
|
||||||
|
|
||||||
(defn coordinate?
|
(defn coordinate?
|
||||||
"Return `true` if this object `o` is a valid coordinate with respect to
|
"Return `true` if this object `o` is a valid coordinate with respect to
|
||||||
|
@ -65,12 +59,13 @@
|
||||||
|
|
||||||
(defn location?
|
(defn location?
|
||||||
"Return `true` if this object `o` is a location as defined above with respect to
|
"Return `true` if this object `o` is a location as defined above with respect to
|
||||||
this `world`, else `false`. Assumes square worlds."
|
this `world`, else `false`."
|
||||||
[o world]
|
[o world]
|
||||||
(try
|
(try
|
||||||
(and (map? o)
|
(and (map? o)
|
||||||
(coordinate? (:x o) world)
|
(integer? (:x o))
|
||||||
(coordinate? (:y o) world))
|
(integer? (:y o))
|
||||||
|
(in-bounds? world (:x o) (:y o)))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(warn (format "Not a valid location: %s; %s" o (.getMessage e)))
|
(warn (format "Not a valid location: %s; %s" o (.getMessage e)))
|
||||||
false)))
|
false)))
|
||||||
|
@ -98,16 +93,16 @@
|
||||||
(let [sx (-> flow :source :x)
|
(let [sx (-> flow :source :x)
|
||||||
sy (-> flow :source :y)
|
sy (-> flow :source :y)
|
||||||
source (get-cell world sx sy)
|
source (get-cell world sx sy)
|
||||||
dx (-> flow :destination :x)
|
dx (-> flow :destination :x)
|
||||||
dy (-> flow :destination :y)
|
dy (-> flow :destination :y)
|
||||||
dest (get-cell world dx dy)
|
dest (get-cell world dx dy)
|
||||||
p (:property flow)
|
p (:property flow)
|
||||||
q (min (:quantity flow) (get-num source p))
|
q (min (:quantity flow) (get-num source p))
|
||||||
s' (assoc source p (- (source p) q))
|
s' (assoc source p (- (source p) q))
|
||||||
d' (assoc dest p (+ (get-num dest p) q))]
|
d' (assoc dest p (+ (get-num dest p) q))]
|
||||||
(info (format "Moving %f units of %s from %d,%d to %d,%d"
|
(info (format "Moving %f units of %s from %d,%d to %d,%d"
|
||||||
(float q) (name p) sx sy dx dy))
|
(float q) (name p) sx sy dx dy))
|
||||||
(merge-cell (merge-cell world s') d'))
|
(merge-cell (merge-cell world s') d'))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(warn (format "Failed to execute flow %s: %s" flow (.getMessage e)))
|
(warn (format "Failed to execute flow %s: %s" flow (.getMessage e)))
|
||||||
;; return the world unmodified.
|
;; return the world unmodified.
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
(ns ^{:doc "Functions to apply a heightmap to a world."
|
(ns ^{:doc "Functions to apply a heightmap to a world.
|
||||||
|
|
||||||
|
Heightmaps are considered only as greyscale images, so colour is redundent
|
||||||
|
(will be ignored). Darker shades are higher."
|
||||||
:author "Simon Brooke"}
|
:author "Simon Brooke"}
|
||||||
mw-engine.heightmap
|
mw-engine.heightmap
|
||||||
(:require [mikera.image.core :refer [load-image filter-image]]
|
(:require [mikera.image.core :refer [load-image filter-image]]
|
||||||
|
@ -28,11 +31,6 @@
|
||||||
;;;; Copyright (C) 2014 Simon Brooke
|
;;;; Copyright (C) 2014 Simon Brooke
|
||||||
;;;;
|
;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;
|
|
||||||
;;;; Heightmaps are considered only as greyscale images, so colour is redundent
|
|
||||||
;;;; (will be ignored). Darker shades are higher.
|
|
||||||
;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(defn tag-property
|
(defn tag-property
|
||||||
"Set the value of this `property` of this cell from the corresponding pixel of this `heightmap`.
|
"Set the value of this `property` of this cell from the corresponding pixel of this `heightmap`.
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
(ns ^{:doc "A set of MicroWorld rules describing a simplified natural ecosystem."
|
(ns ^{:doc "A set of MicroWorld rules describing a simplified natural ecosystem.
|
||||||
:author "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. "
|
||||||
|
:author " Simon Brooke "}
|
||||||
mw-engine.natural-rules
|
mw-engine.natural-rules
|
||||||
(:require [mw-engine.utils :refer [get-int get-neighbours get-neighbours-with-state member?]]))
|
(:require [mw-engine.utils :refer [get-int get-neighbours get-neighbours-with-state member?]]))
|
||||||
|
|
||||||
|
@ -25,13 +30,6 @@
|
||||||
;;;; Copyright (C) 2014 Simon Brooke
|
;;;; 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.
|
;; treeline at arbitrary altitude.
|
||||||
(def treeline 150)
|
(def treeline 150)
|
||||||
|
@ -183,4 +181,4 @@
|
||||||
(list
|
(list
|
||||||
vegetation-rules
|
vegetation-rules
|
||||||
herbivore-rules
|
herbivore-rules
|
||||||
predator-rules)))
|
predator-rules)))
|
|
@ -47,7 +47,6 @@
|
||||||
[_ cell]
|
[_ cell]
|
||||||
(merge cell {:generation (get-int-or-zero cell :generation)}))
|
(merge cell {:generation (get-int-or-zero cell :generation)}))
|
||||||
|
|
||||||
|
|
||||||
(defn in-bounds
|
(defn in-bounds
|
||||||
"True if x, y are in bounds for this world (i.e., there is a cell at x, y)
|
"True if x, y are in bounds for this world (i.e., there is a cell at x, y)
|
||||||
else false. *DEPRECATED*: it's a predicate, prefer `in-bounds?`.
|
else false. *DEPRECATED*: it's a predicate, prefer `in-bounds?`.
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
(ns ^{:doc "Functions to create and to print two dimensional cellular automata."
|
(ns ^{:doc "Functions to create and to print two dimensional cellular automata.
|
||||||
:author "Simon Brooke"}
|
|
||||||
mw-engine.world
|
Nothing in this namespace should determine what states are possible within
|
||||||
(:require [clojure.string :as string]
|
the automaton, except for the initial state, :new.
|
||||||
[mw-engine.utils :refer [population]]))
|
|
||||||
|
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."
|
||||||
|
:author "Simon Brooke"}
|
||||||
|
mw-engine.world
|
||||||
|
(:require [clojure.string :as string]
|
||||||
|
[mw-engine.utils :refer [population]]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;
|
;;;;
|
||||||
|
@ -26,51 +34,14 @@
|
||||||
;;;; Copyright (C) 2014 Simon Brooke
|
;;;; Copyright (C) 2014 Simon Brooke
|
||||||
;;;;
|
;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;
|
|
||||||
;;;; 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.
|
|
||||||
;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
|
(defmacro make-cell
|
||||||
(defn- make-cell
|
|
||||||
"Create a minimal default cell at x, y
|
"Create a minimal default cell at x, y
|
||||||
|
|
||||||
* `x` the x coordinate at which this cell is created;
|
* `x` the x coordinate at which this cell is created;
|
||||||
* `y` the y coordinate at which this cell is created."
|
* `y` the y coordinate at which this cell is created."
|
||||||
[x y]
|
[x y]
|
||||||
{:x x :y y :state :new})
|
`{:x ~x :y ~y :state :new})
|
||||||
|
|
||||||
|
|
||||||
(defn- make-world-row
|
|
||||||
"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."
|
|
||||||
[index width height]
|
|
||||||
(cond (= index width) nil
|
|
||||||
:else (cons (make-cell index height)
|
|
||||||
(make-world-row (inc index) width height))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn- make-world-rows
|
|
||||||
"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."
|
|
||||||
[index width height]
|
|
||||||
(cond (= index height) nil
|
|
||||||
:else (cons (apply vector (make-world-row 0 width index))
|
|
||||||
(make-world-rows (inc index) width height))))
|
|
||||||
|
|
||||||
(defn make-world
|
(defn make-world
|
||||||
"Make a world width cells from east to west, and height cells from north to
|
"Make a world width cells from east to west, and height cells from north to
|
||||||
|
@ -79,16 +50,17 @@
|
||||||
* `width` a natural number representing the width of the matrix to be created;
|
* `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."
|
* `height` a natural number representing the height of the matrix to be created."
|
||||||
[width height]
|
[width height]
|
||||||
(apply vector (make-world-rows 0 width height)))
|
(apply vector
|
||||||
|
(map (fn [h]
|
||||||
|
(apply vector (map #(make-cell % h) (range width))))
|
||||||
|
(range height))))
|
||||||
|
|
||||||
(defn truncate-state
|
(defn truncate-state
|
||||||
"Truncate the print name of the state of this cell to at most limit characters."
|
"Truncate the print name of the state of this cell to at most limit characters."
|
||||||
[cell limit]
|
[cell limit]
|
||||||
(let [s (:state cell)]
|
(let [s (:state cell)]
|
||||||
(cond (> (count (str s)) limit) (subs s 0 limit)
|
(cond (> (count (str s)) limit) (subs s 0 limit)
|
||||||
:else s)))
|
:else s)))
|
||||||
|
|
||||||
|
|
||||||
(defn format-cell
|
(defn format-cell
|
||||||
"Return a formatted string summarising the current state of this cell."
|
"Return a formatted string summarising the current state of this cell."
|
||||||
|
@ -98,13 +70,11 @@
|
||||||
(population cell :deer)
|
(population cell :deer)
|
||||||
(population cell :wolves)))
|
(population cell :wolves)))
|
||||||
|
|
||||||
|
|
||||||
(defn- format-world-row
|
(defn- format-world-row
|
||||||
"Format one row in the state of a world for printing."
|
"Format one row in the state of a world for printing."
|
||||||
[row]
|
[row]
|
||||||
(string/join (map format-cell row)))
|
(string/join (map format-cell row)))
|
||||||
|
|
||||||
|
|
||||||
(defn print-world
|
(defn print-world
|
||||||
"Print the current state of this world, and return nil.
|
"Print the current state of this world, and return nil.
|
||||||
|
|
||||||
|
@ -112,8 +82,8 @@
|
||||||
[world]
|
[world]
|
||||||
(println)
|
(println)
|
||||||
(dorun
|
(dorun
|
||||||
(map
|
(map
|
||||||
#(println
|
#(println
|
||||||
(format-world-row %))
|
(format-world-row %))
|
||||||
world))
|
world))
|
||||||
nil)
|
nil)
|
||||||
|
|
Loading…
Reference in a new issue