Created
May 27, 2013 03:18
-
-
Save sevvie/5655016 to your computer and use it in GitHub Desktop.
Continuing my explorations into Conway's Game of Life.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; An exploration into Conway's Game of Life, in Clojure. | |
; Based on the work of Chas Emerick, Brian Carper and Christophe Grand, in the book | |
; _Clojure Programming_. | |
; | |
; There are three implementations of the stepping function here: | |
; - indexed-step, which handles the algorithm much like I have in other languages. | |
; - indexed-functional-step, which removes the loops and instead operates with reduces | |
; - functional-step, a method that removes the need for indexes by using the properties of sequences | |
; - pure-step, an elegant and tiny redesign of the idea. | |
(defn empty-board | |
"Create a rectangular empty board, of the specified width and height." | |
[w h] | |
(vec (repeat w (vec (repeat h nil)))) | |
) | |
(defn populate | |
"Turn :on each of the cells, specified as [y,x] coordinates" | |
[board living-cells] | |
(reduce (fn [board coords] | |
(assoc-in board coords :on) | |
) board living-cells) | |
) | |
(defn neighbors [[x y]] | |
(for [dx [-1 0 1] dy [-1 0 1] :when (not= 0 dx dy)] | |
[(+ dx x) (+ dy y)] | |
) | |
) | |
(defn count-neighbors [board loc] | |
(count (filter #(get-in board %) (neighbors loc))) | |
) | |
(defn indexed-step | |
"Yields the next state of the board, using indices to determine neighbors, lives, etc." | |
[board] | |
(let [w (count board) | |
h (count (first board))] | |
(loop [new-board board x 0 y 0] | |
(cond (>= x w) new-board | |
(>= y h) (recur new-board (inc x) 0) | |
:else | |
(let [new-liveness | |
(case (count-neighbors board [x y]) | |
2 (get-in board [x y]) | |
3 :on | |
nil)] | |
(recur (assoc-in new-board [x y] new-liveness) x (inc y))) | |
) | |
) | |
) | |
) | |
; Example: | |
; (nth (iterate indexed-functional-step glider) 8) | |
(defn indexed-functional-step [board] | |
(let [w (count board) | |
h (count (first board))] | |
(reduce (fn [new-board [x y]] | |
(let [new-liveness (case (count-neighbors board [x y]) | |
2 (get-in board [x y]) | |
3 :on | |
nil)] | |
(assoc-in new-board [x y] new-liveness) | |
) | |
) board (for [x (range h) y (range w)] [x y])) | |
) | |
) | |
(defn window | |
"Returns a lazy sequence of 3-item 'windows' centered around each item of coll." | |
([coll] (window nil coll)) | |
([pad coll] (partition 3 1 (concat [nil] coll [nil]))) | |
) | |
(defn cell-block | |
"Create a sequence of 3x3 windows from a triple of 3 sequences." | |
[[left mid right]] | |
(window (map vector | |
(or left (repeat nil)) mid (or right (repeat nil)))) | |
) | |
(defn liveness | |
"Returns the next cell state (nil or :on) of the center cell." | |
[block] | |
(let [[_ [_ center _] _] block] | |
(case (- (count (filter #{:on} (apply concat block))) (if (= :on center) 1 0)) | |
2 center | |
3 :on | |
nil | |
) | |
) | |
) | |
(defn- step-row | |
"Yields the next state of the center row." | |
[rows-triple] | |
(vec (map liveness (cell-block rows-triple))) | |
) | |
; Example: | |
; (nth (iterate functional-step glider) 8) | |
(defn functional-step | |
"Yields the next state of the board." | |
[board] | |
(vec (map step-row (window (repeat nil) board))) | |
) | |
; Example: | |
; (->> (iterate step #{[2 0] [2 1] [2 2] [1 2] [0 1]}) | |
; (drop 8) | |
; first | |
; (populate (empty-board 6 6)) | |
; pprint | |
; ) | |
(defn pure-step | |
"Yields the next state of the board." | |
[cells] | |
(set (for [[loc n] (frequencies (mapcat neighbors cells)) | |
:when (or (= n 3) (and (= n 2) (cells loc)))] | |
loc) | |
) | |
) | |
(def glider | |
(populate (empty-board 6 6) #{[2 0] [2 1] [2 2] [1 2] [0 1]}) | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment