Friday, February 27, 2009

Conway's Game of Life

John Conway developed the Game of Life in 1970 as an example of cellular automata. The following Clojure code uses Swing to implement the game.




(import '(javax.swing JFrame JPanel JButton)
'(java.awt BorderLayout Dimension Color)
'(java.awt.event ActionListener))

(def cells (ref {}))

(def running (ref false))

(defn init-cells []
(dosync
(dorun
(for [x (range 32) y (range 48)]
(ref-set cells
(assoc (deref cells) [x y] (= 0 (rand-int 5))))))))

(defn determine-new-state [x y]
(let [count
(+ (if (cells [(- x 1) (- y 1)]) 1 0)
(if (cells [x (- y 1)]) 1 0)
(if (cells [(+ x 1) (- y 1)]) 1 0)
(if (cells [(- x 1) y]) 1 0)
(if (cells [(+ x 1) y]) 1 0)
(if (cells [(+ x 1) (+ y 1)]) 1 0)
(if (cells [x (+ y 1)]) 1 0)
(if (cells [(- x 1) (+ y 1)]) 1 0))]

(or (and (cells [x y]) (> count 1) (< count 4))
(and (not (cells [x y])) (= count 3)))))

(defn calc-state []
(let [new-cells (ref {})]
(dosync
(dorun
(for [x (range 32) y (range 48)]
(ref-set new-cells
(assoc (deref new-cells) [x y] (determine-new-state x y)))))

(ref-set cells (deref new-cells)))))

(defn paint-cells [graphics]
(dorun (map #(let [x (first (first %))
y (second (first %))
state (second %)]

(doto graphics
(. setColor (if state Color/RED Color/WHITE))
(. fillRect (* 10 x) (* 10 y) 10 10)))

(deref cells))))

(defn toggle-thread [panel button]
(if (deref running)

(do (dosync (ref-set running false))
(. button (setText "Start")))

(do (dosync (ref-set running true))
(. button (setText "Stop"))
(. (Thread.
#(loop []
(calc-state)
(. panel repaint)
(Thread/sleep 100)
(if (deref running) (recur))))
start))))

(defn main[]

(init-cells)

(let [f (JFrame.)
b (JButton. "Start")
panel (proxy [JPanel] [] (paint [graphics] (paint-cells graphics)))]

(doto f
(. setLayout (BorderLayout.))
(. setLocation 100 100)
(. setPreferredSize (Dimension. 320 540))
(. add b BorderLayout/SOUTH)
(. add panel BorderLayout/CENTER)
(. setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(. pack)
(. setVisible true))

(. b addActionListener
(proxy [ActionListener] []
(actionPerformed [evt] (toggle-thread panel b))))))

(main)


ref, deref, and ref-set maintain the state of the cells and whether or not the system has started or stopped. Approximately twenty percent of the cells are initialized as alive by the statement (= 0 (rand-int 5)).