Re: A Clojure Highlife
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
Re: A Clojure Highlife
Cool - thanks. I didn't know about that function :-) 2009/11/27 John Harrop jharrop...@gmail.com On Thu, Nov 26, 2009 at 4:37 AM, Chris Jenkins cdpjenk...@gmail.comwrote: (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 flip-cell [b x y] (update-in b [y x] #(- 1 %))) :) -- 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.comclojure%2bunsubscr...@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
Re: A Clojure Highlife
On Thu, Nov 26, 2009 at 4:37 AM, Chris Jenkins cdpjenk...@gmail.com wrote: (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 flip-cell [b x y] (update-in b [y x] #(- 1 %))) :) -- 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
Re: A Clojure Highlife
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)]
Re: A Clojure Highlife
The m-surrounding-neighbors-seq function only memoizes sequences containing Refs surrounding a particular ref at (x,y). I do this to avoid the recalculation of the coordinates of the cells surrounding each cell. As I understand it, memoization caches return values for input values. For example, the the Ref for cell (3,4) has Refs at [(2,3) (3,3) (4,3) (2,4) (4,4) (2,5) (3,5) (4,5)] surrounding it. It is only memoizing a sequence of these Refs, once for each point in the grid, not for every grid. If it was indeed doing that it wouldn't take long before the JVM ran out of memory- the simulation is very unlikely to hit a cycle :) Use the clojure libs I include in the bundle on my website: http://www.solussd.com/wp-content/uploads/2009/11/highlife.zip --- Joseph Smith j...@uwcreations.com (402)601-5443 On Nov 16, 2009, at 8:24 PM, Jeff Heon wrote: Hi there, Unfortunately, I was unable to get it working. From what I gather, I'm using the wrong clojure.jar and or clojure- contrib.jar. Could you tell me which version of theses files you are using? Anyway, I was wondering about the memoizing of the function surrounding-neighbors-seq. It's memoizing for every grid, which I guess is good if the application hits a cycle, but if it doesn't, it will unnecessarily keep function results for passed grids. I'm not sure about the memoizing cache behavior, but if we bet for non- repeating grids, I think it might be better to dynamically memoize for each grid only. This way, memoizing information for passed grids will be GCed. I'm thinking about something like this. Remove the global def: (def m-surrounding-neighbors-seq (memoize surrounding-neighbors-seq)) Modify the two following count-living-neighbors and get-living- neighbors-seq functions to let a memoized version once for each grid: See http://paste.lisp.org/display/90541 Using a let and passing the local memoized function to count-living- neighbors felt weird, but it felt better than doing a binding. I wonder which would be the idiomatic way to go. -- 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
Re: A Clojure Highlife
Thanks. I got it working with the bundle. Arghh, I realized about half an hour after posting that I had misunderstood m-surrounding-neighbors-seq. I withdrew my post from the Google group, but like they say, nothing vanishes without a trace ;) I had not realized that since the grid is made out of refs, and since the refs themselves do not change, that the grid will always be equal to itself and thus not generate new memoization values even if the values inside the refs do change. These days it seems that not matter how long I think about a reply, and no matter how many times I reread it before posting, I'll find an error in it just after posting it 8p -- 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
Re: A Clojure Highlife
Heh. I had wondered if you had withdrawn the post since I only saw it in my email box. No worries- I went back and examined my code to make sure it was doing what I thought it was. --- Joseph Smith j...@uwcreations.com (402)601-5443 On Nov 18, 2009, at 7:35 AM, Jeff Heon jfh...@gmail.com wrote: Thanks. I got it working with the bundle. Arghh, I realized about half an hour after posting that I had misunderstood m-surrounding-neighbors-seq. I withdrew my post from the Google group, but like they say, nothing vanishes without a trace ;) I had not realized that since the grid is made out of refs, and since the refs themselves do not change, that the grid will always be equal to itself and thus not generate new memoization values even if the values inside the refs do change. These days it seems that not matter how long I think about a reply, and no matter how many times I reread it before posting, I'll find an error in it just after posting it 8p -- 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
Re: A Clojure Highlife
On Nov 16, 2:49 pm, John Harrop jharrop...@gmail.com wrote: For a real challenge, try implementing Hashlife using Clojure's immutable data structures. :) This might help. http://www.ddj.com/hpc-high-performance-computing/184406478 To quote the author, Tomas G. Rokicki : This decision lets you use a completely functional approach on an immutable data structure, assuming you enjoy such things. 8) -- 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
Re: A Clojure Highlife
On Mon, Nov 16, 2009 at 12:42 AM, solussd solu...@gmail.com wrote: 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! For a real challenge, try implementing Hashlife using Clojure's immutable data structures. :) -- 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
Re: A Clojure Highlife
Hi there, Unfortunately, I was unable to get it working. From what I gather, I'm using the wrong clojure.jar and or clojure- contrib.jar. Could you tell me which version of theses files you are using? Anyway, I was wondering about the memoizing of the function surrounding-neighbors-seq. It's memoizing for every grid, which I guess is good if the application hits a cycle, but if it doesn't, it will unnecessarily keep function results for passed grids. I'm not sure about the memoizing cache behavior, but if we bet for non- repeating grids, I think it might be better to dynamically memoize for each grid only. This way, memoizing information for passed grids will be GCed. I'm thinking about something like this. Remove the global def: (def m-surrounding-neighbors-seq (memoize surrounding-neighbors-seq)) Modify the two following count-living-neighbors and get-living- neighbors-seq functions to let a memoized version once for each grid: See http://paste.lisp.org/display/90541 Using a let and passing the local memoized function to count-living- neighbors felt weird, but it felt better than doing a binding. I wonder which would be the idiomatic way to go. -- 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