I'm a Clojure beginner and I implemented Game of life to learn how I should code things in Clojure.
(def state (ref [
[false false false false false]
[false false true false false]
[false false true false false]
[false false true false false]
[false false false false false]]))
(defn get-state [state x y]
(nth (nth state y) x))
(defn count-true-neighbours [state x y]
(let [size (count state)]
(->>
(for [p (range (- x 1) (+ x 2))
q (range (- y 1) (+ y 2))
:when (not (and (= x p) (= y q)))]
[p q])
(map (fn [coord]
(let [[p q] coord]
(if (or (< p 0) (>= p size) (< q 0) (>= q size))
false
(get-state state p q)))))
(filter true?)
(count))))
(defn next-state-of [state x y]
(let [alive (get-state state x y)
alive-neighbours (count-true-neighbours state x y)]
(cond
(and (not alive) (= alive-neighbours 3)) true
(and alive (or (= alive-neighbours 2) (= alive-neighbours 3))) true
:else false)))
(defn next-state [state]
(let [size (count state)]
(mapv
(fn [row]
(mapv
#(next-state-of state %1 row)
(range 0 size)))
(range 0 size))))
(defn pretty-print [state]
(let [size (count state)]
(loop [i 0]
(if (< i size)
(do
(println
(->>
(nth state i)
(mapv #(if %1 "■ " "□ "))
(reduce str)))
(recur (inc i)))))))
(defn tick
([times] (tick 0 times))
([i times]
(if (< i times)
(do
(dosync
(println (str i ":"))
(pretty-print @state)
(ref-set state (next-state @state)))
(recur (inc i) times))
nil)))
(defn -main [& args]
(tick 5))
I used ref
to learn how to manage state although it seems like an overkill for this tiny program.
As you see my code is terrible, but I couldn't improve this by myself.
I already checked this answer, but I think I did this in a quite different way and need a different advice.