Saturday, October 3, 2015

Kata: Cellular Automata in Clojure

I just did the Cellular Automata kata in Clojure.

These are the tests using Midje:

(ns elementary-cellular-automaton.core-test
(:use midje.sweet)
(:use [elementary-cellular-automaton.core]))
(def rule-30 {[1 1 1] 0
[1 1 0] 0
[1 0 1] 0
[1 0 0] 1
[0 1 1] 1
[0 1 0] 1
[0 0 1] 1
[0 0 0] 0})
(facts
"An elementary cellular automaton"
(fact
"evolves for several generations following a given rule
and some initial cells"
(evolve rule-30 [1] 1) => [[1]]
(evolve rule-30 [1] 2) => [[1] [1 1 1]]
(evolve rule-30 [1] 3) => [[1] [1 1 1] [1 1 0 0 1]]
(evolve
rule-30 [1] 4) => [[1] [1 1 1] [1 1 0 0 1] [1 1 0 1 1 1 1]])
(fact
"can be rendered as text lines"
(render rule-30 [1] 1) => ["x"]
(render rule-30 [1] 2) => [" x" "xxx"]
(render rule-30 [1] 4) => [" x" " xxx" " xx x" "xx xxxx"]))
and this is the resulting code:

(ns elementary-cellular-automaton.core
(:require [clojure.string :as string]))
(defn- pad [cells]
(concat [0 0] cells [0 0]))
(defn- cell-neighbors [cells]
(partition 3 1 (pad cells)))
(defn- next-generation [cells rule]
(map rule (cell-neighbors cells)))
(defn- spaces [num-generations generation-number]
(let [num-spaces (- num-generations (inc generation-number))]
(apply str (repeat num-spaces \space))))
(defn- center [num-generations generation-number line]
(str (spaces num-generations generation-number) line))
(defn- render-zeros [line]
(string/replace line #"0" " "))
(defn- render-ones [line]
(string/replace line #"1" "x"))
(defn- render-generation
[num-generations generation-number generation]
(->> generation
(apply str)
(render-ones)
(render-zeros)
(center num-generations generation-number)))
(defn evolve [rule initial-cells num-generations]
(take num-generations
(iterate #(next-generation % rule) initial-cells)))
(defn render [rule initial-cells num-generations]
(->> (evolve rule initial-cells num-generations)
(map-indexed #(render-generation num-generations %1 %2))))
(defn print-evolution [rule initial-cells num-generations]
(doseq [line (render rule initial-cells num-generations)]
(println line)))
where I added a function print-evolution to visualize the evolution on the REPL

(def rule-30 {[1 1 1] 0
[1 1 0] 0
[1 0 1] 0
[1 0 0] 1
[0 1 1] 1
[0 1 0] 1
[0 0 1] 1
[0 0 0] 0})
# => #'user/rule-30
(elementary-cellular-automaton.core/print-evolution rule-30 [1] 20)
x
xxx
xx x
xx xxxx
xx x x
xx xxxx xxx
xx x x x
xx xxxx xxxxxx
xx x xxx x
xx xxxx xx x xxx
xx x x xxxx xx x
xx xxxx xx x x xxxx
xx x xxx xx xx x x
xx xxxx xx xxx xxx xx xxx
xx x x xxx x xxx x x
xx xxxx xx x x xxxxx xxxxxxx
xx x xxx xxxx x xxx x
xx xxxx xx xxx xx xx x xxx
xx x x xxx x xx xxx xxxx xx x
xx xxxx xx x xxxxxx x x xxx xxxx
# => nil
As usual I used a mix of TDD and REPL-driven development committing after each green and each refactoring. I also committed the REPL history.

See all the commits here if you want to follow the process.

You can find all the code on GitHub.

No comments:

Post a Comment