444 lines
31 KiB
HTML
444 lines
31 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.flow :refer [flow-world]]
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
026 [mw-engine.utils :refer [add-history-event get-int-or-zero map-world rule-type]]
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
027 [taoensso.timbre :as l]))
|
|
</span><br/>
|
|
<span class="blank" 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 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
031 ;;;; mw-engine: the state/transition engine of MicroWorld.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
032 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
033 ;;;; This program is free software; you can redistribute it and/or
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
034 ;;;; modify it under the terms of the GNU General Public License
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
035 ;;;; as published by the Free Software Foundation; either version 2
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
036 ;;;; of the License, or (at your option) any later version.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
037 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
038 ;;;; 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">
|
|
039 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
040 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
041 ;;;; GNU General Public License for more details.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
042 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
043 ;;;; 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">
|
|
044 ;;;; along with this program; if not, write to the Free Software
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
045 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
046 ;;;; USA.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
047 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
048 ;;;; Copyright (C) 2014 Simon Brooke
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
049 ;;;;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
050 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
051
|
|
</span><br/>
|
|
<span class="covered" title="2 out of 2 forms covered">
|
|
052 (def ^:dynamic *with-history*
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
053 "I suspect that caching history on the cells is greatly worsening the
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
054 memory problems. Make it optional, but by default false."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
055 false)
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
056
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
057 (defn apply-rule
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
058 "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">
|
|
059 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">
|
|
060 fired (and especially so when an exception is thrown). "
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
061 ;; as of version 0-3-0, metadata for rules is now passed around on the metadata
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
062 ;; of the rule function itself. Yes, I know, this is obvious; but I'll confess
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
063 ;; I didn't think of it before.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
064 [world cell rule]
|
|
</span><br/>
|
|
<span class="covered" title="2 out of 2 forms covered">
|
|
065 (let [result (try
|
|
</span><br/>
|
|
<span class="covered" title="7 out of 7 forms covered">
|
|
066 (apply rule (list cell world))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
067 (catch Exception e
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 16 forms covered">
|
|
068 (l/warn e
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
069 (format
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
070 "Error in `apply-rule`: `%s` (%s) while executing rule `%s` on cell `%s`"
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
071 e
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
072 (.getMessage e)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 5 forms covered">
|
|
073 (-> rule meta :lisp)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
074 cell))))]
|
|
</span><br/>
|
|
<span class="covered" title="2 out of 2 forms covered">
|
|
075 (if *with-history*
|
|
</span><br/>
|
|
<span class="covered" title="4 out of 4 forms covered">
|
|
076 (add-history-event result rule)
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
077 result)))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
078
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
079 (defn- apply-rules
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
080 "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">
|
|
081 [world cell rules]
|
|
</span><br/>
|
|
<span class="partial" title="4 out of 5 forms covered">
|
|
082 (or
|
|
</span><br/>
|
|
<span class="covered" title="2 out of 2 forms covered">
|
|
083 (first
|
|
</span><br/>
|
|
<span class="covered" title="2 out of 2 forms covered">
|
|
084 (remove
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
085 nil?
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
086 (try
|
|
</span><br/>
|
|
<span class="covered" title="9 out of 9 forms covered">
|
|
087 (map #(apply-rule world cell %) rules)
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
088 (catch Exception e
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 16 forms covered">
|
|
089 (l/warn e
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
090 (format
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
091 "Error in `apply-rules`: `%s` (%s) while executing rules on cell `%s`"
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 2 forms covered">
|
|
092 (-> e .getClass .getName)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
093 (.getMessage e)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
094 cell))))))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
095 cell))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
096
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
097 (defn- transform-cell
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
098 "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">
|
|
099 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">
|
|
100 [world cell rules]
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
101 (try
|
|
</span><br/>
|
|
<span class="covered" title="2 out of 2 forms covered">
|
|
102 (merge
|
|
</span><br/>
|
|
<span class="covered" title="5 out of 5 forms covered">
|
|
103 (apply-rules world cell rules)
|
|
</span><br/>
|
|
<span class="covered" title="8 out of 8 forms covered">
|
|
104 {:generation (+ (get-int-or-zero cell :generation) 1)})
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
105 (catch Exception e
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
106 (let [narrative (format "Error in `transform-cell`: `%s` (%s) at generation %d when in state %s;"
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 2 forms covered">
|
|
107 (-> e .getClass .getName)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
108 (.getMessage e)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
109 (:generation cell)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
110 (:state cell))]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 17 forms covered">
|
|
111 (l/warn e narrative)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 1 forms covered">
|
|
112 cell))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
113
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
114 (defn transform-world
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
115 "Return a world derived from this `world` by applying the production rules
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
116 found among these `rules` to each cell."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
117 [world rules]
|
|
</span><br/>
|
|
<span class="covered" title="4 out of 4 forms covered">
|
|
118 (map-world world transform-cell
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
119 ;; Yes, that `list` is there for a reason!
|
|
</span><br/>
|
|
<span class="covered" title="2 out of 2 forms covered">
|
|
120 (list
|
|
</span><br/>
|
|
<span class="covered" title="3 out of 3 forms covered">
|
|
121 (filter
|
|
</span><br/>
|
|
<span class="covered" title="5 out of 5 forms covered">
|
|
122 #(= :production (rule-type %))
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
123 rules))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
124
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
125 (defn run-world
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
126 "Run this world with these rules for this number of generations.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
127
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
128 * `world` a world as discussed above;
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
129 * `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">
|
|
130 * `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">
|
|
131 * `generations` an (integer) number of generations.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
132
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
133 **NOTE THAT** all rules **must** be tagged with `rule-type` metadata, or thet **will not**
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
134 be executed.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
135
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
136 Return the final generation of the world."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
137 ([world rules generations]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 7 forms covered">
|
|
138 (run-world world rules rules (dec generations)))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
139 ([world init-rules rules generations]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
140 (reduce (fn [world iteration]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 17 forms covered">
|
|
141 (l/info "Running iteration " iteration)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 5 forms covered">
|
|
142 (let [w' (transform-world world rules)]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
143 (flow-world w' rules)))
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
144 (transform-world world init-rules)
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
145 (range generations))))
|
|
</span><br/>
|
|
</body>
|
|
</html>
|