From d23239a5c64b14a281922e7221ac34c90f9f3f80 Mon Sep 17 00:00:00 2001
From: Simon Brooke <simon@journeyman.cc>
Date: Sat, 19 Jul 2014 13:22:38 +0100
Subject: [PATCH] Improved debugging, and lots more tests. Ultimately I intend
 the enhanced debugging to be optional, because it will have a performance hit

---
 project.clj                   |  1 +
 src/mw_engine/core.clj        | 28 +++++++++++++-
 test/mw_engine/core_test.clj  | 23 ++++++++++--
 test/mw_engine/utils_test.clj | 69 +++++++++++++++++++++++++++++++++++
 test/mw_engine/world_test.clj | 28 ++++++++++++++
 5 files changed, 145 insertions(+), 4 deletions(-)
 create mode 100644 test/mw_engine/utils_test.clj
 create mode 100644 test/mw_engine/world_test.clj

diff --git a/project.clj b/project.clj
index e70c656..44d3a37 100644
--- a/project.clj
+++ b/project.clj
@@ -6,5 +6,6 @@
   :plugins [[lein-marginalia "0.7.1"]]
   :dependencies [[org.clojure/clojure "1.5.1"]
                  [org.clojure/math.combinatorics "0.0.7"]
+                 [org.clojure/tools.trace "0.7.8"]
                  [net.mikera/imagez "0.3.1"]
                  [fivetonine/collage "0.2.0"]])
diff --git a/src/mw_engine/core.clj b/src/mw_engine/core.clj
index a80103e..2bdeb1b 100644
--- a/src/mw_engine/core.clj
+++ b/src/mw_engine/core.clj
@@ -26,11 +26,37 @@
 ;; rules are applied in turn until one matches. Once one rule has matched no
 ;; further rules can be applied.
 
+
+(defn apply-rule 
+  "Apply a single rule to a cell. What this is about is that I want to be able,
+   for debugging purposes, to tag a cell with the rule text of the rule which
+   fired (and especially so when an exception is thrown. So a rule may be either
+   an ifn, or a list (ifn source-text). This function deals with despatching
+   on those two possibilities."
+  ([cell world rule]
+   (cond
+     (ifn? rule) (apply-rule cell world rule nil)
+     (seq? rule) (let [[afn src] rule] (apply-rule cell world afn src))))
+                  ;; {:afn afn :src src})))
+     ;; (apply-rule cell world (first rule) (first (rest rule)))))
+  ([cell world rule source]
+    (try
+      (let [result (apply rule (list cell world))]
+        (cond 
+          (and result source) (merge result {:rule source})
+          true result))
+      (catch Exception e
+        (merge cell {:error (format "%s at generation %d when in state %s"
+                           (.getMessage e)
+                           (:generation cell)
+                           (:state cell))
+                     :error-rule source})))))
+
 (defn- apply-rules
   "Derive a cell from this cell of this world by applying these rules."
   [cell world rules]
   (cond (empty? rules) cell
-    true (let [result (apply (eval (first rules)) (list cell world))]
+    true (let [result (apply-rule cell world (first rules))]
            (cond result result
              true (apply-rules cell world (rest rules))))))
 
diff --git a/test/mw_engine/core_test.clj b/test/mw_engine/core_test.clj
index 7714531..6d1fc6e 100644
--- a/test/mw_engine/core_test.clj
+++ b/test/mw_engine/core_test.clj
@@ -2,6 +2,23 @@
   (:require [clojure.test :refer :all]
             [mw-engine.core :refer :all]))
 
-(deftest a-test
-  (testing "FIXME, I fail."
-    (is (= 0 0))))
+(deftest apply-rule-test
+  (testing "Application of a single rule"
+           (let [afn (eval 
+                       (fn [cell world]
+                         (cond 
+                           (= (:state cell) :new) 
+                           (merge cell {:state :grassland}))))
+                 pair (list afn "Test source")]
+             (is (nil? (apply-rule {:state :water} nil afn))
+                 "Rule shouldn't fire when state is wrong")
+             (is (nil? (apply-rule {:state :water} nil pair))
+                 "Rule shouldn't fire when state is wrong")
+             (is (= (:state (apply-rule {:state :new} nil afn)) :grassland)
+                 "Rule should fire when state is correct")
+             (is (= (:state (apply-rule {:state :new} nil pair)) :grassland)
+                 "Rule should fire when state is correct")
+             (is (nil? (:rule (apply-rule {:state :new} nil afn)))
+                 "No rule text if not provided")
+             (is (= (:rule (apply-rule {:state :new} nil pair)) "Test source")
+                 "Rule text cached on cell if provided"))))
\ No newline at end of file
diff --git a/test/mw_engine/utils_test.clj b/test/mw_engine/utils_test.clj
new file mode 100644
index 0000000..a91eae2
--- /dev/null
+++ b/test/mw_engine/utils_test.clj
@@ -0,0 +1,69 @@
+(ns mw-engine.utils-test
+  (:use [mw-engine.core :as core] 
+        [mw-engine.world :as world]
+        [mw-engine.heightmap :as height]
+        [mw-engine.natural-rules :as rules])
+  (:require [clojure.test :refer :all]
+            [mw-engine.utils :refer :all]))
+
+(deftest get-neighbours-test
+  (testing "Gross functionality of get-neighbours: checks the right number of 
+            neighbours returned, doesn't actually check they're the right ones."
+           (let [world (make-world 9 9)
+                 corner (get-cell world 0 0)
+                 midside (get-cell world 0 4)
+                 centre (get-cell world 4 4)]
+             (is (= (count (get-neighbours world corner 1)) 3))
+             (is (= (count (get-neighbours world midside 1)) 5))
+             (is (= (count (get-neighbours world centre 1)) 8))
+             (is (= (count (get-neighbours world corner 2)) 8))
+             (is (= (count (get-neighbours world midside 2)) 14))
+             (is (= (count (get-neighbours world centre 2)) 24))
+             (is (= (count (get-neighbours world corner 3)) 15))
+             (is (= (count (get-neighbours world midside 3)) 27))
+             (is (= (count (get-neighbours world centre 3)) 48))
+             (is (= (count (get-neighbours world corner 4)) 24))
+             (is (= (count (get-neighbours world midside 4)) 44))
+             (is (= (count (get-neighbours world centre 4)) 80))
+             )))
+             
+
+(deftest get-neighbours-with-property-value-test
+  (testing "Testing the action of the over-complicated utility function"
+           (let [world '(({ :altitude 13, :x 0, :y 0, }
+                           { :altitude 20, :x 1, :y 0, }
+                           { :altitude 29, :x 2, :y 0, }
+                           { :altitude 39, :x 3, :y 0, }
+                           { :altitude 51, :x 4, :y 0, })
+                          ({ :altitude 19, :x 0, :y 1, }
+                            { :altitude 29, :x 1, :y 1, }
+                            { :altitude 41, :x 2, :y 1, }
+                            { :altitude 55, :x 3, :y 1, }
+                            { :altitude 72, :x 4, :y 1, })
+                          ({ :altitude 27, :x 0, :y 2, }
+                            { :altitude 41, :x 1, :y 2, }
+                            { :altitude 55, :x 2, :y 2, }
+                            { :altitude 72, :x 3, :y 2, }
+                            { :altitude 91, :x 4, :y 2, })
+                          ({ :altitude 33, :x 0, :y 3, }
+                            { :altitude 47, :x 1, :y 3, }
+                            { :altitude 68, :x 2, :y 3, }
+                            { :altitude 91, :x 3, :y 3, }
+                            { :altitude 111, :x 4, :y 3, })
+                          ({ :altitude 36, :x 0, :y 4, }
+                            { :altitude 53, :x 1, :y 4, }
+                            { :altitude 75, :x 2, :y 4, }
+                            { :altitude 100, :x 3, :y 4, }
+                            { :altitude 123, :x 4, :y 4, }))]
+                 (is (= (get-neighbours-with-property-value world 3 3 1 :altitude 100 >) 
+                       '({ :altitude 111, :x 4, :y 3, } 
+                         { :altitude 123, :x 4, :y 4, })))
+                 (is (= (get-neighbours-with-property-value world 3 3 1 :altitude 100 <)
+                       '({ :altitude 55, :x 2, :y 2, } 
+                          { :altitude 68, :x 2, :y 3, } 
+                          { :altitude 75, :x 2, :y 4, } 
+                          { :altitude 72, :x 3, :y 2, } 
+                          { :altitude 91, :x 4, :y 2, })))
+                 (is (= (get-neighbours-with-property-value world 3 3 1 :altitude 100)
+                       '({ :altitude 100, :x 3, :y 4, }))))))
+
diff --git a/test/mw_engine/world_test.clj b/test/mw_engine/world_test.clj
new file mode 100644
index 0000000..7d50a3b
--- /dev/null
+++ b/test/mw_engine/world_test.clj
@@ -0,0 +1,28 @@
+(ns mw-engine.world-test
+  (:require [clojure.test :refer :all]
+            [mw-engine.world :refer :all]
+            [clojure.math.combinatorics :as combo]))
+
+(deftest genesis-test
+  (testing "World creation."
+           (is 
+             (empty?
+               (remove 
+                 true? 
+                 (flatten
+                   (map 
+                     (fn [size] 
+                       (let [world (make-world size size)]
+                         (is (= (count world) size)
+                             "World should be NxN matrix")
+                         (is (empty? (remove #(= % size) (map count world)))
+                             "World should be NxN matrix")
+                         (map #(let [[x y] %
+                                     cell (nth (nth world y) x)]
+                                 (is (= (:x cell) x) "Checking x coordinate")
+                                 (is (= (:y cell) y) "Checking y coordinate")
+                                 (is (= (:state cell) :new) "Checking state is new"))
+                              (combo/cartesian-product (range size) (range size)))
+                         ))
+                     (range 1 10)))))
+             "Comprehensive new world test")))
\ No newline at end of file