;; Contributed by Larry Sherrill with a lot of help from forum members. ;; See http://en.wikipedia.org/wiki/Conway's_Game_of_Life (import '(javax.swing JFrame JPanel JButton JCheckBox JLabel) '(java.awt BorderLayout Dimension Color) '(java.awt.event ActionListener ItemListener MouseMotionListener MouseListener MouseAdapter MouseMotionAdapter)) (def x-cells 50) (def y-cells 50) (def life-delay 20) (def life-prob 5) (def cells (ref {})) (def running (ref false)) (def bounded (ref false)) (def last-x (ref nil)) (def last-y (ref nil)) (defn determine-initial-state [x y] false) (defn determine-random-state [x y] (= 0 (rand-int life-prob))) (defn neighbor-value [x y dx dy] (if @bounded [(+ x dx) (+ y dy)] [(mod (+ x dx) x-cells) (mod (+ y dy) y-cells)])) (defn determine-new-state [x y] (let [neighbor-count (count (for [dx [-1 0 1] dy [-1 0 1] :when (and (not (= 0 dx dy)) (@cells (neighbor-value x y dx dy)))] :alive))] (if (cells [x y]) (< 1 neighbor-count 4) (= neighbor-count 3)))) (defn calc-state [cell-state] "Determine the state for all cells using the passed in cell-state function." (dosync (ref-set cells (reduce conj {} (for [x (range x-cells) y (range y-cells)] [[x y] (cell-state x y)]))))) (defn paint-one-cell [#^java.awt.Graphics graphics x y state] (doto graphics (.setColor (if state Color/RED Color/WHITE)) (.fillRect (* 10 x) (* 10 y) 10 10))) (defn paint-cells [#^java.awt.Graphics graphics] (doseq [[[x, y] state] @cells] (paint-one-cell graphics x y state))) (defn toggle-thread [panel button] "The start/stop button was clicked. Change wording on the button and run the animation thread." (if @running (do (dosync (ref-set running false)) (.setText button "Start")) (do (dosync (ref-set running true)) (.setText button "Stop") (.start (Thread. #(loop [] (calc-state determine-new-state) (.repaint panel) (Thread/sleep life-delay) (if @running (recur)))))))) (defn screen-to-world [s] (int (/ s 10))) (defn main [] (calc-state determine-random-state) (let [f (JFrame.) button-start (JButton. "Start") button-random (JButton. "Rand") button-clear (JButton. "Clear") checkbox-bounded (JCheckBox. "Bounded") panel-buttons (JPanel.) panel-canvas (proxy [JPanel] [] (paint [#^java.awt.Graphics graphics] (paint-cells graphics)))] (doto panel-buttons (.add button-start) (.add button-random) (.add button-clear) (.add checkbox-bounded)) (.setPreferredSize panel-canvas (Dimension. (* 10 x-cells) (* 10 y-cells))) (doto f (.setLayout (BorderLayout.)) (.setLocation 100 100) (.add panel-canvas BorderLayout/NORTH) (.add panel-buttons BorderLayout/CENTER) (.add (JLabel. " Use mouse to draw on canvas.") BorderLayout/SOUTH) (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE) (.pack) (.setVisible true)) (.addMouseMotionListener panel-canvas (proxy [MouseMotionAdapter] [] (mouseDragged [evt] (let [x (screen-to-world (.getX evt)) y (screen-to-world (.getY evt)) state (@cells [x y])] ; checking last-x and last-y are needed to reduce flicker while dragging (when (and (= @running false) (not (and (= @last-x x) (= @last-y y)))) (do (dosync (ref-set cells (assoc @cells [x y] (not state))) (ref-set last-x x) (ref-set last-y y)) (paint-one-cell (.getGraphics panel-canvas) x y (not state)))))))) (.addMouseListener panel-canvas (proxy [MouseAdapter] [] (mouseClicked [evt] (let [x (screen-to-world (.getX evt)) y (screen-to-world (.getY evt)) state (@cells [x y])] (when (not (= @running true)) (do (dosync (ref-set cells (assoc @cells [x y] (not state)))) (paint-one-cell (.getGraphics panel-canvas) x y (not state)))))))) (.addActionListener button-start (proxy [ActionListener] [] (actionPerformed [evt] (.setEnabled button-random @running) (.setEnabled button-clear @running) (.setEnabled checkbox-bounded @running) (toggle-thread panel-canvas button-start)))) (.addActionListener button-random (proxy [ActionListener] [] (actionPerformed [evt] (calc-state determine-random-state) (.repaint panel-canvas)))) (.addActionListener button-clear (proxy [ActionListener] [] (actionPerformed [evt] (calc-state determine-initial-state) (.repaint panel-canvas)))) (.addItemListener checkbox-bounded (proxy [ItemListener] [] (itemStateChanged [evt] (dosync (ref-set bounded (.isSelected checkbox-bounded )))))))) ;; (main)