Very cool. I had originally planned to add some stats keeping to my implementation along with connected component coloring (hence the ref'd maps). I also found refs convenient for synchronizing my neighbor calculations with grid updates since I have random seeding, the ability to change the grid while it is running, and pausing. Looks like your implementation will always maintain a correctly calculated grid.
You might replace: > (defn render [#^Graphics g b] > (doseq [y (range height)] > (doseq [x (range width)] > (let [cell (cell-at b x y)] > (if (= cell 0) > (.setColor g Color/WHITE) > (.setColor g Color/BLACK)) > (.fillRect g (* x cell-width) (* y cell-height) > (dec cell-width) (dec cell-height)))))) with: (defn render [#^Graphics g b] (doseq [y (range height) x (range width)] ... No need for the nested doseqs. I'm thinking... clojuriffic? clojured? clojonic? :) --- Joseph Smith j...@uwcreations.com (402)601-5443 On Nov 26, 2009, at 3:37 AM, Chris Jenkins wrote: > Thanks for sharing this. Coincidentally, I just wrote my first > Clojure program which was... an implementation of Conway's Game of > Life :-) I took a different approach - I represented the board as a > vector of vectors of integers (1 for alive, 0 for dead) and then > implemented a new-board function that takes a board as an argument > and returns the next generation of that board. It seemed the > functional way to do it. I then create an atom whose state is a grid > and repeatedly call (swap! board new-board) in order to move it > through the generations. > > I'll copy the source to my little program at the bottom of this > email. If anyone has any comments on the style or possible > improvements then I would be very interested to hear them. I'm > particularly keen to hear how close to idiomatic Clojure style it is > (is there a Clojure equivalent of the adjective "Pythonic"?) > > (import > '(java.awt Color Graphics Dimension FlowLayout) > '(javax.swing JPanel JFrame JButton Timer BoxLayout) > '(java.awt.event MouseListener ActionListener ActionEvent)) > > (set! *warn-on-reflection* true) > > (def width 50) > (def height 20) > (def cell-width 10) > (def cell-height 10) > > (def sleep-time 100) > > (def initial-board > ; (apply vector (replicate height > ; (apply vector (replicate width 0))))) > [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 1 1 0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 1 1 0 0 1 1 0 0 > 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 > 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 1 1 0 0 > 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0]]) > > (def board-atom (atom initial-board )) > > (def running-atom (atom false)) > > ;; Board manipulation functions > > (defn print-board [b] > (doseq [row b] > (println (str row)))) > > (defn within [v min max] > (and (>= v min) (< v max))) > > (defn cell-at [b x y] > (if (and (within x 0 width) (within y 0 height)) > (nth (nth b y) x) > 0)) > > (defn occurences [l item] > (count (filter #(= % item) l))) > > (defn new-cell-value [cell neighbours] > (let [num-1s (occurences neighbours 1)] > (if (= cell 0) > ; cell is currently dead and will become alive iff it has 3 > living > ; neighbours > (if (= num-1s 3) 1 0) > ; else cell is currently alive and will die iff it has fewer > than 2 or > ; more than 3 living neighbours > (if (or (< num-1s 2) (> num-1s 3)) 0 1)))) > > (defn cell-neighbours [b x y] > [(cell-at b (- x 1) (- y 1)) > (cell-at b x (- y 1)) > (cell-at b (+ x 1) (- y 1)) > (cell-at b (- x 1) y) > (cell-at b (+ x 1) y) > (cell-at b (- x 1) (+ y 1)) > (cell-at b x (+ y 1)) > (cell-at b (+ x 1) (+ y 1))]) > > (defn new-value-at [b x y] > (let [cell (cell-at b x y) > neighbours (cell-neighbours b x y)] > (new-cell-value cell neighbours))) > > (defn new-board [b] > (vec (for [y (range height)] > (vec (for [x (range width)] > (new-value-at b x y)))))) > > (defn flip-cell [b x y] > (let [row (nth b y) > cell (nth row x) > new-cell (- 1 cell) > new-row (assoc row x new-cell)] > (assoc b y new-row))) > > (defn apply-n-times [f n x] > (if (zero? n) > x > (recur f (dec n) (f x)))) > > ;; GUI functions > > (defn make-action-listener [f] > (proxy [ActionListener] [] (actionPerformed [e] (f e)))) > > (defn render [#^Graphics g b] > (doseq [y (range height)] > (doseq [x (range width)] > (let [cell (cell-at b x y)] > (if (= cell 0) > (.setColor g Color/WHITE) > (.setColor g Color/BLACK)) > (.fillRect g (* x cell-width) (* y cell-height) > (dec cell-width) (dec cell-height)))))) > > (defn make-board-panel [] > (let [panel > (proxy [JPanel MouseListener] [] > (paint [g] (render g @board-atom)) > (getPreferredSize [] (Dimension. (* width cell-width) > (* height cell-height))))] > (doto panel > (.addMouseListener (proxy [MouseListener] [] > (mouseClicked [e] > (let [x (int (/ (.getX e) cell-width)) > y (int (/ (.getY e) cell-height))] > (swap! board-atom flip-cell x y))) > (mousePressed [e]) > (mouseReleased [e]) > (mouseEntered [e]) > (mouseExited [e])))))) > > (defn make-button [text action] > (doto (JButton. text) > (.addActionListener (make-action-listener action)))) > > (defn make-control-panel [] > (let [control-panel (JPanel.)] > (doto control-panel > (.setLayout (BoxLayout. control-panel BoxLayout/Y_AXIS)) > (.add (make-button "Start" (fn [e] > (swap! running-atom (constantly true))))) > (.add (make-button "Stop" (fn [e] > (swap! running-atom (constantly false))))) > (.add (make-button "Step" (fn [e] > (swap! board-atom new-board))))))) > > (defn make-frame [] > (let [frame (JFrame. "Life")] > (doto (.getContentPane frame) > (.setLayout (FlowLayout.)) > (.add (make-board-panel)) > (.add (make-control-panel))) > (add-watch board-atom "repaint" (fn [k r o n] (.repaint frame))) > (doto frame > (.pack) > (.show)) > frame)) > > (defn go [b] > (let [frame (make-frame)] > (.start (Timer. sleep-time (make-action-listener > (fn [e] (when @running-atom > (swap! b new-board)))))))) > (go board-atom) > > > 2009/11/16 solussd <solu...@gmail.com> > I just finished an implementation of the Conway's Game of Life > derivative, Highlife, in Clojure. It consists of a simple swing GUI > and makes good use of Refs for coordinating grid updates. A more > detailed description, source, and jars can be found here: > http://www.solussd.com/2009/11/a-clojure-highlife/ enjoy! > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient > with your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient > with your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en