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.