Skip to content

Instantly share code, notes, and snippets.

@lithp
Created April 3, 2015 22:59
Show Gist options
  • Save lithp/d5096384b1ded35c81c4 to your computer and use it in GitHub Desktop.
Save lithp/d5096384b1ded35c81c4 to your computer and use it in GitHub Desktop.
Game of Life in Clojure
(ns game-of-life.core
(:gen-class)
(:require [clojure.string :as string])
)
; inclusive coordinates of two opposite corners
(defn window [x1 y1 x2 y2] [x1 y1 x2 y2])
(defn top-right [[x1 y1 x2 y2]] [(max x1 x2) (max y1 y2)])
(defn bottom-left [[x1 y1 x2 y2]] [(min x1 x2) (min y1 y2)])
(def under-population 2)
(def overcrowding 3)
(def reproduction 3)
(defn alive? [already-alive? num-neighbors]
(if already-alive?
(and (>= num-neighbors under-population)
(<= num-neighbors overcrowding))
(= num-neighbors reproduction)))
(defn board-repr [board [x1 y1 x2 y2]]
(letfn [(position [y x] (if (contains? board [x y]) "#" " "))
(line [y] (let [content (apply str (map (partial position y) (range x1 (+ 1 x2))))]
(str "|" content "|")))]
(string/join "\n" (map line (range y1 (+ 1 y2))))))
(def neighbor-offsets
(let [r (range -1 2)]
(for [x r y r :when (or (not= x 0) (not= y 0))]
[x y])))
(defn inf-neighbors [[x y]]
"Given an infinite board returns the neighbors of a cell"
(map (partial map +) neighbor-offsets (repeat [x y])))
(defn donut-neighbors [window [x y]]
"Given a toroidal board returns the neighbors of a cell"
(let [[x1 y1] (bottom-left window)
[x2 y2] (top-right window)]
(letfn [(clip [lower upper value]
(+ lower (mod (- value lower) (- (+ 1 upper) lower))))
(clip-cell [[x y]] [(clip x1 x2 x) (clip y1 y2 y)])]
(map (comp clip-cell (partial map +)) neighbor-offsets (repeat [x y])))))
; to calculate the next iteration generate all neighbors and group, keeping track of neighbor counts
(defn next-generation [neighbors board]
(->> board
(mapcat neighbors)
(map (fn [cell] {cell 1}))
(apply merge-with +)
(filter (fn [[cell num-neighbors]] (alive? (contains? board cell) num-neighbors)))
(keys)
(into #{})))
(defn random-board [n [x1 y1 x2 y2]]
"Does not guarantee n cells, only n samples"
(into #{}
(for [_ (range n)]
[(+ x1 (rand-int (+ 1 x2))) (+ y1 (rand-int (+ 1 y2)))])))
(defn -main
"Computes the Game of Life"
[& args]
(let [wind (window 0 0 70 70)]
(loop [board (random-board 800 wind)]
(println (board-repr board wind) "\n")
(Thread/sleep 50)
(recur (next-generation (partial donut-neighbors wind) board)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment