Instantly share code, notes, and snippets.

@rm-hull /nks1.cljs
Last active Aug 29, 2015

Embed
What would you like to do?
Stephen Wolfram announced a class of a one-dimensional binary cellular automaton rules in 1983 and published further analysis in his 2002 book _A New Kind of Science_. The most famous instance is "Rule 30": a Class III rule, displaying aperiodic, chaotic behaviour. This rule is of particular interest because it produces complex, seemingly random…
(ns big-bang.examples.nks1
(:require
[jayq.core :refer [show]]
[inkspot.color-chart :as cc]
[big-bang.core :refer [big-bang]]
[enchilada :refer [ctx canvas canvas-size value-of]]
[monet.canvas :refer [clear-rect fill-rect fill-style draw-image]]
[dommy.core :refer [insert-after! set-text!]])
(:require-macros
[dommy.macros :refer [sel1 node]]))
(def to-number
(memoize
(fn [xs radix]
(let [index (cons 1 (reductions * (repeat radix)))]
(reduce + (map * index (reverse xs)))))))
(def digits
(memoize
(fn [n radix]
(loop [n n
res nil]
(if (zero? n)
res
(recur
(quot n radix)
(cons (rem n radix) res)))))))
(defn neighbours [data]
(partition 3 1 data))
(defn rule-bits [n]
(vec
(take 8
(concat
(reverse (digits n 2))
(repeat 0)))))
(defn rule [n]
(let [bits (rule-bits n)]
(fn [x]
(nth bits (to-number x 2)))))
(defn inflate [coll]
(vec (cons 0 (conj coll 0))))
(defn generations [rule-fn data]
(lazy-seq
(cons
(vec data)
(generations
rule-fn
(inflate (mapv rule-fn (neighbours (inflate data))))))))
(defn midpoint [n]
(/ (dec n) 2))
(defn window [n] ; <-- width should be odd
(if (even? n)
(window (dec n))
(let [offset (midpoint n)]
(fn [data]
(let [shortfall (- offset (midpoint (count data)))
data (if (pos? shortfall)
(nth (iterate inflate data) shortfall)
data)
data-midpoint (midpoint (count data))]
(subvec data (- data-midpoint offset) (+ data-midpoint offset 1)))))))
(defn initial-state [rule-number]
(let [seed [0 1 0]
[w h] (canvas-size)
block-size (js/parseInt (value-of :block-size 4))
rule-fn (rule (mod rule-number 256))
win-size (quot w block-size)]
{:t 0
:rule rule-number
:clear? true
:block-size block-size
:width w
:height h
:color-chart (cc/ui-gradient
(value-of :gradient :miaka)
win-size)
:generations (map
(window win-size)
(generations rule-fn seed))}))
(defn update-state [event world-state]
(->
world-state
(update-in [:t] inc)
(update-in [:generations] rest)
(assoc :clear? false)))
(defn handle-mousedown [event world-state]
(initial-state (rand-int 256)))
(defn scroll-y [ctx offset width height]
(let [img-data (.getImageData ctx 0 0 width height)]
(.putImageData ctx img-data 0 (- offset))
;(clear-rect ctx {:x 0 :y (- height offset) :w width :h offset})
))
(def render
; Create a pre-rendered canvas (double-buffering)
(let [canvas (.createElement js/document "canvas")
[w h] (canvas-size)]
(set! (.-width canvas) w)
(set! (.-height canvas) h)
(fn [{:keys [generations t rule block-size width height color-chart clear?] :as world-state}]
(-> (sel1 "#rule") (set-text! (str "Rule: " rule)))
(-> (sel1 "#generation") (set-text! (str "Generation: " t)))
(let [buffer (.getContext canvas "2d")]
(if clear?
(clear-rect buffer {:x 0 :y 0 :w width :h height})
(scroll-y buffer block-size width height))
(loop [co-ords {:x 0 :y (- height block-size block-size) :w block-size :h block-size}
elems (first generations)
colors color-chart]
(when-not (empty? elems)
(when (pos? (first elems))
(->
buffer
(fill-style (first colors))
(fill-rect co-ords)))
(recur
(update-in co-ords [:x] (partial + block-size))
(rest elems)
(rest colors)))))
(clear-rect ctx {:x 0 :y 0 :w width :h height})
(draw-image ctx canvas 0 0))))
(->>
(sel1 :#canvas-area)
(insert-after!
(node
[:div#app
[:style
"#canvas-area {
cursor: pointer;
}
#app {
background: rgba(0, 0, 0, 0.6);
color: rgb(211, 211, 211);
font-family: sans-serif;
font-size: 10pt;
border-radius: 3px;
display: inline-block;
position: relative;
left: -790px;
top: -550px;
width: 110px;
padding: 3px;
}"]
[:p#rule]
[:p#generation]])))
(show canvas)
(big-bang
:event-target canvas
:initial-state (initial-state (value-of :rule (rand-int 256)))
:on-mousedown handle-mousedown
:on-tick update-state
;:tick-rate 100
:to-draw render)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment