Last active
June 27, 2021 10:16
-
-
Save mmzsource/7c17b7336accb3bbf64bb7215e88b7fd to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#!/usr/bin/env bb | |
;; This script prints generates a random sequence of 'cells' (on or off) and | |
;; calculates each next generation of cells based on a given rule. | |
;; See: https://en.wikipedia.org/wiki/Elementary_cellular_automaton | |
;; | |
;; This commandline script has 1 dependency: | |
;; Babashka installed: https://github.com/babashka/babashka#installation (and bb on PATH) | |
;; | |
;; Run like this: bb bb-elementary-cellular-automata.clj | |
;; arguments help: bb bb-elementary-cellular-automata.clj -h | |
;; 128 cells, rule 30: bb bb-elementary-cellular-automata.clj -c128 -r30 | |
;;;;;;;;;;;;;;;; | |
;; USER INPUT ;; | |
;;;;;;;;;;;;;;;; | |
(defn to-int [string] | |
(try | |
(Integer. string) | |
(catch Exception))) | |
(defn valid-int? [int] | |
(and (integer? int) (>= int 0) (<= int 255))) | |
(def validate-msg "Must be a positive integer <= 255") | |
(def cli-options | |
[["-c" "--cells CELLS" "Number of cells in the one dimensional cellular automaton" | |
:default 128 | |
:parse-fn to-int | |
:validate [valid-int? validate-msg] ] | |
["-r" "--rule RULE" "Rule number (see https://en.wikipedia.org/wiki/Elementary_cellular_automaton#The_numbering_system)" | |
:default 30 | |
:parse-fn to-int | |
:validate [valid-int? validate-msg]] | |
["-h" "--help"]]) | |
(def options (tools.cli/parse-opts *command-line-args* cli-options)) | |
;;;;;;;;;;;;;;;;;;;;;;;;; | |
;; DATA TRANSFORMATION ;; | |
;;;;;;;;;;;;;;;;;;;;;;;;; | |
;; generates a 'rule map' with 3 bit sequences as keys | |
;; requires an int from 0..255 | |
;; converts it to a rule according to | |
;; https://en.wikipedia.org/wiki/Elementary_cellular_automaton#The_numbering_system | |
(defn rule-map [i] | |
(let [ks ["111" "110" "101" "100" "011" "010" "001" "000"] | |
;; left pad the binary string to guarantee 8 bits | |
vs (str/replace (format "%8s" (Integer/toBinaryString i)) " " "0")] | |
(zipmap ks vs))) | |
;; generates a string of random bits with length n | |
;; requires an int | |
;; returns a 3 bit sequence at min, a 255 bit sequence at max | |
;; 3 bits min because of 'CA domain' (see wiki link) | |
;; 255 bits max is arbitrary, but seems reasonable for a cmdline program | |
(defn bits [n] | |
(cond | |
(< n 3) (bits 3) | |
(> n 255) (bits 255) | |
:else (reduce str (repeatedly n #(rand-nth ["0" "1"]))))) | |
;; splits a one dimensional collection of 'cells' | |
;; (with its ends wrapped around in a circle) | |
;; into 'triplets' which represent a cell and its 2 neighbours | |
;; | |
;; requires a string representing at least 3 bits | |
;; will return a collection of 'triplets' like this (without the spaces between the bits): | |
;; "b1 b2 b3... bn" -> ["bn b1 b2" "b1 b2 b3" "b2 b3 .." "b3 .. .." ".. .. bn" ".. bn b1"] | |
(defn split [bits] | |
(let [first-element (str (last bits) (first bits) (second bits)) | |
middle-elements (mapv #(reduce str %) (partition 3 1 bits)) | |
last-element (str (last (butlast bits)) (last bits) (first bits))] | |
(concat [first-element] middle-elements [last-element]))) | |
;; applies the CA rules to a string of bits leading to the next CA generation | |
(defn step [rules bits] | |
(reduce str (map #(get rules %) (split bits)))) | |
;;;;;;;;;;;;;;;; | |
;; SUPER-GLUE ;; | |
;;;;;;;;;;;;;;;; | |
(defn print [state] | |
(-> state | |
(str/replace "1" "▒") | |
(str/replace "0" " ") | |
(prn))) | |
(when (:errors options) | |
(println (:errors options)) | |
(System/exit 1)) | |
(when ((comp :help :options) options) | |
(println (:summary options)) | |
(System/exit 1)) | |
(let [rules (rule-map ((comp :rule :options) options)) | |
start-state (bits ((comp :cells :options) options))] | |
(print start-state) | |
(loop [state start-state] | |
(Thread/sleep 100) | |
(let [new-state (step rules state)] | |
(print new-state) | |
(recur new-state)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment