Created
April 3, 2015 22:59
-
-
Save lithp/d5096384b1ded35c81c4 to your computer and use it in GitHub Desktop.
Game of Life in Clojure
This file contains 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
(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