sw1nn

Adventures. Stories. Code.

Clojurescript - Game of Life Pt 1

| Comments

Last night was the monthly London Clojure Dojo. The format will be familiar to anyone who’s been to a dojo. 30 odd developers meet up after work, devour large quantities of pizza and a little beer then split into groups to solve an interesting problem. The problems are suggested over pizza and via a couple of rounds of voting are narrowed down to a couple to focus on.

Last night the problems where Conway’s Game Of Life and Monte Carlo methods

The team i was allocated to decided to tackle Game Of Life. The added twist for this dojo was that the solutions were to be developed in ClojureScript

Now I’ve discussed previously there are still quite a lot of rough edges to clojurescript, so the first challenge was getting a development environment up and running. Even tho I had a working clojurescript one environment I felt it was a little heavyweight for this dojo. We opted for lein-cljsbuild instead. A quick git clone and some minor tweaks and we were up and running.

We realized that there were two possibilities for drawing the game board.

  • use canvas and the associated draw API
  • use an HTML table to represent the grid.

From my experience I knew that wrangling the DOM was going to be easier that accessing the javascript canvas api, so that’s the approach we took.

We used hiccup to generate the table on the server side. This looks this:

1
2
3
4
5
6
7
8
  (defn generate-table []
      [:table
          (for [ x (range 10) ]
               [:tr (for [y (range 10)]
                   [:td {:id (str x "-" y) :class "dead"} " "])])])

   (defn set-cell-state [x y state]
      (.setAttribute (by-id (str x "-" y)) "class" state))

Points to note:

  • in the table-generation function we are assigning the CSS class of ‘dead’. The idea is that we’ll just toggle the CSS class from live -> dead -> live as each generation progresses.
  • We assign an id to each <td> that is basically the (x,y) co-ordinate of the cell. This allows us to find the node by id and set the class attribute like this (set-cell-state 10 23 :live)

And that’s it for the server side code! It generates a table looking something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
<table id="gol-blog-example">
  <tr>
    <td class="live"></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
  </tr>
  <tr>
    ...
 </tr>
    ...
 </table>

which renders like this:

Next we move to the client side code and this, inevitably, is where those rough edges I mentioned earlier start to show through. The first problem was that clojurescript has a slightly different interop form because of the nature of javascript objects. Because methods and properties are both entries in the hash associated with the object, you need to distinguish between

  • You want to retrieve the function at a particular key
  • you want to call the function identified by the key.

this leads to these forms:

1
2
3
4
5
6
7
8
9
10
11

   (.-property o) ; access the property
   (. o -property); access the property

   (.method o <args>); call the method with args  on object
   (. o method <args> ); call the method with args on object.

   (set! (.-property o) "value") ; set the property on on to value

   (aget o "property") ; get the property when the name is a runtime value
   (aset o "property" "value") ; set the property when the name is a runtime value

I think it’s fair to say this was a significant source of confusion for everyone.

Before we ran out of time we managed to get the grid rendering some random generated data. By refreshing the page we could a least simulate the real operation!

In part 2 I’ll talk about how I took the smoke and mirrors skeleton demo’d at the dojo into a working demo.

UPDATE: read part 2

Comments