Conway's Life in ClojureScript
;; Credits: | |
;; | |
;; * ClojureScript | |
;; * Reagent https://reagent-project.github.io/ | |
;; * Figwheel https://github.com/bhauman/lein-figwheel | |
;; * core.async https://clojure.github.io/core.async/ | |
;; * Christophe Grand's Life implementation http://clj-me.cgrand.net/2011/08/19/conways-game-of-life/ | |
(ns life.core | |
(:require [reagent.core :as r] | |
[cljs.core.async :refer [timeout <!]]) | |
(:require-macros [cljs.core.async.macros :refer [go-loop]])) | |
;; Initial state, the "Blinker" lifeform | |
(defonce state (r/atom #{[-1 0] [0 0] [1 0]})) | |
; Also try the "R-Pentomimo": #{[-1 0] [-1 1] [0 -1] [0 0] [1 0]} | |
(defn neighbors | |
"Given a cell, return the collection of its neighbors in every direction" | |
[[x y]] | |
(for [dx [-1 0 1] | |
dy [-1 0 1] | |
:when (not= [dx dy] [0 0])] | |
[(+ x dx) (+ y dy)])) | |
(defn next-pop | |
"Given a population, return the next generation of it | |
following the rules of Conway's Life" | |
[pop] | |
(let [all-neighbors (mapcat neighbors pop) | |
neigh-count (frequencies all-neighbors)] | |
(set (for [[cell count] neigh-count | |
:when (or (= count 3) | |
(and (= count 2) | |
(pop cell)))] | |
cell)))) | |
(defn evolve | |
"Begin a process of evolution. Will swap the state | |
to the next generation every 100ms" | |
[] | |
(go-loop [] | |
(<! (timeout 100)) | |
(swap! state next-pop) | |
(recur))) | |
(defn grid | |
"A React/Reagent component that renders life | |
to an SVG element" | |
[] | |
[:svg {:width 450 | |
:height 450 | |
:viewBox "-225 -225 450 450"} | |
(for [[x y] @state] | |
[:rect {:width 20 | |
:height 20 | |
:x (* x 20) | |
:y (* y 20)}])]) | |
;; Render the grid component to the document | |
(r/render [grid] (.-body js/document)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment