diff --git a/.gitignore b/.gitignore index a4a8d78..9c621b4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ pom.xml.asc /.lein-* /.nrepl-port /resources/js/main.js +/resources/js/test.js +/.repl diff --git a/README.md b/README.md index 2198001..2d2e621 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A Clojure implementation of the game Spanish table top game [Nonaga] [bgg]. +A playable copy is available at http://logaan.net/nonaga. + ## License Copyright © 2013 Logan Campbell diff --git a/externs/react.js b/externs/react.js new file mode 100644 index 0000000..478753e --- /dev/null +++ b/externs/react.js @@ -0,0 +1,909 @@ +/** + * @fileoverview Externs for React 0.5.1. + * + * @see http://facebook.github.io/react + * @externs + */ + +/** + * @type {Object} + * @const + */ +var React = {}; + +/** + * @type {string} + */ +React.version; + +/** + * @param {boolean} shouldUseTouch + */ +React.initializeTouchEvents = function(shouldUseTouch) {}; + +/** + * @param {Object} specification + * @return {function( + Object=, + (string|React.ReactComponent|Array.)= + ): React.ReactComponent} + */ +React.createClass = function(specification) {}; + +/** + * @param {*} componentClass + * @return {boolean} + */ +React.isValidClass = function(componentClass) {}; + +/** + * @param {React.ReactComponent} container + * @param {Element} mountPoint + * @param {Function=} callback + * @return {React.ReactComponent} + */ +React.renderComponent = function(container, mountPoint, callback) {}; + +/** + * @param {Element} container + */ +React.unmountComponentAtNode = function(container) {}; + +/** + * @param {React.ReactComponent} component + * @param {Function} callback + */ +React.renderComponentToString = function(component, callback) {}; + +/** + * Constructs a component instance of `constructor` with `initialProps` and + * renders it into the supplied `container`. + * + * @param {Function} constructor React component constructor. + * @param {Object} props Initial props of the component instance. + * @param {Element} container DOM element to render into. + * @return {React.ReactComponent} Component instance rendered in `container`. + */ +React.constructAndRenderComponent = function(constructor, props, container) {}; + +/** + * Constructs a component instance of `constructor` with `initialProps` and + * renders it into a container node identified by supplied `id`. + * + * @param {Function} componentConstructor React component constructor + * @param {Object} props Initial props of the component instance. + * @param {string} id ID of the DOM element to render into. + * @return {React.ReactComponent} Component instance rendered in the container node. + */ +React.constructAndRenderComponentByID = function(componentConstructor, props, id) {}; + +/** + * @interface + */ +React.ReactComponent = function() {}; + +/** + * @type {Object} + */ +React.ReactComponent.prototype.props; + +/** + * @type {Object} + */ +React.ReactComponent.prototype.state; + +/** + * @type {Object} + */ +React.ReactComponent.prototype.refs; + +/** + * @type {Object} + */ +React.ReactComponent.prototype.context; + +/** + * @type {Object} + * @protected + */ +React.ReactComponent.prototype.propTypes; + +/** + * @type {Object} + * @protected + */ +React.ReactComponent.prototype.contextTypes; + +/** + * @param {Object} nextProps + * @param {Function=} callback + */ +React.ReactComponent.prototype.setProps = function(nextProps, callback) {}; + +/** + * @return {Object} + */ +React.ReactComponent.prototype.getInitialState = function() {}; + +/** + * @return {Object} + */ +React.ReactComponent.prototype.getDefaultProps = function() {}; + +/** + * @return {Object} + */ +React.ReactComponent.prototype.getChildContext = function() {}; + +/** + * @return {Element} + */ +React.ReactComponent.prototype.getDOMNode = function() {}; + +/** + * @param {Object} nextProps + * @param {Function=} callback + */ +React.ReactComponent.prototype.replaceProps = function(nextProps, callback) {}; + +/** + * @param {React.ReactComponent} targetComponent + * @return {React.ReactComponent} + * @protected + */ +React.ReactComponent.prototype.transferPropsTo = function(targetComponent) {}; + +/** + * @param {Function=} callback + * @protected + */ +React.ReactComponent.prototype.forceUpdate = function(callback) {}; + +/** + * @param {Object} nextState + * @param {Function=} callback + * @protected + */ +React.ReactComponent.prototype.setState = function(nextState, callback) {}; + +/** + * @param {Object} nextState + * @param {Function=} callback + * @protected + */ +React.ReactComponent.prototype.replaceState = function(nextState, callback) {}; + +/** + * @param {Element} element + * @protected + */ +React.ReactComponent.prototype.componentDidMount = function(element) {}; + +/** + * @param {Object} nextProps + * @protected + */ +React.ReactComponent.prototype.componentWillReceiveProps = function( + nextProps) {}; + +/** + * @param {Object} nextProps + * @param {Object} nextState + * @return {boolean} + * @protected + */ +React.ReactComponent.prototype.shouldComponentUpdate = function( + nextProps, nextState) {}; + +/** + * @param {Object} nextProps + * @param {Object} nextState + * @protected + */ +React.ReactComponent.prototype.componentWillUpdate = function( + nextProps, nextState) {}; + +/** + * @return {React.ReactComponent} + * @protected + */ +React.ReactComponent.prototype.render = function() {}; + +/** + * @param {Object} prevProps + * @param {Object} prevState + * @param {Element} element + * @protected + */ +React.ReactComponent.prototype.componentDidUpdate = function( + prevProps, prevState, element) {}; + +/** + * @protected + */ +React.ReactComponent.prototype.componentWillUnmount = function() {}; + +/** + * There is no such thing like ReactAttribute, we just need to preserve + * some React attributes after advanced compilation. + * @interface + */ +React.ReactAtrribute = function() {}; + +/** + * @type {Object} + */ +React.ReactAtrribute.dangerouslySetInnerHTML; + +/** + * @type {string} + */ +React.ReactAtrribute.__html; + +/** + * @type {string} + */ +React.ReactAtrribute.key; + +/** + * @type {string} + */ +React.ReactAtrribute.ref; + +/** + * @type {Object} + * @const + */ +React.DOM = {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.a = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.article = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.abbr = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.address = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.audio = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.b = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.body = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.br = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.button = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.code = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.col = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.colgroup = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.dd = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.div = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.section = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.dl = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.dt = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.em = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.embed = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.fieldset = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.footer = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.form = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.h1 = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.h2 = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.h3 = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.h4 = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.h5 = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.h6 = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.header = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.hr = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.i = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.iframe = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.img = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.input = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.label = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.legend = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.li = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.line = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.nav = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.object = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.ol = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.optgroup = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.option = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.p = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.param = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.pre = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.select = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.small = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.source = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.span = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.sub = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.sup = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.strong = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.table = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.tbody = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.td = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.textarea = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.tfoot = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.th = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.thead = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.time = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.title = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.tr = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.u = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.ul = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.video = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.wbr = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.circle = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.g = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.path = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.polygon = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.polyline = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.rect = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.svg = function(props, children) {}; + +/** + * @param {Object=} props + * @param {...string|React.ReactComponent|Array.} children + * @return {React.ReactComponent} + * @protected + */ +React.DOM.text = function(props, children) {}; + +/** + * @typedef {function(boolean, boolean, Object, string, string, string): boolean} React.ChainableTypeChecker + */ +React.ChainableTypeChecker; + +/** + * @type {React.ChainableTypeChecker} + */ +React.ChainableTypeChecker.weak; + +/** + * @type {React.ChainableTypeChecker} + */ +React.ChainableTypeChecker.weak.isRequired; + +/** + * @type {React.ChainableTypeChecker} + */ +React.ChainableTypeChecker.isRequired; + +/** + * @type {React.ChainableTypeChecker} + */ +React.ChainableTypeChecker.isRequired.weak; + +/** + * @type {Object} + * @const + */ +React.PropTypes = { + /** @type {React.ChainableTypeChecker} */ + "array": function () {}, + /** @type {React.ChainableTypeChecker} */ + "boolean": function () {}, + /** @type {React.ChainableTypeChecker} */ + "func": function () {}, + /** @type {React.ChainableTypeChecker} */ + "number": function () {}, + /** @type {React.ChainableTypeChecker} */ + "object": function () {}, + /** @type {React.ChainableTypeChecker} */ + "string": function () {}, + /** @type {React.ChainableTypeChecker} */ + "oneOf": function () {}, + /** @type {React.ChainableTypeChecker} */ + "oneOfType": function () {}, + /** @type {React.ChainableTypeChecker} */ + "instanceOf": function () {}, + /** @type {React.ChainableTypeChecker} */ + "renderable": function () {} +}; diff --git a/project.clj b/project.clj index 5ac8713..fc76386 100644 --- a/project.clj +++ b/project.clj @@ -6,20 +6,22 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.5.1"] [org.clojure/clojurescript "6969"] - [prismatic/dommy "0.1.1"] - [com.cemerick/clojurescript.test "0.2.1"]] + [com.cemerick/clojurescript.test "0.2.1"] + [com.cemerick/piggieback "0.1.2"]] :plugins [[lein-cljsbuild "1.0.1"] [com.cemerick/clojurescript.test "0.2.1"]] + :externs ["externs/react.js"] :hooks [leiningen.cljsbuild] + :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]} :cljsbuild {:builds {:dev {:source-paths ["src"] :compiler {:output-to "resources/js/main.js" :optimizations :whitespace :pretty-print true}} :test {:source-paths ["src" "test"] - :compiler {:output-to "target/cljs/unit-test.js" + :compiler {:output-to "resources/js/test.js" :optimizations :whitespace :pretty-print true}}} :test-commands {"unit" ["phantomjs" :runner "window.literal_js_was_evaluated=true" - "target/cljs/unit-test.js"]}}) + "resources/js/test.js"]}}) diff --git a/resources/index.html b/resources/index.html index 30a72c5..96c51d1 100644 --- a/resources/index.html +++ b/resources/index.html @@ -53,7 +53,8 @@

A player wins when any of their marbles touches both other marbles.

- The current implementation does not detect wins. + The code can be found on + Github.

diff --git a/resources/test.html b/resources/test.html new file mode 100644 index 0000000..0cfa57a --- /dev/null +++ b/resources/test.html @@ -0,0 +1,17 @@ + + + + + +
+

+    
+    
+    
+  
+
diff --git a/src/nonaga/core.cljs b/src/nonaga/core.cljs
index 89a2f05..75b2403 100644
--- a/src/nonaga/core.cljs
+++ b/src/nonaga/core.cljs
@@ -22,28 +22,6 @@
        
                      [3 0]}})
 
-(def enclosing-ring
-  {:rings
-    #{   [1 4] [2 4] [3 4]  
-       [0 3]             [3 3]
-     [0 2]                   [4 2] 
-       [0 1] [1 1]       [3 1] 
-         [1 0]       [3 0]}
-  
-   :red
-    #{   [1 4]
-    
-                             [4 2] 
-     
-         [1 0]}
-  
-   :blue
-    #{               [3 4]  
-       
-     [0 2]
-       
-                     [3 0]}})
-
 (defn move-ball [game color from to]
   (update-in game [color] #(-> % (disj from) (conj to))))
 
diff --git a/src/nonaga/draw.cljs b/src/nonaga/draw.cljs
index bc100b8..b1242f2 100644
--- a/src/nonaga/draw.cljs
+++ b/src/nonaga/draw.cljs
@@ -1,160 +1,16 @@
 (ns nonaga.draw
-  (:use [nonaga.react :only [circle div p svg create-class render-component]])
-  (:require [nonaga.core :as n]
-            [nonaga.rules.ball :as b]
-            [nonaga.rules.rings :as r])
-  (:use-macros [dommy.macros :only [sel1]]))
-
-; Events
-;
-; [:turn-began :red]
-; - Clickable red marbles
-; [:marble-selected :red coords]
-; - Clickable red marbles
-; - Clickable pink marbles
-; [:marble-moved :red]
-; - Clickable moveable rings
-; [:ring-selected :ring coords]
-; - Clickable moveable rings
-; - Clickable light grey rings
-;
-; [:turn-began :blue]
-; - Clickable blue marbles
-; [:marble-selected :blue coords]
-; - Clickable blue marbles
-; - Clickable pink marbles
-; [:marble-moved :blue]
-; - Clickable moveable rings
-; [:ring-selected :ring coords]
-; - Clickable moveable rings
-; - Clickable light grey rings
-
-(defn hex->svg [[hex-x hex-y]]
-  (let [width  40
-        half (/ width 2)
-        svg-x (+ 160 (* width hex-x) (if (odd? hex-y) half 0))
-        svg-y (+ 160 (* width hex-y))]
-    [svg-x svg-y]))
-
-(defn ring
-  ([color coord] (ring color nil coord))
-  ([color click [x y :as coord]]
-   (circle {"cx"          x
-            "cy"          y
-            "r"           14
-            "fill"        "transparent"
-            "stroke"      color
-            "strokeWidth" "7px"
-            "style"       {"cursor" (if click "pointer")}
-            "onClick"     click
-            "key"         (str "ring:" x "," y)})))
-
-(defn marble
-  ([color coord] (marble color nil coord))
-  ([color click [x y :as coord]]
-   (circle {"cx"      x
-            "cy"      y
-            "r"       8
-            "fill"    (name color)
-            "onClick" click
-            "style"   {"cursor" (if click "pointer")}
-            "key"     (str "marble" x "," y)})))
-
-(def opposite
-  {:red :blue
-   :blue :red})
-
-(def light-colors
-  {:red :pink
-   :blue :lightblue})
-
-(defn update-state [component update-fn]
-  (fn []
-    (let [old-state (.-wrapper (.-state component))]
-      (.setState component #js {:wrapper (update-fn old-state)}))))
-
-(defn move-marble [component color from to]
-  (update-state component
-                #(-> % (n/move-ball color from to)
-                     (assoc :event [:marble-moved color]))))
-
-(defn draw-valid-marble-moves [component state]
-  (let [[type & event-data] (:event state)]
-    (when (= :marble-selected type)
-      (let [[color selected] event-data]
-        (map (fn [hex]
-               (marble (light-colors color)
-                       (move-marble component color selected hex)
-                       (hex->svg hex)))
-              (b/valid-destinations state selected))))))
-
-(defn start-marble-move [component color coord]
-  (update-state component #(assoc % :event [:marble-selected color coord])))
-
-(defn draw-marbles [component state color]
-  (let [etype  (get-in state [:event 0])
-        cp     (get-in state [:event 1])
-        coords (color state)
-        click  (partial start-marble-move component)]
-    (map (fn [hex svg]
-           (marble color (if (and (or (= etype :turn-began)
-                                      (= etype :marble-selected))
-                                  (= cp color))
-                           (click color hex)) svg))
-         coords (map hex->svg coords))))
-
-; This is the same as start-marble-move
-(defn ring-selected [component color coord]
-  (update-state component #(assoc % :event [:ring-selected color coord])))
-
-(defn draw-rings [component state]
-  (let [[type & event-data] (:event state)
-        coords (:rings state)]
-    (map (fn [hex svg]
-           (if (and (or (= :marble-moved type)
-                        (= :ring-selected type))
-                    (r/can-be-moved? state hex))
-             (let [[color] event-data]
-               (ring "#444" (ring-selected component color hex) svg))
-             (ring "#999" svg)))
-         coords (map hex->svg coords))))
-
-; Same as move-marble
-(defn move-ring [component color from to]
-  (update-state component
-                #(-> % (n/move-ring from to)
-                     (assoc :last-ring to)
-                     (assoc :event [:turn-began (opposite color)]))))
-
-; These things should be a multi method that dispatches on state. Or I could
-; have state records with a draw protocol.
-(defn draw-potential-rings [component state]
-  (let [[type & event-data] (:event state)
-        coords (:rings state)]
-    (if (= :ring-selected type)
-      (let [[color source] event-data
-            destinations (r/valid-destinations coords source)]
-        (map (fn [hex svg]
-               (ring "#DDD" (move-ring component color source hex) svg))
-             destinations (map hex->svg destinations))))))
-
-(def color-name
-  {:red "Red"
-   :blue "Blue"})
-
-(defmulti instructions first)
-
-(defmethod instructions :turn-began [[type color]]
-  (str (color-name color) " selects a marble to move."))
-
-(defmethod instructions :marble-selected [[type color source]]
-  (str (color-name color) " selects where to move the marble."))
-
-(defmethod instructions :marble-moved [[type color]]
-  (str (color-name color) " selects a ring to move."))
-
-(defmethod instructions :ring-selected [[type color source]]
-  (str (color-name color) " selects where to move the ring."))
+  (:use [nonaga.react :only [div p svg create-class render-component]]
+        [nonaga.draw.instructions       :only [instructions]]
+        [nonaga.draw.valid-marble-moves :only [draw-valid-marble-moves]]
+        [nonaga.draw.marbles            :only [draw-marbles]]
+        [nonaga.draw.rings              :only [draw-rings]]
+        [nonaga.draw.potential-rings    :only [draw-potential-rings]]
+        [nonaga.draw.last-ring          :only [draw-last-ring]])
+  (:require [nonaga.core :as n]))
+
+; To implement win detection it would be quite convenient to have the game
+; state in an atom. This would allow for a win detection watcher that would
+; spot that someone has one and would change the :event to end the game.
 
 (def board
   (create-class
@@ -174,12 +30,11 @@
                       (svg {:width 480 :height 480}
                            (draw-rings this state)
                            (draw-potential-rings this state)
-                           (if-let [last-ring (:last-ring state)]
-                             (ring "#000" (hex->svg last-ring)))      
+                           (draw-last-ring state)
                            (draw-marbles this state :red)
                            (draw-marbles this state :blue)
                            (draw-valid-marble-moves this state))))))))
 
 (defn start []
-  (render-component (board) (sel1 :#content)))
+  (render-component (board) (js/document.getElementById "content")))
 
diff --git a/src/nonaga/draw/instructions.cljs b/src/nonaga/draw/instructions.cljs
new file mode 100644
index 0000000..8e408cb
--- /dev/null
+++ b/src/nonaga/draw/instructions.cljs
@@ -0,0 +1,23 @@
+(ns nonaga.draw.instructions)
+
+(def color-name
+  {:red "Red"
+   :blue "Blue"})
+
+(defmulti instructions first)
+
+(defmethod instructions :turn-began [[type color]]
+  (str (color-name color) " selects a marble to move."))
+
+(defmethod instructions :marble-selected [[type color source]]
+  (str (color-name color) " selects where to move the marble."))
+
+(defmethod instructions :marble-moved [[type color]]
+  (str (color-name color) " selects a ring to move."))
+
+(defmethod instructions :ring-selected [[type color source]]
+  (str (color-name color) " selects where to move the ring."))
+
+(defmethod instructions :game-won [[type color]]
+  (str (color-name color) " wins."))
+
diff --git a/src/nonaga/draw/last_ring.cljs b/src/nonaga/draw/last_ring.cljs
new file mode 100644
index 0000000..f6cb954
--- /dev/null
+++ b/src/nonaga/draw/last_ring.cljs
@@ -0,0 +1,7 @@
+(ns nonaga.draw.last-ring
+  (:use [nonaga.draw.util :only [hex->svg ring]]))
+
+(defn draw-last-ring [state]
+  (if-let [last-ring (:last-ring state)]
+    (ring "#000" (hex->svg last-ring))))      
+
diff --git a/src/nonaga/draw/marbles.cljs b/src/nonaga/draw/marbles.cljs
new file mode 100644
index 0000000..b770c92
--- /dev/null
+++ b/src/nonaga/draw/marbles.cljs
@@ -0,0 +1,26 @@
+(ns nonaga.draw.marbles
+  (:use [nonaga.draw.util :only [hex->svg marble update-state event-type]]))
+
+(defn start-marble-move [component color coord]
+  (update-state component #(assoc % :event [:marble-selected color coord])))
+
+(defmulti draw-marbles (fn [_ state _] (event-type state)))
+
+(defmethod draw-marbles :default [component state color]
+  (let [marbles (color state)]
+    (map (partial marble color) (map hex->svg marbles))))
+
+(derive :turn-began      ::marble-selectable)
+(derive :marble-selected ::marble-selectable)
+
+(defmethod draw-marbles ::marble-selectable
+  [component {[t current-player] :event :as state} marble-color]
+  (let [coords (marble-color state)
+        click  (partial start-marble-move component)]
+    (map (fn [hex svg]
+           (marble marble-color
+                   (if (= current-player marble-color)
+                     (click marble-color hex))
+                   svg))
+         coords (map hex->svg coords))))
+
diff --git a/src/nonaga/draw/potential_rings.cljs b/src/nonaga/draw/potential_rings.cljs
new file mode 100644
index 0000000..76d13a4
--- /dev/null
+++ b/src/nonaga/draw/potential_rings.cljs
@@ -0,0 +1,26 @@
+(ns nonaga.draw.potential-rings
+  (:require [nonaga.core :as n]
+            [nonaga.rules.rings :as r])
+  (:use [nonaga.draw.util :only [hex->svg ring update-state event-type]]))
+
+(def opposite
+  {:red :blue
+   :blue :red})
+
+(defn move-ring [component color from to]
+  (update-state component
+                #(-> % (n/move-ring from to)
+                     (assoc :last-ring to)
+                     (assoc :event [:turn-began (opposite color)]))))
+
+(defmulti draw-potential-rings #(event-type %2))
+
+(defmethod draw-potential-rings :default [_ _])
+
+(defmethod draw-potential-rings :ring-selected
+  [component {[t color source] :event coords :rings}]
+  (let [destinations (r/valid-destinations coords source)]
+    (map (fn [hex svg]
+           (ring "#DDD" (move-ring component color source hex) svg))
+         destinations (map hex->svg destinations))))
+
diff --git a/src/nonaga/draw/rings.cljs b/src/nonaga/draw/rings.cljs
new file mode 100644
index 0000000..5a5be5f
--- /dev/null
+++ b/src/nonaga/draw/rings.cljs
@@ -0,0 +1,24 @@
+(ns nonaga.draw.rings
+  (:require [nonaga.rules.rings :as r])
+  (:use [nonaga.draw.util :only [hex->svg ring update-state event-type]]))
+
+(defn ring-selected [component color coord]
+  (update-state component #(assoc % :event [:ring-selected color coord])))
+
+(defmulti draw-rings #(event-type %2))
+
+(defmethod draw-rings :default
+  [component {:keys [rings]}]
+  (map (partial ring "#999") (map hex->svg rings)))
+
+(derive :marble-moved  ::ring-selectable)
+(derive :ring-selected ::ring-selectable)
+
+(defmethod draw-rings ::ring-selectable
+  [component {[t color] :event rings :rings :as state}]
+  (map (fn [hex svg]
+         (if (r/can-be-moved? state hex)
+           (ring "#444" (ring-selected component color hex) svg)
+           (ring "#999" svg)))
+       rings (map hex->svg rings)))
+
diff --git a/src/nonaga/draw/util.cljs b/src/nonaga/draw/util.cljs
new file mode 100644
index 0000000..411b9a1
--- /dev/null
+++ b/src/nonaga/draw/util.cljs
@@ -0,0 +1,43 @@
+(ns nonaga.draw.util
+  (:use [nonaga.react :only [circle]]))
+
+(defn event-type [state]
+  (first (:event state)))
+
+(defn hex->svg [[hex-x hex-y]]
+  (let [width  40
+        half (/ width 2)
+        svg-x (+ 160 (* width hex-x) (if (odd? hex-y) half 0))
+        svg-y (+ 160 (* width hex-y))]
+    [svg-x svg-y]))
+
+
+(defn ring
+  ([color coord] (ring color nil coord))
+  ([color click [x y :as coord]]
+   (circle {"cx"          x
+            "cy"          y
+            "r"           14
+            "fill"        "transparent"
+            "stroke"      color
+            "strokeWidth" "7px"
+            "style"       {"cursor" (if click "pointer")}
+            "onClick"     click
+            "key"         (str "ring:" x "," y)})))
+
+(defn marble
+  ([color coord] (marble color nil coord))
+  ([color click [x y :as coord]]
+   (circle {"cx"      x
+            "cy"      y
+            "r"       8
+            "fill"    (name color)
+            "onClick" click
+            "style"   {"cursor" (if click "pointer")}
+            "key"     (str "marble" x "," y)})))
+
+(defn update-state [component update-fn]
+  (fn []
+    (let [old-state (.-wrapper (.-state component))]
+      (.setState component #js {:wrapper (update-fn old-state)}))))
+
diff --git a/src/nonaga/draw/valid_marble_moves.cljs b/src/nonaga/draw/valid_marble_moves.cljs
new file mode 100644
index 0000000..9696a4a
--- /dev/null
+++ b/src/nonaga/draw/valid_marble_moves.cljs
@@ -0,0 +1,29 @@
+(ns nonaga.draw.valid-marble-moves
+  (:require [nonaga.rules.ball :as b]
+            [nonaga.core :as n])
+  (:use [nonaga.draw.util :only [hex->svg marble update-state event-type]]))
+
+(def light-colors
+  {:red  :pink
+   :blue :lightblue})
+
+(defn move-marble [component color from to]
+  (update-state component
+    (fn [s] (let [s' (n/move-ball s color from to) 
+                  winner (b/find-winner s)]
+      (if (nil? winner)
+        (assoc s' :event [:marble-moved color])
+        (assoc s' :event [:game-won winner]))))))
+
+(defmulti draw-valid-marble-moves #(event-type %2))
+
+(defmethod draw-valid-marble-moves :default [_ _])
+
+(defmethod draw-valid-marble-moves :marble-selected
+  [component {[t color selected] :event :as state}]
+  (map (fn [hex]
+         (marble (light-colors color)
+                 (move-marble component color selected hex)
+                 (hex->svg hex)))
+       (b/valid-destinations state selected)))
+
diff --git a/src/nonaga/rules/ball.cljs b/src/nonaga/rules/ball.cljs
index c6171d8..9f4c0f4 100644
--- a/src/nonaga/rules/ball.cljs
+++ b/src/nonaga/rules/ball.cljs
@@ -1,5 +1,5 @@
 (ns nonaga.rules.ball
-  (:use [nonaga.rules.coord :only [directions]]))
+  (:use [nonaga.rules.coord :only [directions neighbours]]))
 
 (defn invalid-space? [{:keys [rings red blue]} coord]
   (not (and (rings coord)
@@ -12,6 +12,23 @@
           :when (invalid-space? board (direction step))]
       step)))
 
+(defn find-winner [{:keys [rings red blue]}]
+  (let [all-connected
+	(fn [marble-color] 
+	  (> 
+	    (count 
+		  (reduce clojure.set/union
+		    (for [coord marble-color coord' marble-color] 
+			  (clojure.set/intersection 
+			    #{coord}
+				(neighbours coord')))))
+        2))]
+      (if (all-connected red)
+	    :red 
+		  (if (all-connected blue)
+		    :blue 
+			nil))))
+	  
 (defn valid-destinations [board coord]
   (disj (into #{} (map (partial move board coord) directions)) coord))
 
diff --git a/src/nonaga/rules/coord.cljs b/src/nonaga/rules/coord.cljs
index 720859c..535417e 100644
--- a/src/nonaga/rules/coord.cljs
+++ b/src/nonaga/rules/coord.cljs
@@ -10,6 +10,9 @@
 (def directions
   [nw ne e se sw w])
 
+(def side-by-side-directions
+  [[nw ne] [ne e] [e se] [se sw] [sw w] [w nw]])
+
 (def neighbouring-directions
   {nw [ne w]  ne [nw e]
    w  [nw sw] e  [ne se]
diff --git a/src/nonaga/rules/rings.cljs b/src/nonaga/rules/rings.cljs
index 39234da..7fe8c4c 100644
--- a/src/nonaga/rules/rings.cljs
+++ b/src/nonaga/rules/rings.cljs
@@ -1,7 +1,7 @@
 (ns nonaga.rules.rings
   (:require [clojure.set :refer [difference intersection]])
   (:use [nonaga.rules.coord :only [neighbours neighbouring-directions
-                                   tag-with-distance]]))
+                                   tag-with-distance side-by-side-directions]]))
 
 (defn valid-slide? [rings coord direction]
   (let [sliding-into-ring? (rings (direction coord))
@@ -50,10 +50,23 @@
         slidables  (filter (partial can-move-to? rings source) availables)]
     (set slidables)))
 
-; To be able to be moved must have At least a gap of two
+(defn has-double-gap? [rings coord]
+  (->
+    (->>
+      ((->> side-by-side-directions
+            (map (partial apply juxt))
+            (apply juxt))
+       coord)
+      (map set)
+      (map (partial intersection rings))
+      (filter empty?)
+      empty?
+      not)))
+
 (defn can-be-moved? [{:keys [rings red blue]} coord]
   (let [num-neighbours (count (intersection (neighbours coord) rings))]
     (and (rings coord)
          (not (or (red coord) (blue coord)))
-         (< num-neighbours 5))))
+         (< num-neighbours 5)
+         (has-double-gap? rings coord))))
 
diff --git a/test/nonaga/rules/ball_test.cljs b/test/nonaga/rules/ball_test.cljs
index 95d93bb..3ae34c5 100644
--- a/test/nonaga/rules/ball_test.cljs
+++ b/test/nonaga/rules/ball_test.cljs
@@ -1,4 +1,4 @@
-(ns nonaga.ball-test
+(ns nonaga.rules.ball-test
   (:require-macros [cemerick.cljs.test :refer (deftest is are)])
   (:use [nonaga.core :only [initial-game]]
         [nonaga.rules.ball :only [invalid-space? move valid-destinations]]
diff --git a/test/nonaga/rules/rings_test.cljs b/test/nonaga/rules/rings_test.cljs
index 29da1a3..20b89d7 100644
--- a/test/nonaga/rules/rings_test.cljs
+++ b/test/nonaga/rules/rings_test.cljs
@@ -1,9 +1,10 @@
-(ns nonaga.rules.ring-test
+(ns nonaga.rules.rings-test
   (:require-macros [cemerick.cljs.test :refer (deftest is are)])
   (:use [nonaga.core :only [initial-game]]
         [nonaga.rules.rings :only [valid-slide? valid-slides can-move-to?
-                                   valid-destinations can-be-moved?]]
-        [nonaga.rules.coord :only [nw ne e se sw w]] )
+                                   valid-destinations can-be-moved? has-double-gap?]]
+        [nonaga.rules.coord :only [nw ne e se sw w]]
+        [clojure.set :only [difference intersection]])
   (:require [cemerick.cljs.test :as t]))
 
 (def board-with-gap
@@ -83,3 +84,16 @@
   (is (can-be-moved? initial-game [2 4]))
   (is (not (can-be-moved? initial-game [2 2])))) 
 
+
+(def one-each-way
+  #{   [1 4]       [3 4]  
+           [1 3] [2 3] [3 3]
+   [0 2] [1 2] [2 2] [3 2] [4 2] 
+     [0 1] [1 1] [2 1] [3 1] 
+       [1 0] [2 0] [3 0]})
+
+(deftest has-double-gap?-test
+  (is (has-double-gap?      one-each-way [1 4]))
+  (is (not (has-double-gap? one-each-way [2 2])))
+  (is (not (has-double-gap? one-each-way [1 3]))))
+
diff --git a/test/nonaga/setup.cljs b/test/nonaga/setup.cljs
new file mode 100644
index 0000000..fa7b66e
--- /dev/null
+++ b/test/nonaga/setup.cljs
@@ -0,0 +1,9 @@
+(ns nonaga.setup)
+
+(defn set-pre! [output]
+  (let [new-text (js/document.createTextNode output)
+        output (js/document.getElementById "test-output")]
+    (.appendChild output new-text)))
+
+(set-print-fn! set-pre!)
+