diff --git a/resources/public/docs/mw-engine/uberdoc.html b/resources/public/docs/mw-engine/uberdoc.html index 8f84d66..fb03a49 100644 --- a/resources/public/docs/mw-engine/uberdoc.html +++ b/resources/public/docs/mw-engine/uberdoc.html @@ -3038,6 +3038,10 @@ net.brehaut.ClojureTools = (function (SH) { 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 @@ -3047,7 +3051,9 @@ legal.
that every cell's :x and :y properties reflect its place in the matrix. Seeworld.clj
.
-Rules are applied in turn until one matches.
+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.
Derive a cell from this cell of this world by applying these rules.
(defn- transform-cell
[cell world rules]
@@ -3080,21 +3086,7 @@ See world.clj
.
(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))))
(defn animate-world - "Run this world with these rules for this number of generations, and return nil - to avoid cluttering the screen. Principally for debugging.
-world
a world as discussed above;init-rules
a sequence of rules as defined above, to be run once to initialise the world;rules
a sequence of rules as definied above, to be run iteratively for each generation;generations
an (integer) number of generations."
-[world init-rules rules generations]
-(let [state (list (transform-world world init-rules) rules)]
-(dorun
- (take generations (iterate transform-world-state state)))
-state))Functions to apply a heightmap to a world.
+ (take generations (iterate transform-world-state state))))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.
@@ -3109,7 +3101,7 @@ ignored). Darker shades are higher. [fivetonine.collage.util]))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, but I don't care so much about them.
+ onto positive real numbers.n
a number, on the set of real numbers.(defn- abs [n] (cond (< n 0) (- 0 n) true n))
Set the altitude of this cell from the corresponding pixel of this heightmap. - If the heightmap you supply is smaller than the world, this will break and - it's ALL YOUR FAULT.
+ If the heightmap you supply is smaller than the world, this will break.cell
a cell, as discussed in world.clj, q.v. Alternatively, a map;Set the altitude of each cell in this sequence from the corresponding pixel of this heightmap. - If the heightmap you supply is smaller than the world, this will break and - it's ALL YOUR FAULT.
+ If the heightmap you supply is smaller than the world, this will break.row
a row in a world, as discussed in world.clj, q.v. Alternatively, a
@@ -3148,7 +3138,7 @@ ignored). Darker shades are higher.
(defn- apply-heightmap-row [row heightmap] - (apply vector (map #(transform-altitude %1 heightmap) row)))
Apply the image file loaded from this path to this world, and return a world whose + (apply vector (map #(transform-altitude % heightmap) row)))
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.
@@ -3160,7 +3150,7 @@ ignored). Darker shades are higher. [world imagepath] ;; bizarrely, the collage load-util is working for me, but the imagez version isn't. (let [heightmap (filter-image (grayscale)(load-image imagepath))] - (apply vector (map #(apply-heightmap-row %1 heightmap) world))))A set of MicroWorld rules describing a simplified natural ecosystem.
+ (apply vector (map #(apply-heightmap-row % heightmap) world))))A set of MicroWorld rules describing a simplified natural ecosystem.
(ns mw-engine.natural-rules (:use mw-engine.utils @@ -3343,33 +3333,52 @@ ignored). Darker shades are higher.
(defn population [cell species] - (get-int cell species))
Get the neighbours to distance depth of the cell at x, y in this world.
+ +* `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.
+
(defn get-neighbours - ([world x y depth] - "Get the neighbours to distance depth of the cell at x, y in this world. - * `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." - (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] - "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." - (get-neighbours world (:x cell) (:y cell) depth)) - ([world cell] - "Get the immediate neighbours of this cell in this world - * `world` a world, as described in world.clj; - * `cell` a cell within that world." - (get-neighbours world cell 1)))
Get the neighbours to distance depth of the cell at x, y in this world which + ([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] + "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." + (get-neighbours world (:x cell) (:y cell) depth)) + ([world cell] + "Get the immediate neighbours of this cell in this world + * `world` a world, as described in world.clj; + * `cell` a cell within that world." + (get-neighbours world cell 1)))
Get the neighbours to distance depth of the cell at x, y in this world which + have this value for this property.
+ +* `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
+
+(defn get-neighbours-with-property-value + ([world x y depth property value] + (filter #(= (get % property) value) (get-neighbours world x y depth))) + ([world cell depth property value] + (get-neighbours-with-property-value world (:x cell) (:y cell) depth + property value)) + ([world cell property value] + (get-neighbours-with-property-value world cell 1 + property value)))
Get the neighbours to distance depth of the cell at x, y in this world which have this state.
* `world` a world, as described in `world.clj`;
@@ -3379,8 +3388,12 @@ ignored). Darker shades are higher.
* `state` a keyword representing a state in the world.
(defn get-neighbours-with-state - [world x y depth state] - (filter #(= (:state %) state) (get-neighbours world x y depth)))
Functions to create and to print two dimensional cellular automata. Nothing in this + ([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)))
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.
diff --git a/resources/public/docs/mw-parser/uberdoc.html b/resources/public/docs/mw-parser/uberdoc.html index acd9ee8..5e6043a 100644 --- a/resources/public/docs/mw-parser/uberdoc.html +++ b/resources/public/docs/mw-parser/uberdoc.html @@ -3037,16 +3037,24 @@ net.brehaut.ClojureTools = (function (SH) {It should also but does not yet parse rules of the form:
-it generates rules in the form expected by mw-engine.core
, q.v.
it generates rules in the form expected by mw-engine.core
+It is, as I say, very simple; it generates a complete rule, or it fails completely, returning nil. +Very occasionally it generates a wrong rule - one which is not a correct translation of the rule +semantics - but that is buggy behaviour, which I'll try to fix over the next few weeks, not a +design fault.
+ +More significantly it does not generate useful error messages on failure. This is, I think, a much +more complex issue which I don't yet know how to address.
(ns mw-parser.core (:use mw-engine.utils @@ -3054,62 +3062,198 @@ net.brehaut.ClojureTools = (function (SH) {
(declare parse-conditions) (declare parse-not-condition) (declare parse-simple-condition)
a regular expression which matches string representation of numbers
-(def re-number #"^[0-9.]*$")
Parse '[property] is less than [value]'.
+(def re-number #"^[0-9.]*$")
If this token appears to represent an explicit number, return that number; + otherwise, make a keyword of it and return that.
+(defn keyword-or-numeric + [token] + (cond + (re-matches re-number token) (read-string token) + (keyword? token) token + true (keyword token)))
Generally all functions in this file with names beginning 'parse-' take a +sequence of tokens (and in some cases other optional arguments) and return a +vector comprising
+ +In every case if the function cannot parse the desired construct from the +front of the sequence of tokens it returns nil.
+Parse a number.
+(defn parse-numeric-value + [[value & remainder]] + (if (re-matches re-number value) [(read-string value) remainder]))
Parse a token assumed to be the name of a property of the current cell, + whose value is assumed to be an integer.
+(defn parse-property-int + [[value & remainder]] + (if value [(list 'get-int 'cell (keyword value)) remainder]))
Parse a token assumed to be the name of a property of the current cell.
+(defn parse-property-value + [[value & remainder]] + (if value [(list (keyword value) 'cell) remainder]))
Parse a value from the first of these tokens
. If expect-int
is true, return
+ an integer or something which will evaluate to an integer.
(defn parse-simple-value + ([tokens expect-int] + (or + (parse-numeric-value tokens) + (cond expect-int + (parse-property-int tokens) + true (parse-property-value tokens)))) + ([tokens] + (parse-simple-value tokens false)))
Parse a list of values from among these tokens
. If expect-int
is true, return
+ an integer or something which will evaluate to an integer.
(defn parse-disjunct-value + [[OR token & tokens] expect-int] + (cond (member? OR '("or" "in")) + (let [[others remainder] (parse-disjunct-value tokens expect-int)] + [(cons + (cond + expect-int (first (parse-simple-value (list token) true)) + true (keyword token)) + others) + remainder]) + true [nil (cons OR (cons token tokens))]))
Parse a value from among these tokens
. If expect-int
is true, return
+ an integer or something which will evaluate to an integer.
(defn parse-value + ([tokens expect-int] + (or + (parse-disjunct-value tokens expect-int) + (parse-simple-value tokens))) + ([tokens] + (parse-value tokens false)))
(defn parse-member-condition + [[property IN & rest]] + (if (= IN "in") + (let [[l remainder] (parse-disjunct-value (cons "in" rest) false)] + [(list 'member? (keyword property) l) remainder])))
Parse '[property] less than [value]'.
(defn parse-less-condition - [[property is less than value & rest]] - (cond (and (member? is '("is" "are")) (= less "less") (= than "than")) - [(list '< (list 'get-int 'cell (keyword property)) (read-string value)) rest]))
Parse '[property] is more than [value]'.
+ [[property LESS THAN value & rest]] + (cond (and (= LESS "less") (= THAN "than")) + [(list '< (list 'get-int 'cell (keyword property)) (read-string value)) rest]))Parse '[property] more than [value]'.
(defn parse-more-condition - [[property is more than value & rest]] - (cond (and (member? is '("is" "are")) (= more "more") (= than "than")) - [(list '> (list 'get-int 'cell (keyword property)) (read-string value)) rest]))
Parse clauses of the form 'x is y', but not 'x is more than y' or 'x is less than y'. + [[property MORE THAN value & rest]] + (cond (and (= MORE "more") (= THAN "than")) + [(list '> (list 'get-int 'cell (keyword property)) (read-string value)) rest]))
(defn parse-between-condition + [[p BETWEEN v1 AND v2 & rest]] + (cond (and (= BETWEEN "between") (= AND "and") (not (nil? v2))) + (let [property (first (parse-simple-value (list p) true)) + value1 (first (parse-simple-value (list v1) true)) + value2 (first (parse-simple-value (list v2) true))] + [(list 'or + (list '< value1 property value2) + (list '> value1 property value2)) rest])))
Parse clauses of the form 'x is y', 'x is in y or z...', + 'x is between y and z', 'x is more than y' or 'x is less than y'. It is necessary to disambiguate whether value is a numeric or keyword.
(defn parse-is-condition - [[property is value & rest]] - (cond (and (member? is '("is" "are")) - (not (member? value '("more" "less" "exactly" "not")))) - [(cond - (re-matches re-number value)(list '= (list 'get-int 'cell (keyword property)) (read-string value)) - true (list '= (list (keyword property) 'cell) (keyword value))) - rest]))
Parse the negation of a simple condition.
+ [[property IS value & rest]] + (cond + (member? IS '("is" "are")) + (let [tokens (cons property (cons value rest))] + (cond + (= value "in") (parse-member-condition tokens) + (= value "between") (parse-between-condition tokens) + (= value "more") (parse-more-condition tokens) + (= value "less") (parse-less-condition tokens) + (re-matches re-number value) [(list '= (list 'get-int 'cell (keyword property)) (read-string value)) rest] + value [(list '= (list (keyword property) 'cell) (keyword value)) rest]))))Parse the negation of a simple condition.
(defn parse-not-condition - [[property is not & rest]] - (cond (and (member? is '("is" "are")) (= not "not")) - (let [partial (parse-simple-condition (cons property (cons is rest)))] - (cond partial - (let [[condition remainder] partial] - [(list 'not condition) remainder])))))
Parse conditions of the form '[property] [comparison] [value]'.
+ [[property IS NOT & rest]] + (cond (and (member? IS '("is" "are")) (= NOT "not")) + (let [partial (parse-simple-condition (cons property (cons "is" rest)))] + (cond partial + (let [[condition remainder] partial] + [(list 'not condition) remainder])))))(defn- gen-neighbours-condition + [comparator quantity property value remainder] + [(list comparator + (list 'count + (list 'get-neighbours-with-property-value 'world 'cell + (keyword property) (keyword-or-numeric value))) + quantity) + remainder])
Parse conditions of the form '...more than 6 neighbours are [condition]'
+(defn parse-comparator-neighbours-condition + [[MORE THAN n NEIGHBOURS have-or-are & rest]] + (let [quantity (first (parse-numeric-value (list n))) + comparator (cond (= MORE "more") '> + (member? MORE '("fewer" "less")) '<)] + (cond + (and quantity + comparator + (= THAN "than") + (= NEIGHBOURS "neighbours")) + (cond + (= have-or-are "are") + (let [[value & remainder] rest] + (gen-neighbours-condition comparator quantity :state value remainder)) + (= have-or-are "have") + (let [[property comp1 comp2 value & remainder] rest] + (cond (and (= comp1 "equal") (= comp2 "to")) + (gen-neighbours-condition comparator quantity property value remainder) +;; (and (= comp1 "more") (= comp2 "than")) +;; (gen-neighbours-condition '> quantity property value remainder) +;; (and (= comp1 "less") (= comp2 "than")) +;; (gen-neighbours-condition '< quantity property value remainder)))))))
(defn parse-some-neighbours-condition + [[SOME NEIGHBOURS & rest]] + (cond + (and (= SOME "some") (= NEIGHBOURS "neighbours")) + (parse-comparator-neighbours-condition (concat '("more" "than" "0" "neighbours") rest))))
Parse conditions of the form '...6 neighbours are condition'
+(defn parse-simple-neighbours-condition + [[n NEIGHBOURS have-or-are & rest]] + (let [quantity (first (parse-numeric-value (list n)))] + (cond + (and quantity (= NEIGHBOURS "neighbours")) + (cond + (= have-or-are "are") + (let [[value & remainder] rest] + (gen-neighbours-condition '= quantity :state value remainder)) + (= have-or-are "have") + (let [[property comp1 comp2 value & remainder] rest] + (cond (and (= comp1 "equal") (= comp2 "to")) + (gen-neighbours-condition '= quantity property value remainder) +;; (and (= comp1 "more") (= comp2 "than")) +;; (gen-neighbours-condition '> quantity property value remainder) +;; (and (= comp1 "less") (= comp2 "than")) +;; (gen-neighbours-condition '< quantity property value remainder)))))))
Parse conditions referring to neighbours
+(defn parse-neighbours-condition + [tokens] + (or + (parse-simple-neighbours-condition tokens) + (parse-comparator-neighbours-condition tokens) + (parse-some-neighbours-condition tokens)))
Parse conditions of the form '[property] [comparison] [value]'.
(defn parse-simple-condition [tokens] - (or (parse-is-condition tokens) - (parse-not-condition tokens) - (parse-less-condition tokens) - (parse-more-condition tokens)))
Parse '... or [condition]' from tokens
, where left
is the already parsed first disjunct.
Parse '... or [condition]' from tokens
, where left
is the already parsed first disjunct.
(defn parse-disjunction-condition [left tokens] (let [partial (parse-conditions tokens)] - (if - partial - (let [[right remainder] partial] - [(list 'or left right) remainder]))))
Parse '... and [condition]' from tokens
, where left
is the already parsed first conjunct.
Parse '... and [condition]' from tokens
, where left
is the already parsed first conjunct.
(defn parse-conjunction-condition [left tokens] (let [partial (parse-conditions tokens)] (if partial - (let [[right remainder] partial] - [(list 'and left right) remainder]))))
Parse conditions from tokens
, where conditions may be linked by either 'and' or 'or'.
Parse conditions from tokens
, where conditions may be linked by either 'and' or 'or'.
(defn parse-conditions [tokens] (let [partial (parse-simple-condition tokens)] (if partial - (let [[left [next & remainder]] partial] - (cond - (= next "and") (parse-conjunction-condition left remainder) - (= next "or") (parse-disjunction-condition left remainder) - true partial)))))
Parse the left hand side ('if...') of a production rule.
+ (let [[left [next & remainder]] partial] + (cond + (= next "and") (parse-conjunction-condition left remainder) + (= next "or") (parse-disjunction-condition left remainder) + true partial)))))Parse the left hand side ('if...') of a production rule.
(defn parse-left-hand-side - [tokens] - (if + [tokens] + (if (= (first tokens) "if") (parse-conditions (rest tokens))))
Parse actions of the form '[property] should be [property] [arithmetic-operator] [value]', e.g. 'fertility should be fertility + 1', or 'deer should be deer - wolves'.
@@ -3121,27 +3265,41 @@ net.brehaut.ClojureTools = (function (SH) { [(list 'merge (or previous 'cell) {(keyword prop1) (list (symbol operator) (list 'get-int 'cell (keyword prop2)) (cond - (re-matches re-number value) (read-string value) - true (list 'get-int 'cell (keyword value))))}) rest]))Parse actions of the form '[property] should be [value].'
+ (re-matches re-number value) (read-string value) + true (list 'get-int 'cell (keyword value))))}) rest]))Parse actions of the form '[property] should be [value].'
(defn parse-set-action [previous [property should be value & rest]] (if (and (= should "should") (= be "be")) [(list 'merge (or previous 'cell) {(keyword property) (cond (re-matches re-number value) (read-string value) true (keyword value))}) rest]))
(defn parse-simple-action [previous tokens] - (or (parse-arithmetic-action previous tokens) - (parse-set-action previous tokens)))
Parse actions from tokens.
+ (or (parse-arithmetic-action previous tokens) + (parse-set-action previous tokens)))Parse actions from tokens.
(defn parse-actions [previous tokens] (let [[left remainder] (parse-simple-action previous tokens)] (cond left (cond (= (first remainder) "and") (parse-actions left (rest remainder)) - true (list left)))))
Parse the right hand side ('then...') of a production rule.
+ true (list left)))))Parse a probability of an action from this collection of tokens
+(defn parse-probability + [previous [n CHANCE IN m & tokens]] + (cond + (and (= CHANCE "chance")(= IN "in")) + (let [[action remainder] (parse-actions previous tokens)] + (cond action + [(list 'cond + (list '< + (list 'rand + (first (parse-simple-value (list m) true))) + (first (parse-simple-value (list n) true))) + action) remainder]))))
Parse the right hand side ('then...') of a production rule.
(defn parse-right-hand-side - [tokens] - (if (= (first tokens) "then") - (parse-actions nil (rest tokens))))
Parse a complete rule from this string or sequence of string tokens.
+ [[THEN & tokens]] + (if (= THEN "then") + (or + (parse-probability nil tokens) + (parse-actions nil tokens))))Parse a complete rule from this string or sequence of string tokens.
(defn parse-rule [line] (cond @@ -3149,5 +3307,13 @@ net.brehaut.ClojureTools = (function (SH) { true (let [[left remainder] (parse-left-hand-side line) [right junk] (parse-right-hand-side remainder)] ;; there shouldn't be any junk (should be null) - (list 'fn ['cell 'world] (list 'if left right)))))