読者です 読者をやめる 読者になる 読者になる

Arantium Maestum

プログラミング、囲碁、読書の話題

Clojure/QuilでGame of Lifeを表示してみた

前回はn時点での生きたセルの集合からn+1時点での生きたセルの集合を算出する関数を定義した。

はっきり言ってGame of Life的なところはそれで終わりなのだが、やはりアニメーションで見てみたい、ということでQuilで表示してみる。

atomなどで状態を管理してみようかとも思ったのだが、Quil自体に関数型プログラミング的に状態遷移を扱うfun-modeというものが組み込まれているようなのでそちらを使ってみる。

まずnamespaceとライブラリ読み込み:

(ns game-of-life
  (:require [quil.core :as q] [quil.middleware :as m]))

quil.middlewareにfun-modeが定義されている。

とりあえず状態は関係なく、immutableな生きたセルの集合を引数に画面に出力する関数を書いてみる。

(defn draw-live-cells [live-cells]
  (doseq [[i j] live-cells]
    (apply q/rect 
      (map #(* 10 %) [i j 1 1]))))

次に、初期状態を定義し、setup関数がその状態を戻り値として返すようにする。

(def start-state 
  #{[1 1] [0 0] [1 0] [0 1] [3 3] [2 2] [2 3]})

(defn setup []
  (q/frame-rate 10)
  start-state)

update関数もdraw関数もimmutableなstateを引数に受け取り、新しいimmutableなstateを返すか、stateから画面出力するだけ。

どちらもできれば短めにして、ロジックの部分も描画の部分もQuilのアニメーションメカニズムからある程度分離させておきたい。updateは前回定義したnext-roundを呼び出しているだけ。

(defn update [state]
  (next-round state))

(defn draw [state]
  (q/background 255)
  (q/fill 255 0 0)  
  (q/translate 250 250)  
  (draw-live-cells state))

あとはdefsketchで組み合わせるだけ。

(q/defsketch game-of-life
   :size [500 500]
   :setup setup
   :update update
   :draw draw
   :middleware [m/fun-mode])

上記の初期状態からだと以下のような形で安定するのが確認できる。

f:id:zehnpaard:20160521071946p:plain

(実際にはちゃんと動く)