354 lines
24 KiB
HTML
354 lines
24 KiB
HTML
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<link rel="stylesheet" href="../coverage.css"/> <title> mw_engine/core.clj </title>
|
|
</head>
|
|
<body>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
001 (ns ^{:doc "Functions to transform a world and run rules.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
002
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
003 Every rule is a function of two arguments, a cell and a world. If the rule
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
004 fires, it returns a new cell, which should have the same values for `:x` and
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
005 `:y` as the old cell. Anything else can be modified.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
006
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
007 While any function of two arguments can be used as a rule, a special high
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
008 level rule language is provided by the `mw-parser` package, which compiles
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
009 rules expressed in a subset of English rules into suitable functions.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
010
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
011 A cell is a map containing at least values for the keys :x, :y, and :state;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
012 a transformation should not alter the values of :x or :y, and should not
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
013 return a cell without a keyword as the value of :state. Anything else is
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
014 legal.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
015
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
016 A world is a two dimensional matrix (sequence of sequences) of cells, such
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
017 that every cell's `:x` and `:y` properties reflect its place in the matrix.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
018 See `world.clj`.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
019
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
020 Each time the world is transformed (see `transform-world`), for each cell,
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
021 rules are applied in turn until one matches. Once one rule has matched no
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
022 further rules can be applied to that cell."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
023 :author "Simon Brooke"}
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
024 mw-engine.core
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
025 (:require [mw-engine.utils :refer [get-int-or-zero map-world]]
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
026 [taoensso.timbre :as l]))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
027
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
028 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
029 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
030 ;;;; mw-engine: the state/transition engine of MicroWorld.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
031 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
032 ;;;; This program is free software; you can redistribute it and/or
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
033 ;;;; modify it under the terms of the GNU General Public License
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
034 ;;;; as published by the Free Software Foundation; either version 2
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
035 ;;;; of the License, or (at your option) any later version.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
036 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
037 ;;;; This program is distributed in the hope that it will be useful,
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
038 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
039 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
040 ;;;; GNU General Public License for more details.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
041 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
042 ;;;; You should have received a copy of the GNU General Public License
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
043 ;;;; along with this program; if not, write to the Free Software
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
044 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
045 ;;;; USA.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
046 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
047 ;;;; Copyright (C) 2014 Simon Brooke
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
048 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
049 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
050
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
051 (defn apply-rule
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
052 "Apply a single `rule` to a `cell`. What this is about is that I want to be able,
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
053 for debugging purposes, to tag a cell with the rule text of the rule which
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
054 fired (and especially so when an exception is thrown. So a rule may be either
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
055 an ifn, or a list (ifn source-text). This function deals with despatching
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
056 on those two possibilities. `world` is also passed in in order to be able
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
057 to access neighbours."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
058 ([world cell rule]
|
|
</span><br/>
|
|
<span class="partial" title="2 out of 3 forms covered">
|
|
059 (cond
|
|
</span><br/>
|
|
<span class="covered" title="9 out of 9 forms covered">
|
|
060 (ifn? rule) (apply-rule world cell rule nil)
|
|
</span><br/>
|
|
<span class="covered" title="11 out of 11 forms covered">
|
|
061 (seq? rule) (let [[afn src] rule] (apply-rule world cell afn src))))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
062 ([world cell rule source]
|
|
</span><br/>
|
|
<span class="covered" title="8 out of 8 forms covered">
|
|
063 (let [result (apply rule (list cell world))]
|
|
</span><br/>
|
|
<span class="covered" title="2 out of 2 forms covered">
|
|
064 (cond
|
|
</span><br/>
|
|
<span class="covered" title="12 out of 12 forms covered">
|
|
065 (and result source) (merge result {:rule source})
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
066 :else result))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
067
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
068 (defn- apply-rules
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
069 "Derive a cell from this `cell` of this `world` by applying these `rules`."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
070 [world cell rules]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 6 forms covered">
|
|
071 (cond (empty? rules) cell
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 8 forms covered">
|
|
072 :else (let [result (apply-rule world cell (first rules))]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
073 (cond result result
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 7 forms covered">
|
|
074 :else (apply-rules world cell (rest rules))))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
075
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
076 (defn- transform-cell
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
077 "Derive a cell from this `cell` of this `world` by applying these `rules`. If an
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
078 exception is thrown, cache its message on the cell and set it's state to error"
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
079 [world cell rules]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
080 (try
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 2 forms covered">
|
|
081 (merge
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 5 forms covered">
|
|
082 (apply-rules world cell rules)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 8 forms covered">
|
|
083 {:generation (+ (get-int-or-zero cell :generation) 1)})
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
084 (catch Exception e
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 8 forms covered">
|
|
085 (merge cell {:error
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
086 (format "%s at generation %d when in state %s"
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
087 (.getMessage e)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
088 (:generation cell)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
089 (:state cell))
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 5 forms covered">
|
|
090 :stacktrace (map #(.toString %) (.getStackTrace e))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
091 :state :error}))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
092
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
093 (defn transform-world
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
094 "Return a world derived from this `world` by applying these `rules` to each cell."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
095 ([world rules]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 7 forms covered">
|
|
096 (map-world world transform-cell (list rules))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
097
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
098 (defn run-world
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
099 "Run this world with these rules for this number of generations.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
100
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
101 * `world` a world as discussed above;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
102 * `init-rules` a sequence of rules as defined above, to be run once to initialise the world;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
103 * `rules` a sequence of rules as defined above, to be run iteratively for each generation;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
104 * `generations` an (integer) number of generations.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
105
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
106 Return the final generation of the world."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
107 [world init-rules rules generations]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
108 (reduce (fn [world iteration]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 17 forms covered">
|
|
109 (l/info "Running iteration " iteration)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
110 (transform-world world rules))
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
111 (transform-world world init-rules)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
112 (range generations)))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
113
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
114
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
115
|
|
</span><br/>
|
|
</body>
|
|
</html>
|