diff --git a/README.md b/README.md
index a11cb06..9375cea 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,140 @@
-# mw-ui
+## What this is about
-FIXME
+MicroWorld is a rule driven cellular automaton. What does that mean? Well, it's
+a two dimensional world made up of squares called **cells**. The world develops
+in steps, and at each step, each cell is modified by applying the rules.
-## Prerequisites
+[Go and watch](world) it running for a few moments, then come back here.
-You will need [Leiningen][1] 2.0 or above installed.
+The demonstration world is a mountain, with snow at the top and the sea at the
+bottom. as you watched, you probably saw the bright green of grass on the lower
+slopes of the mountain turn to the darker green of forest. You may have seen
+some forest fires break out.
-## Running
+That's all controlled by rules. You make the rules. To start Noah's flood,
+[go to the rules page](rules) now, and add this rule at the very top:
-To start a web server for the application, run:
+ if altitude is less than 200 then state should be water
+
+then, [go and watch the world](world) again. What happens? You should see water
+spread across everywhere except the very top of the mountain. But after the
+flood, the waters should drain away again. Go back to [rules](rules) and add
+this rule at the very top:
+
+ if altitude is more than 9 and state is water then state should be grassland
+
+Now the world alternates between *new* and *grassland*. That's no good! Go back to
+[rules](rules) and delete the rule that you first added - the one that says
+
+ if altitude is less than 200 then state should be water
+
+And see! The world starts growing again.
+
+## What you can do next
+
+### Change some rules
+
+Change some of the other rules and see what happens. Very likely, one of the
+first things that will happen is that you will get a message like this:
+
+ I did not understand 'if state is grassland then 1 chance in 10 state will be heath'
+
+That means that you changed a rule in a way that the engine could no longer
+understand it. To find out what the engine will understand, have a look at the
+[documentation](docs#grammar).
+
+### Invent some rules of your own!
+
+What happens when people come into the world? Where would they make their first
+camp? Would they want to be near the water, so they could fish? Would they want
+be near fertile grassland, to graze their sheep and cattle?
+
+__Write a rule which adds some camps to the world__
+
+What happens to the land around a camp? Do the people burn down forest to make
+new grassland? Do they convert the grassland into meadow, or into crop?
+
+Does growing crops reduce the soil fertility? What makes people decide that their
+camp is a good enough place to build a proper house?
+
+__Write some rules which describe this__
+
+How many squares of meadow or crop does it take to feed each house full of people?
+What happens when there are too many houses and not enough fields? Can houses
+catch fire? What happens to a house which is next to a fire?
+
+How many houses do you need for a market place? Where would people build a
+harbour?
+
+### Change the rules completely
+
+I've provided rules which use the MicroWorld cellular automaton to make a simple
+model of the changes to land in Europe after the ice age. But you don't have to
+use it like that, at all.
+
+[Conway's Game of Life](http://en.wikipedia.org/wiki/Conway's_Game_of_Life) is one
+of the famous uses of a cellular automaton. The rules for the Game of Life are
+very simple. To set up your game of life you'll need some initialisation rules,
+one for every cell you want to start live (we'll use _black_ for live, and
+_white_ for dead):
+
+ if x is equal to 4 and y is equal to 4 and state is new then state should be black
+
+Add as many of these as you need for your starting pattern. Then add a rule, after
+all those:
+
+ if state is new then state should be white
+
+I'll leave you to work out what the rules of life are for yourself, from the
+Wiki page I linked to.
+
+### Change the engine
+
+If you want to modify the engine itself, you will need
+[Leiningen](https://github.com/technomancy/leiningen) 2.0 or above installed on
+your own computer, and you'll need to download the source, and unpack it.
+
+You will find that there are three packages:
+
++ __mw-engine__ deals with creating worlds, and transforming them;
++ __mw-parser__ deals with turning the rule language into something the engine can work on;
++ __mw-ui__ is the web site which provides you with a user interface.
+
+For each of these packages, you need to run, in the root directory of the package,
+the following command:
+
+ lein install
+
+Once you've done that, you'll have everything built. To start a web server for
+the application, run the following command in the _mw-ui_ directory:
lein ring server
+Now you have it working, you can start changing things.
+
+#### Add states
+
+Adding new states is easy:
+just add new tiles in _mw-ui/resources/public/img/tiles_. The tiles should be
+png (Portable Network Graphics) files, and should be 32 pixels square. The name
+of the tile should be the name of your new state. It's good to also edit the file
+_mw-ui/resources/public/css/states.css_ to add a class for your new state.
+
+#### Change the code
+
+Once you've done all that, you're ready to be really ambitious. Change the code.
+Implement some new feature I haven't thought of, or fix bugs I've accidentally
+left in.
+
+You'll need an editor. I recommend either [NightCode](https://nightcode.info/),
+which is quite small and will run on a Raspberry Pi, or
+[LightTable](http://www.lighttable.com/), which is extremely helpful but needs
+a more powerful computer.
+
+Have fun!
+
## License
-Copyright © 2014 Simon Brooke
+Copyright © 2014 [Simon Brooke](mailto:simon@journeyman.cc)
-Distributed under the terms of the [GNU General Public License v2][2]
-
-
-[1]: https://github.com/technomancy/leiningen
-[2]: http://www.gnu.org/licenses/gpl-2.0.html
\ No newline at end of file
+Distributed under the terms of the [GNU General Public License v2](http://www.gnu.org/licenses/gpl-2.0.html)
\ No newline at end of file
diff --git a/resources/public/css/standard.css b/resources/public/css/standard.css
index e60166e..0fd8ba0 100644
--- a/resources/public/css/standard.css
+++ b/resources/public/css/standard.css
@@ -35,8 +35,8 @@ body {
width:100%;
margin: -10px;
padding: 0.25em 10%;
- position: fixed;
- z-index: 149;
+ position: fixed;
+ z-index: 149;
background-color: black;
background-repeat: no-repeat;
color: white;
diff --git a/resources/public/docs/mw-ui/uberdoc.html b/resources/public/docs/mw-ui/uberdoc.html
index 2d2ec5b..4a5d41a 100644
--- a/resources/public/docs/mw-ui/uberdoc.html
+++ b/resources/public/docs/mw-ui/uberdoc.html
@@ -3029,7 +3029,7 @@ net.brehaut.ClojureTools = (function (SH) {
};
})(SyntaxHighlighter);
mw-ui -- Marginaliadependenciesorg.clojure/clojure |
| 1.6.0 | mw-engine |
| 0.1.0-SNAPSHOT | mw-parser |
| 0.1.0-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) |
| |
| |
+dependenciesorg.clojure/clojure |
| 1.6.0 | mw-engine |
| 0.1.0-SNAPSHOT | mw-parser |
| 0.1.0-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]]
@@ -3208,18 +3208,16 @@ net.brehaut.ClojureTools = (function (SH) {
(.stop @server)
(reset! server nil)) |
| |
| |
| (ns mw-ui.routes.home
- (:use compojure.core)
+ (:use compojure.core
+ [mw-ui.routes.rules :as rules])
(: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.session :as session])) |
| (defn home-page []
- (layout/render "world.html" {:title "Watch your world grow"
- :content (html (world/render-world-table))
- :seconds (or (session/get :seconds) 5)
- :maybe-refresh "refresh"})) |
+ (layout/render "home.html" {:title "Welcome to MicroWorld"
+ :content (util/md->html "/md/mw-ui.md")})) |
| (defn world-page []
(layout/render "world.html" {:title "Watch your world grow"
:content (html (world/render-world-table))
@@ -3237,29 +3235,58 @@ net.brehaut.ClojureTools = (function (SH) {
:parser (util/md->html "/md/mw-parser.md")
:states (list-states)
:components ["mw-engine" "mw-parser" "mw-ui"]})) |
- | (defn rules-page
- ([request]
- (let [rule-text (:src request)
- error
- (try
- (do
- (if rule-text
- (session/put! :rules (compiler/compile-string rule-text)))
- (session/put! :rule-text rule-text)
- nil)
- (catch Exception e (.getMessage e)))]
- (layout/render "rules.html" {:title "Edit Rules"
- :rule-text (or (session/get :rule-text) (slurp "resources/public/rulesets/basic.txt"))
- :error error})))
- ([]
- (rules-page nil))) |
| (defroutes home-routes
(GET "/" [] (home-page))
(GET "/about" [] (about-page))
(GET "/docs" [] (docs-page))
(GET "/world" [] (world-page))
- (GET "/rules" request (rules-page request))
- (POST "/rules" request (rules-page request))) |
| |
| |
+ (GET "/rules" request (rules/rules-page request))
+ (POST "/rules" request (rules/rules-page request))) |
| |
| |
+ | (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.session :as session])) |
+ | (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)
+ (slurp "resources/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]
[markdown.core :as md])) |
reads a markdown file from public/md and returns an HTML string
diff --git a/resources/public/rulesets/basic.txt b/resources/rulesets/basic.txt
similarity index 100%
rename from resources/public/rulesets/basic.txt
rename to resources/rulesets/basic.txt
diff --git a/resources/templates/base.html b/resources/templates/base.html
index 4191f0c..6b208f3 100644
--- a/resources/templates/base.html
+++ b/resources/templates/base.html
@@ -19,7 +19,7 @@
- |