diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bff2b65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ + + +resources/public/docs/mw-*/ diff --git a/resources/public/docs/mw-engine/uberdoc.html b/resources/public/docs/mw-engine/uberdoc.html deleted file mode 100644 index 72c4e57..0000000 --- a/resources/public/docs/mw-engine/uberdoc.html +++ /dev/null @@ -1,3033 +0,0 @@ - -mw-engine -- Marginalia

mw-engine

0.1.2-SNAPSHOT


Cellular automaton world builder.

-

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



(this space intentionally left almost blank)
 
\ No newline at end of file diff --git a/resources/public/docs/mw-parser/uberdoc.html b/resources/public/docs/mw-parser/uberdoc.html deleted file mode 100644 index 756b2c6..0000000 --- a/resources/public/docs/mw-parser/uberdoc.html +++ /dev/null @@ -1,3412 +0,0 @@ - -mw-parser -- Marginalia

mw-parser

0.1.3-SNAPSHOT


Parser for production rules for MicroWorld engine

-

dependencies

org.clojure/clojure
1.5.1
org.clojure/tools.trace
0.7.8
mw-engine
0.1.3-SNAPSHOT



(this space intentionally left almost blank)
 

parse multiple rules from a stream, possibly a file - although the real -objective is to parse rules out of a block of text from a textarea

-
-
(ns mw-parser.bulk
-  (:use mw-parser.core
-        mw-engine.utils
-        clojure.java.io
-        [clojure.string :only [split trim]])
-  (:import (java.io BufferedReader StringReader)))

Is this line a comment?

-
(defn comment? 
-  [line]
-  (or (empty? (trim line)) (member? (first line) '(nil \# \;))))

Parse rules from successive lines in this string, assumed to have multiple - lines delimited by the new-line character. Return a list of S-expressions.

-
(defn parse-string 
-  [string]
-        ;; TODO: tried to do this using with-open, but couldn't make it work.
-  (map parse-rule (remove comment? (split string #"\n"))))

Parse rules from successive lines in the file loaded from this filename. - Return a list of S-expressions.

-
(defn parse-file 
-  [filename]
-  (parse-string (slurp filename)))

Compile each non-comment line of this string into an executable anonymous - function, and return the sequence of such functions.

-
(defn compile-string
-  [string]
-  (map #(compile-rule % true) (remove comment? (split string #"\n"))))

Compile each non-comment line of the file indicated by this filename into - an executable anonymous function, and return the sequence of such functions.

-
(defn compile-file 
-  [filename]
-  (compile-string (slurp filename)))
 

A very simple parser which parses production rules of the following forms:

- -
    -
  • "if altitude is less than 100 and state is forest then state should be climax and deer should be 3"
  • -
  • "if altitude is 100 or fertility is 25 then state should be heath and fertility should be 24.3"
  • -
  • "if altitude is 100 or fertility is 25 then state should be heath"
  • -
  • "if deer is more than 2 and wolves is 0 and fertility is more than 20 then deer should be deer + 2"
  • -
  • "if deer is more than 1 and wolves is more than 1 then deer should be deer - wolves"
  • -
  • "if state is grassland and 4 neighbours have state equal to water then state should be village"
  • -
  • "if state is forest and fertility is between 55 and 75 then state should be climax"
  • -
  • "if 6 neighbours have state equal to water then state should be village"
  • -
  • "if state is in grassland or pasture or heath and 4 neighbours are water then state should be village"
  • -
  • "if state is forest or state is climax and some neighbours have state equal to fire then 3 in 5 chance that state should be fire"
  • -
  • "if state is pasture and more than 3 neighbours have state equal to scrub then state should be scrub"
  • -* -
- -

it generates rules in the form expected by mw-engine.core, q.v.

- -

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
-        [clojure.string :only [split trim triml]])
-  (:gen-class))
-
(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.]*$")

error thrown when an attempt is made to set a reserved property

-
(def reserved-properties-error 
-  "The properties 'x' and 'y' of a cell are reserved and should not be set in rule actions")

error thrown when a rule cannot be parsed

-
(def bad-parse-error "I did not understand '%s'")

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

- -
    -
  1. A code fragment parsed from the front of the sequence of tokens, and
  2. -
  3. the remaining tokens which were not consumed in constructing that fragment.
  4. -
- -

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 token assumed to be a simple token value.

-
(defn parse-token-value
-  [[value & remainder]]
-  (if value [(keyword value) 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-token-value tokens))))
-  ([tokens]
-    (parse-simple-value tokens false)))

Parse a single value from this single token and return just the generated - code, not a pair.

-
(defn gen-token-value
-  [token expect-int]
-  (first (parse-simple-value (list token) expect-int)))

Parse a list of values from among these tokens. If expect-int is true, return - integers or things which will evaluate to integers.

-
(defn parse-disjunct-value
-  [[OR token & tokens] expect-int]
-  (cond (member? OR '("or" "in"))
-    (let [value (first (parse-simple-value (list token) expect-int))
-          seek-others (= (first tokens) "or")]
-      (cond seek-others
-        (let [[others remainder] (parse-disjunct-value tokens expect-int)]
-          [(cons value others) remainder])
-        true
-        [(list value) 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 expect-int)))
-  ([tokens]
-    (parse-value tokens false)))

Parses a condition of the form '[property] in [value] or [value]...'

-
(defn parse-member-condition
-  [[property IS IN & rest]]
-  (if (and (member? IS '("is" "are")) (= IN "in"))
-    (let [[l remainder] (parse-disjunct-value (cons "in" rest) false)]
-      [(list 'member? (list (keyword property) 'cell) (list 'quote l)) remainder])))

Parse '[property] less than [value]'.

-
(defn- parse-less-condition
-  [[property IS LESS THAN & rest]]
-  (cond (and (member? IS '("is" "are")) (member? LESS '("less" "fewer")) (= THAN "than"))
-    (let [[value remainder] (parse-value rest true)]
-        [(list '< (list 'get-int 'cell (keyword property)) value) remainder])))

Parse '[property] more than [value]'.

-
(defn- parse-more-condition
-  [[property IS MORE THAN & rest]]
-  (cond (and (member? IS '("is" "are")) (member? MORE '("more" "greater")) (= THAN "than"))
-    (let [[value remainder] (parse-value rest true)]
-        [(list '> (list 'get-int 'cell (keyword property)) value) remainder])))
-
(defn- parse-between-condition
-  [[p IS BETWEEN v1 AND v2 & rest]]
-  (cond (and (member? IS '("is" "are")) (= 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 
-    (member? IS '("is" "are"))
-    (let [tokens (cons property (cons value rest))]
-      (cond 
-        (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])))))
-
(defn- gen-neighbours-condition
-  ([comp1 quantity property value remainder comp2 distance] 
-    [(list comp1  
-         (list 'count
-               (list 'get-neighbours-with-property-value 'world 
-                     '(cell :x) '(cell :y) distance 
-                     (keyword property) (keyword-or-numeric value) comp2))
-         quantity)
-           remainder])
-  ([comp1 quantity property value remainder comp2]
-    (gen-neighbours-condition comp1 quantity property value remainder comp2 1)))

Parse conditions of the form '...more than 6 neighbours are [condition]'

-
(defn parse-comparator-neighbours-condition
-  [[MORE THAN n NEIGHBOURS WITHIN distance have-or-are & rest]]
-  (let [quantity (first (parse-numeric-value (list n)))
-        comparator (cond (= MORE "more") '>
-                     (member? MORE '("fewer" "less")) '<)]       
-    (cond
-      (not (= WITHIN "within"))
-      (parse-comparator-neighbours-condition 
-        (flatten 
-          ;; two tokens were mis-parsed as 'within distance' that weren't
-          ;; actually 'within' and a distance. Splice in 'within 1' and try
-          ;; again.
-          (list MORE THAN n NEIGHBOURS "within" "1" WITHIN distance have-or-are rest)))
-      (and quantity 
-           comparator
-           (= THAN "than")
-           (= NEIGHBOURS "neighbours"))
-      (cond
-        (= have-or-are "are") 
-        (let [[value & remainder] rest
-              dist (gen-token-value distance true)]
-          (gen-neighbours-condition comparator quantity :state value remainder = dist))
-        (= have-or-are "have")
-        (let [[property comp1 comp2 value & remainder] rest
-              dist (gen-token-value distance true)]
-          (cond (and (= comp1 "equal") (= comp2 "to"))
-            (gen-neighbours-condition comparator quantity property 
-                                      value remainder = dist)
-            (and (= comp1 "more") (= comp2 "than"))
-            (gen-neighbours-condition comparator quantity property 
-                                      value remainder > dist)
-            (and (= comp1 "less") (= comp2 "than"))
-            (gen-neighbours-condition comparator quantity property 
-                                      value remainder < dist)))))))
-
(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 WITHIN distance have-or-are & rest]]
-  (let [quantity (first (parse-numeric-value (list n)))]       
-    (cond
-      (and quantity (= NEIGHBOURS "neighbours"))
-      (cond
-        (not (= WITHIN "within"))
-        (parse-simple-neighbours-condition
-          (flatten 
-            ;; two tokens were mis-parsed as 'within distance' that weren't
-            ;; actually 'within' and a distance. Splice in 'within 1' and try
-            ;; again.
-            (list n NEIGHBOURS "within" "1" WITHIN distance have-or-are rest)))
-        (= have-or-are "are") 
-        (let [[value & remainder] rest
-              dist (gen-token-value distance true)]
-          (gen-neighbours-condition '= quantity :state value remainder = dist))
-        (= have-or-are "have")
-        (let [[property comp1 comp2 value & remainder] rest
-              dist (gen-token-value distance true)]
-          (cond (and (= comp1 "equal") (= comp2 "to"))
-            (gen-neighbours-condition '= quantity property value remainder = 
-                                      dist)
-            (and (= comp1 "more") (= comp2 "than"))
-            (gen-neighbours-condition '= quantity property value remainder > 
-                                      dist)
-            (and (= comp1 "less") (= comp2 "than"))
-            (gen-neighbours-condition '= quantity property value remainder < 
-                                      dist)))))))

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-neighbours-condition tokens)
-    (parse-member-condition tokens)
-    (parse-not-condition tokens)
-    (parse-less-condition tokens)
-    (parse-more-condition tokens)
-    (parse-between-condition tokens)
-    (parse-is-condition tokens)))

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.

-
(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'.

-
(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.

-
(defn- parse-left-hand-side
- [[IF & tokens]]
- (if
-   (= IF "if")
-   (parse-conditions 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'.

-
(defn- parse-arithmetic-action 
-  [previous [prop1 SHOULD BE prop2 operator value & rest]]
-  (cond
-    (member? prop2 '("x" "y"))
-    (throw 
-      (Exception. reserved-properties-error))
-    (and (= SHOULD "should")
-           (= BE "be")
-           (member? operator '("+" "-" "*" "/")))
-    [(list 'merge (or previous 'cell)
-           {(keyword prop1) (list 'int 
-                                  (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].'

-
(defn- parse-set-action 
-  [previous [property SHOULD BE value & rest]]
-  (cond 
-    (member? property '("x" "y"))
-    (throw 
-      (Exception. reserved-properties-error))
-    (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.

-
(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 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
-  [[THEN & tokens]]
-  (if (= THEN "then")
-    (or
-      (parse-probability nil tokens)
-      (parse-actions nil tokens))))

Parse a complete rule from this line, expected to be either a string or a - sequence of string tokens. Return the rule in the form of an S-expression.

- -

Throws an exception if parsing fails.

-
(defn parse-rule 
-  [line]
-  (cond
-   (string? line) 
-   (let [rule (parse-rule (split (triml line) #"\s+"))]
-     (cond rule rule
-       true (throw (Exception. (format bad-parse-error line)))))
-   true 
-   (let [[left remainder] (parse-left-hand-side line)
-              [right junk] (parse-right-hand-side remainder)]
-     (cond 
-       ;; there should be a valide left hand side and a valid right hand side
-       ;; there shouldn't be anything left over (junk should be empty)
-       (and left right (empty? junk))
-       (list 'fn ['cell 'world] (list 'if left right))))))

Parse this rule-text, a string conforming to the grammar of MicroWorld rules, - into Clojure source, and then compile it into an anonymous - function object, getting round the problem of binding mw-engine.utils in - the compiling environment. If return-tuple? is present and true, return - a list comprising the anonymous function compiled, and the function from - which it was compiled.

- -

Throws an exception if parsing fails.

-
(defn compile-rule 
-  ([rule-text return-tuple?]
-    (do
-      (use 'mw-engine.utils)
-      (let [afn (eval (parse-rule rule-text))]
-        (cond 
-          (and afn return-tuple?)(list afn (trim rule-text))
-          true afn))))
-  ([rule-text]
-    (compile-rule rule-text false)))
 
\ No newline at end of file diff --git a/resources/public/docs/mw-ui/uberdoc.html b/resources/public/docs/mw-ui/uberdoc.html deleted file mode 100644 index fccdf70..0000000 --- a/resources/public/docs/mw-ui/uberdoc.html +++ /dev/null @@ -1,3410 +0,0 @@ - -mw-ui -- Marginalia

mw-ui

0.1.3-SNAPSHOT


Web-based user interface for MicroWorld

-

dependencies

org.clojure/clojure
1.6.0
mw-engine
0.1.3-SNAPSHOT
mw-parser
0.1.3-SNAPSHOT
lib-noir
0.8.4
ring-server
0.3.1
selmer
0.6.8
com.taoensso/timbre
3.2.1
com.taoensso/tower
2.0.2
markdown-clj
0.9.44
environ
0.5.0
noir-exception
0.2.2



(this space intentionally left almost blank)
 
-
(ns mw-ui.handler
-  (:require [compojure.core :refer [defroutes]]
-            [mw-ui.routes.home :refer [home-routes]]
-            [mw-ui.middleware :refer [load-middleware]]
-            [noir.response :refer [redirect]]
-            [noir.util.middleware :refer [app-handler]]
-            [compojure.route :as route]
-            [taoensso.timbre :as timbre]
-            [taoensso.timbre.appenders.rotor :as rotor]
-            [selmer.parser :as parser]
-            [environ.core :refer [env]]))
-
(defroutes app-routes
-  (route/resources "/")
-  (route/not-found "Not Found"))

init will be called once when - app is deployed as a servlet on - an app server such as Tomcat - put any initialization code here

-
(defn init
-  []
-  (timbre/set-config!
-    [:appenders :rotor]
-    {:min-level :info
-     :enabled? true
-     :async? false ; should be always false for rotor
-     :max-message-per-msecs nil
-     :fn rotor/appender-fn})
-  (timbre/set-config!
-    [:shared-appender-config :rotor]
-    {:path "mw_ui.log" :max-size (* 512 1024) :backlog 10})
-  (if (env :dev) (parser/cache-off!))
-  (timbre/info "mw-ui started successfully"))

destroy will be called when your application - shuts down, put any clean up code here

-
(defn destroy
-  []
-  (timbre/info "mw-ui is shutting down..."))
-
(def app (app-handler
-           ;; add your application routes here
-           [home-routes app-routes]
-           ;; add custom middleware here
-           :middleware (load-middleware)
-           ;; timeout sessions after 30 minutes
-           :session-options {:timeout (* 60 30)
-                             :timeout-response (redirect "/")}
-           ;; add access rules here
-           :access-rules []
-           ;; serialize/deserialize the following data formats
-           ;; available formats:
-           ;; :json :json-kw :yaml :yaml-kw :edn :yaml-in-html
-           :formats [:json-kw :edn]))
 
-
(ns mw-ui.layout
-  (:require [selmer.parser :as parser]
-            [clojure.string :as s]
-            [ring.util.response :refer [content-type response]]
-            [compojure.response :refer [Renderable]]))
-
(def template-path "templates/")
-
(deftype RenderableTemplate [template params]
-  Renderable
-  (render [this request]
-    (content-type
-      (->> (assoc params
-                  (keyword (s/replace template #".html" "-selected")) "active"
-                  :servlet-context
-                  (if-let [context (:servlet-context request)]
-                    (.getContextPath context)))
-        (parser/render-file (str template-path template))
-        response)
-      "text/html; charset=utf-8")))
-
(defn render [template & [params]]
-  (RenderableTemplate. template params))
 
-
(ns mw-ui.middleware
-  (:require [taoensso.timbre :as timbre]
-            [selmer.parser :as parser]
-            [environ.core :refer [env]]
-            [selmer.middleware :refer [wrap-error-page]]
-            [noir-exception.core
-              :refer [wrap-internal-error wrap-exceptions]]))
-
(defn log-request [handler]
-  (fn [req]
-    (timbre/debug req)
-    (handler req)))
-
(def development-middleware
-  [log-request
-   wrap-error-page
-   wrap-exceptions])
-
(def production-middleware
-  [#(wrap-internal-error % :log (fn [e] (timbre/error e)))])
-
(defn load-middleware []
-  (concat (when (env :dev) development-middleware)
-          production-middleware))
 
-
(ns mw-ui.render-world
-  (:require [clojure.java.io :as jio]
-            [mw-engine.core :as engine]
-            [mw-engine.world :as world]
-            [mw-engine.heightmap :as heightmap]
-            [mw-parser.bulk :as compiler]
-            [hiccup.core :refer [html]]
-            [noir.io :as io]
-            [noir.session :as session]))
-
(defn format-css-class [statekey]
-  "Format this statekey, assumed to be a keyword indicating a state in the
-   world, into a CSS class"
-  (subs (str statekey) 1))

Render this statekey, assumed to be a keyword indicating a state in the - world, into a path which should recover the corresponding image file.

-
(defn format-image-path
-  [statekey]
-  (format "img/tiles/%s.png" (format-css-class statekey)))
-
(defn format-mouseover [cell]
-  (str cell))

Render this world cell as a Hiccup table cell.

-
(defn render-cell
-  [cell]
-  (let [state (:state cell)]
-    [:td {:class (format-css-class state) :title (format-mouseover cell)}
-     [:a {:href (format "inspect?x=%d&y=%d" (:x cell) (:y cell))}       
-      [:img {:alt (:state cell) :src (format-image-path state)}]]]))

Render this world row as a Hiccup table row.

-
(defn render-world-row
-  [row]
-  (apply vector (cons :tr (map render-cell row))))

Render the world implied by the current session as a complete HTML table in a DIV.

-
(defn render-world-table
-  []
-  (let [world (or (session/get :world)
-                  (heightmap/apply-heightmap 
-                      (io/get-resource "/img/heightmaps/small_hill.png")))
-        rules (or (session/get :rules) 
-                  (do (session/put! :rules 
-                                    (compiler/compile-file 
-                                      (io/get-resource "/rulesets/basic.txt")))
-                    (session/get :rules)))
-        generation (+ (or (session/get :generation) 0) 1)
-        w2 (engine/transform-world world rules)
-        ]
-    (session/put! :world w2)
-    (session/put! :generation generation)
-    [:div {:class "world"}
-      (apply vector
-                 (cons :table
-                       (map render-world-row w2)))
-      [:p 
-       (str "Generation " generation)]]))
-
(defn render-inspector
-  [cell table]
-  [:table {:class "music-ruled"}
-   [:tr 
-    [:td {:colspan 2 :style "text-align: center;"} 
-     [:img {:src (str "img/tiles/" (name (:state cell)) ".png")
-            :width 64
-            :height 64}]]]
-   [:tr [:th "Key"][:th "Value"]]
-   (map #(vector :tr (vector :th %)(vector :td (cell %))) (keys cell))])
 
-
(ns mw-ui.repl
-  (:use mw-ui.handler
-        ring.server.standalone
-        [ring.middleware file-info file])
-  (:gen-class))
-
(defonce server (atom nil))
-
(defn get-handler []
-  ;; #'app expands to (var app) so that when we reload our code,
-  ;; the server is forced to re-resolve the symbol in the var
-  ;; rather than having its own copy. When the root binding
-  ;; changes, the server picks it up without having to restart.
-  (-> #'app
-      ; Makes static assets in $PROJECT_DIR/resources/public/ available.
-      (wrap-file "resources")
-      ; Content-Type, Content-Length, and Last Modified headers for files in body
-      (wrap-file-info)))

used for starting the server in development mode from REPL

-
(defn start-server
-  [& [port]]
-  (let [port (if port (Integer/parseInt port) 3000)]
-    (reset! server
-            (serve (get-handler)
-                   {:port port
-                    :init init
-                    :auto-reload? true
-                    :destroy destroy
-                    :join? false}))
-    (println (str "You can view the site at http://localhost:" port))))
-
(defn stop-server []
-  (.stop @server)
-  (reset! server nil))
-
(defn -main []
-  (start-server))
 
-
(ns mw-ui.routes.home
-  (:use clojure.walk
-        compojure.core
-        [mw-engine.utils :as engine-utils]
-        [mw-ui.routes.rules :as rules]
-        [mw-ui.routes.params :as params])
-  (:require [hiccup.core :refer [html]]
-            [mw-ui.layout :as layout]
-            [mw-ui.util :as util]
-            [mw-ui.render-world :as world]
-            [noir.session :as session]
-            [ring.util.response :as response]))
-
(defn home-page []
-  (layout/render "trusted-content.html" {:title "Welcome to MicroWorld" 
-                              :content (util/md->html "/md/mw-ui.md")}))
-
(defn inspect-page [request]
-  (let [params (keywordize-keys (:params request))
-        xs (:x params)
-        ys (:y params)
-        x (if (not (empty? xs)) (read-string xs) 0)
-        y (if (not (empty? ys)) (read-string ys) 0)
-        world (session/get :world)
-        cell (engine-utils/get-cell world x y)
-        state (:state params)]
-    (cond state
-      (do
-        (session/put! :world (engine-utils/set-property world cell :state (keyword state)))
-        (response/redirect "world"))
-      true
-      (layout/render "inspector.html"
-                     {:title (format "Inspect cell at %d, %d" x y)
-                      :content (html (world/render-inspector cell world))
-                      :cell cell
-                      :x (:x cell)
-                      :y (:y cell)
-                      :states (util/list-resources 
-                                "/img/tiles" #"([0-9a-z-_]+).png")}))))
-
(defn world-page []
-  (layout/render "trusted-content.html" 
-                 {:title "Watch your world grow" 
-                 :world-selected "active" 
-                 :content (html (world/render-world-table)) 
-                 :pause (or (session/get :pause) 5) 
-                 :maybe-refresh "refresh"}))
-
(defn about-page []
-  (layout/render "trusted-content.html" 
-                 {:title "About MicroWorld"
-                  :about-selected "active"  
-                  :content (util/md->html "/md/about.md")}))
-
(defn md-page [request]
-  (let [params (keywordize-keys (:params request))
-        content (or (:content params) "missing.md")]
-    (layout/render "trusted-content.html" 
-                   {:title "Welcome to MicroWorld" 
-                    :content (util/md->html (str "/md/" content))})))
-
(defn list-states []
-  (sort
-    (filter #(not (nil? %)) 
-            (map #(first (rest (re-matches #"([0-9a-z-]+).png" (.getName %))))
-                 (file-seq (clojure.java.io/file "resources/public/img/tiles"))))))
-
(defn docs-page []
-  (layout/render "docs.html" {:title "Documentation"
-                              :parser (util/md->html "/md/mw-parser.md" )
-                              :states (util/list-resources "/img/tiles" #"([0-9a-z-_]+).png")
-                              :lessons (util/list-resources "/md/lesson-plans"  #"([0-9a-z-_]+).md")
-                              :components ["mw-engine" "mw-parser" "mw-ui"]}))
-
(defroutes home-routes
-  (GET "/" [] (home-page))
-  (GET "/about" [] (about-page))
-  (GET "/docs"  [] (docs-page))
-  (GET "/world"  [] (world-page))
-  (GET "/params" [] (params/params-page))
-  (GET "/md" request (md-page request))
-  (POST "/params" request (params/params-page request))
-  (GET "/rules" request (rules/rules-page request))
-  (POST "/rules" request (rules/rules-page request))
-  (GET "/inspect" request (inspect-page request))
-  (POST "/inspect" request (inspect-page request)))
 
-
(ns mw-ui.routes.params
-  (:use clojure.walk
-        clojure.java.io
-        compojure.core)
-  (:require [hiccup.core :refer [html]]
-            [mw-engine.heightmap :as heightmap]
-            [mw-parser.bulk :as compiler]
-            [mw-ui.layout :as layout]
-            [mw-ui.util :as util]
-            [mw-ui.render-world :as world]
-            [noir.io :as io]
-            [noir.session :as session]))
-
(defn- send-params []
-  {:title "Choose your world"
-   :heightmaps (util/list-resources "/img/heightmaps" #"([0-9a-z-_]+).png")
-   :pause (or (session/get :pause) 5)
-   :rulesets (util/list-resources "/rulesets" #"([0-9a-z-_]+).txt")
-   })

Handler for params request. If no request passed, show empty params form. - If request is passed, put parameters from request into session and show - the world page.

-
(defn params-page 
-  ([]
-    (layout/render "params.html" (send-params)))
-  ([request]
-    (try
-      (let [params (keywordize-keys (:form-params request))
-            map (:heightmap params)
-            pause (:pause params)
-            rulefile (:ruleset params)
-            rulepath (str "/rulesets/" rulefile ".txt")]
-        (if (not (= map "")) 
-          (session/put! :world 
-                        (heightmap/apply-heightmap 
-                          (io/get-resource (str "/img/heightmaps/" map ".png")))))
-        (if (not (= rulefile ""))
-          (do
-            (session/put! :rule-text (io/slurp-resource rulepath))
-            (session/put! :rules (compiler/compile-file (io/get-resource rulepath)))))
-        (if (not (= pause ""))
-          (session/put! :pause pause))
-        (layout/render "params.html" 
-                       (merge (send-params) 
-                              {:r rulefile
-                               :h map
-                               :message "Your parameters are saved, now look at your world"})))
-      (catch Exception e
-        (let [params (keywordize-keys (:form-params request))]
-          (layout/render "params.html" 
-                         (merge (send-params)
-                                {:title "Choose your world" 
-                                 :r (:ruleset params)
-                                 :h (:heightmap params)
-                                 :message "Your paramters are not saved"
-                                 :error (str (.getName (.getClass e)) ": " (.getMessage e) "; " params)})))))))
 
-
(ns mw-ui.routes.rules
-  (:use clojure.walk
-        compojure.core)
-  (:require [hiccup.core :refer [html]]
-            [mw-parser.bulk :as compiler]
-            [mw-ui.layout :as layout]
-            [mw-ui.util :as util]
-            [mw-ui.render-world :as world]
-            [noir.io :as io]
-            [noir.session :as session]
-            [ring.util.response :as response]))
-
(defn process-rules-request
-  [request]
-  (let [src (:src (keywordize-keys (:form-params request)))]
-      (try 
-        (cond src 
-          (let [rules (compiler/compile-string src)]
-            {:rule-text src
-             :rules rules
-             :message (str "Successfully compiled " 
-                           (count rules) 
-                           " rules")           })
-          true {:rule-text (or 
-                             (session/get :rule-text) 
-                             (io/slurp-resource "/rulesets/basic.txt"))
-                :message "No rules found in request; loading defaults"})
-        (catch Exception e 
-          {:rule-text src
-           :message "An error occurred during compilation"
-           :error (str (.getName (.getClass e)) ": " (.getMessage e))}))))

Request handler for the rules request. If the request contains a value - for :src, treat that as rule source and try to compile it. If compilation - succeeds, stash the compiled rules and the rule text on the session, and - provide feedback; if not, provide feedback.

- -

If request doesn't contain a value for :src, load basic rule source from - the session or from resources/rulesets/basic.txt and pass that back.

-
(defn rules-page 
-  ([request]
-    (let [processed (process-rules-request request)]
-      (if (:rules processed)
-        (session/put! :rules (:rules processed)))
-      (if (:rule-text processed)
-        (session/put! :rule-text (:rule-text processed)))
-      (layout/render "rules.html" 
-                     (merge {:title "Edit Rules"} processed))))
-  ([]
-    (rules-page nil)))
 
-
(ns mw-ui.util
-  (:require [noir.io :as io]
-            [noir.session :as session]
-            [markdown.core :as md]))

reads a markdown file from public/md and returns an HTML string

-
(defn md->html
-  [filename]
-  (->>
-    (io/slurp-resource filename)
-    (md/md-to-html-string)))
-
(defn list-resources [directory pattern]
-  "List resource files matching `pattern` in `directory`."
-  (let 
-    [path (str (io/resource-path) directory)]
-    (session/put! :list-resources-path path)
-    (sort
-      (filter #(not (nil? %)) 
-            (map #(first (rest (re-matches pattern (.getName %))))
-                 (file-seq (clojure.java.io/file path)))))))
 
\ No newline at end of file diff --git a/src/mw_ui/render_world.clj b/src/mw_ui/render_world.clj index b6623ce..9d103d5 100644 --- a/src/mw_ui/render_world.clj +++ b/src/mw_ui/render_world.clj @@ -47,7 +47,7 @@ (compiler/compile-file (io/get-resource "/rulesets/basic.txt"))) (session/get :rules))) - generation (+ (or (session/get :generation) 0) 1) + generation (inc (or (session/get :generation) 0)) w2 (engine/transform-world world rules) ] (session/put! :world w2) diff --git a/src/mw_ui/routes/home.clj b/src/mw_ui/routes/home.clj index a5f5441..ac779aa 100644 --- a/src/mw_ui/routes/home.clj +++ b/src/mw_ui/routes/home.clj @@ -46,8 +46,8 @@ (let [params (keywordize-keys (:params request)) xs (:x params) ys (:y params) - x (if (not (empty? xs)) (read-string xs) 0) - y (if (not (empty? ys)) (read-string ys) 0) + x (if (seq xs) (read-string xs) 0) + y (if (seq ys) (read-string ys) 0) world (session/get :world) cell (engine-utils/get-cell world x y) state (:state params)] @@ -84,6 +84,7 @@ :pause (or (session/get :pause) 5) :maybe-refresh "refresh"})) + (defroutes home-routes (GET "/" [] (home-page)) (GET "/about" [] (about-page)) diff --git a/src/mw_ui/routes/params.clj b/src/mw_ui/routes/params.clj index 913a1bd..fe05161 100644 --- a/src/mw_ui/routes/params.clj +++ b/src/mw_ui/routes/params.clj @@ -31,15 +31,14 @@ pause (:pause params) rulefile (:ruleset params) rulepath (str "/rulesets/" rulefile ".txt")] - (if (not (= map "")) + (if (not= map "") (session/put! :world (heightmap/apply-heightmap (io/get-resource (str "/img/heightmaps/" map ".png"))))) - (if (not (= rulefile "")) - (do - (session/put! :rule-text (io/slurp-resource rulepath)) - (session/put! :rules (compiler/compile-file (io/get-resource rulepath))))) - (if (not (= pause "")) + (when (not= rulefile "") + (session/put! :rule-text (io/slurp-resource rulepath)) + (session/put! :rules (compiler/compile-file (io/get-resource rulepath)))) + (if (not= pause "") (session/put! :pause pause)) (layout/render "params.html" (merge (send-params) diff --git a/src/mw_ui/util.clj b/src/mw_ui/util.clj index 0956239..46def67 100644 --- a/src/mw_ui/util.clj +++ b/src/mw_ui/util.clj @@ -16,6 +16,6 @@ [path (str (io/resource-path) directory)] (session/put! :list-resources-path path) (sort - (filter #(not (nil? %)) + (remove nil? (map #(first (rest (re-matches pattern (.getName %)))) (file-seq (clojure.java.io/file path)))))))