Skip to content

Instantly share code, notes, and snippets.

@sevvie
Created May 27, 2013 03:18
Show Gist options
  • Save sevvie/5655016 to your computer and use it in GitHub Desktop.
Save sevvie/5655016 to your computer and use it in GitHub Desktop.
Continuing my explorations into Conway's Game of Life.
; 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