Now runs rather nicely, although output could be tidied up a bit!

This commit is contained in:
Simon Brooke 2014-06-30 12:14:58 +01:00
parent 7bfefe95ab
commit 977947d4b0
3 changed files with 119 additions and 10 deletions

View file

@ -1,6 +1,98 @@
(ns mw-engine.core) (ns mw-engine.core
(:use mw-engine.world
mw-engine.utils))
(defn foo ;; every rule is a function of two arguments, a cell and a world. If the rule
"I don't do a whole lot." ;; fires, it returns a new cell, which should have the same values for :x and
[x] ;; :y as the old cell. Anything else can be modified.
(println x "Hello, World!")) ;;
;; Rules are applied in turn until one matches.
(def treeline 10)
(def natural-rules
(list
;; Randomly, birds plant tree seeds into pasture.
(fn [cell world] (cond (and (= (:state cell) :pasture)(< (rand 10) 1))(merge cell {:state :scrub})))
;; Scrub below the treeline grows gradually into forest
(fn [cell world]
(cond (and
(= (:state cell) :scrub)
(< (:altitude cell) treeline))
(merge cell {:state :scrub2})))
(fn [cell world] (cond (= (:state cell) :scrub2) (merge cell {:state :forest})))
;; Forest on fertile land at low altitude grows to climax
(fn [cell world]
(cond
(and
(= (:state cell) :forest)
(> (:fertility cell) 10))
(merge cell {:state :climax})))
;; Climax forest occasionally catches fire (e.g. lightning strikes)
(fn [cell world] (cond (and (= (:state cell) :climax)(< (rand 10) 1)) (merge cell {:state :fire})))
;; Climax forest neighbouring fires is likely to catch fire
(fn [cell world]
(cond
(and (= (:state cell) :climax)
(< (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 scrub, otherwise grassland.
(fn [cell world]
(cond
(and (= (:state cell) :waste)
(not
(empty?
(flatten
(list
(get-neighbours-with-state world (:x cell) (:y cell) 1 :scrub2)
(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 :scrub})))
(fn [cell world]
(cond (= (:state cell) :waste)
(merge cell {:state :pasture})))
;; Forest increases soil fertility
(fn [cell world]
(cond (member? (:state cell) '(:forest :climax))
(merge cell {:fertility (+ (:fertility cell) 1)})))
))
(defn transform-cell
"Derive a cell from this cell of this world by applying these rules."
[cell world rules]
(cond (empty? rules) cell
true (let [r (apply (eval (first rules)) (list cell world))]
(cond r r
true (transform-cell cell world (rest rules))))))
(defn transform-world-row
"Return a row derived from this row of this world by applying these rules to each cell."
[row world rules]
(map #(transform-cell % world rules) row))
(defn transform-world
"Return a world derived from this world by applying these rules to each cell."
[world rules]
(map
#(transform-world-row % world rules)
world))
(defn transform-world-state
"Consider this single argument as a list of world and rules; apply the rules
to transform the world, and return a list of the new, transformed world and
these rules. As a side effect, print the world."
[state]
(list
(print-world (transform-world (first state) (first (rest state))))
(first (rest state))))
(defn run-world
"Run this world with these rules for this number of generations."
[world rules generations]
(let [state (list world rules)]
(take generations (iterate transform-world-state state))))

5
src/mw_engine/utils.clj Normal file
View file

@ -0,0 +1,5 @@
(ns mw-engine.utils)
(defn member?
"True if elt is a member of col."
[elt col] (some #(= elt %) col))

View file

@ -4,7 +4,7 @@
(defn make-cell (defn make-cell
"Create a default cell at x, y" "Create a default cell at x, y"
[x y] [x y]
{:x x :y y :altitude 1 :state :pasture}) {:x x :y y :altitude 1 :state :waste :fertility 1})
(defn make-world-row (defn make-world-row
"Make the (remaining) cells in a row at this height in a world of this width." "Make the (remaining) cells in a row at this height in a world of this width."
@ -17,14 +17,14 @@
"Make the (remaining) rows in a world of this width and height, from this "Make the (remaining) rows in a world of this width and height, from this
index." index."
(cond (= index height) nil (cond (= index height) nil
true (cons (apply vector (make-world-row 0 width index)) true (cons (make-world-row 0 width index)
(make-world-rows (+ index 1) width height)))) (make-world-rows (+ index 1) 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
south." south."
[width height] [width height]
(apply vector (make-world-rows 0 width height))) (make-world-rows 0 width height))
(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)
@ -46,6 +46,12 @@
(range (- x depth) (+ x depth)) (range (- x depth) (+ x depth))
(range (- y depth) (+ y depth))))) (range (- y depth) (+ y depth)))))
(defn get-neighbours-with-state
"Get the neighbours to distance depth of the cell at x, y in this world which
have this state."
[world x y depth state]
(filter #(= (:state %) state) (get-neighbours world x y depth)))
(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]
@ -60,6 +66,12 @@
(map #(format "%10s" (truncate-state % 10)) row))) (map #(format "%10s" (truncate-state % 10)) row)))
(defn print-world (defn print-world
"Print the current state of this world." "Print the current state of this world, and return nil"
[world] [world]
(dorun (map #(println (format-world-row %)) world)) nil) (println)
(dorun
(map
#(println
(format-world-row %))
world))
world)