Pure Danger Tech


navigation
home

Clojure and Processing

17 Nov 2011

I had a great time at Devoxx this week – many thanks to Stephan and crew and whoever is making all this delicious beer here in Belgium.

During the end of my Cracking Clojure talk I showed a screencast I did where I progressively build up a solution to Conway’s Life in Clojure.

The end solution is completely stolen from Christophe Grand‘s elegant solution – I just filled in the work leading up to it (and refactored slightly). My end result from the ‘cast:

(defn neighbors [[x y]]
  (for [dx [-1 0 1]
        dy (if (zero? dx)
             [-1 1]
             [-1 0 1])]
    [(+ dx x) (+ dy y)]))

(defn live [n alive?]
  (or (= n 3)
      (and (= n 2) alive?)))

(defn step [world]
  (set
   (for [[cell n] (frequencies (mapcat neighbors world))
         :when (live n (world cell))]
     cell)))

(defn life [initial-world]
  (iterate step initial-world))

Note that the final function life will return you an infinite sequence of steps in the game of life starting from an initial world (a set of cells).

When I went to Vaclav Pech’s talk on GPars on Tuesday he did a Groovy life implementation in GPars with dataflow which was pretty slick and had a real gui. So, in the keynote before my talk I whipped up a ui of my own using Processing and the clj-processing wrappers. My code is gross, especially in comparison to the life impl but it wraps a ref around the infinite sequence, then peels off the next item (in do-step) each time draw is called (on draw) by Processing and writes it to the screen.

To add clj-processing to you project.clj, use:

[org.clojars.automata/rosado.processing "1.1.0"]

It appears to only play nice with Clojure 1.2.x right now due to a dynamic var in the applet.

(ns demo.lifeui
  (:use [demo.life]
        [rosado.processing]
        [rosado.processing.applet]))

(def glider #{[0 1] [1 2] [2 0] [2 1] [2 2]})
(def blinker #{[3 3] [3 4] [3 5]})
(def worlds (ref (life glider)))

(defn setup []
  (background-int 0)
  (stroke-int 255)
  (framerate 4))

(defn do-step []
  (dosync
   (let [next-world (first @worlds)]
     (alter worlds #(drop 1 %))
     next-world)))

(defn grid [rows cols cells]
  (let [cellw (/ (width) cols)
        cellh (/ (height) cols)]
    (doseq [x (range rows)
            y (range cols)]
      (if (contains? cells [x y])
        (fill-float 250 50 50)
        (fill-float 0))
      (rect (* x cellw) (* y cellh) cellw cellh))))

(defn draw []
  (background 0)
  (grid 32 32 (do-step)))

(defapplet lifeui
  :title "Life"
  :setup setup
  :draw draw
  :size [800 600])

(run lifeui)

One key problem with this whole impl is that it ignores boundary wrapping so please feel free to fix that as an exercise for the reader.