はじめに
いままで作っているもの。
CanvasによるLangton's Loops - 仮想計算機構
Tempesti loop:自己複製しながら文字を描くセルオートマトン - 仮想計算機構
今回は Evoloop やります。
http://bingweb.binghamton.edu/~sayama/papers/ALIFE5-4-343.pdf
(PDF) Toward the Realization of an Evolving Ecosystem on Cellular Automata
準備
準備その1:変換規則
変換規則はセルの状態の変化を表す決まりの集合体です。Evoloop の変化規則には下記のような特徴があります。
- 回転対称
- フォンノイマン近傍
- 9状態
規則を記述したファイルは以下から入手します。
golly/Evoloop.table at master · jimblandy/golly · GitHub
膨大なルールを特殊な記法で完結に表しているのがわかります。
下記のスクリプトで省略された変換規則を元の状態に復元します。
f = open("Evoloop.table","r") g = open("output.txt","a+") lines = f.readlines() d = dict() for line in lines: if line[0]=="#": continue elif line[0:3]=="var": expr = line.split(" ")[1] v, s = expr.split("=") l = s[1:-2].split(",") d[v] = l elif True not in [k in line for k in d.keys()]: g.write(line.replace(",","")) else: rules = [line] for k in d.keys(): l = [] ids = [] processed_rules = [] for i,rule in enumerate(rules): srule = rule.split(',') if k in srule: processed_rules.append(rule) for v in d[k]: l.append(','.join([v if x==k else x for i,x in enumerate(srule)])) for r in processed_rules: rules.remove(r) rules.extend(l) for r in rules: g.write(r.replace(",","")) f.close() g.close()
色々と雑いプログラムですが、無視してください。
output.txt というファイルができるので、下記のとおり編集し、JavaScript のプログラムとして保存します。
const rule_str = `000012
020012
050012
(..........)
877750
877760
877770`
準備その2:Canvasによるアニメーション
アニメーションに必要なプログラムを下記の通り作成します。
<html> <body> <script type="text/javascript" src="rule.js"></script> <script type="text/javascript" src="main.js"></script> </body> </html>
const WIDTH = 630; const HEIGHT = 340; const canvas = document.createElement('canvas'); canvas.width = WIDTH; canvas.height = HEIGHT; const context = canvas.getContext('2d'); document.body.appendChild(canvas); const CELL_SIZE = 4; const N_ROW = Math.round(HEIGHT / CELL_SIZE); const N_COL = Math.round(WIDTH / CELL_SIZE); const evo_loops = [ [0,2,2,2,2,2,2,2,2,0,0,0,0,0,0], [2,1,7,0,1,4,0,1,4,2,0,0,0,0,0], [2,0,2,2,2,2,2,2,0,2,0,0,0,0,0], [2,7,2,0,0,0,0,2,1,2,0,0,0,0,0], [2,1,2,0,0,0,0,2,1,2,0,0,0,0,0], [2,0,2,0,0,0,0,2,1,2,0,0,0,0,0], [2,7,2,0,0,0,0,2,1,2,0,0,0,0,0], [2,1,2,2,2,2,2,2,1,2,2,2,2,2,0], [2,0,7,1,0,7,1,0,7,1,1,1,1,1,2], [0,2,2,2,2,2,2,2,2,2,2,2,2,2,0]]; const state2color = {"0":"black", "1":"blue", "2":"red", "3":"green", "4":"yellow", "5":"magenta", "6":"white", "7":"cyan", "8":"brown","9":"gray"}; class Evoloop{ constructor(){ this.init_field(); this.load_rule(); } init_field(){ // fieldの初期化 this.field = new Array(N_ROW); for(let i=0;i<N_ROW;i++){ this.field[i] = new Array(N_COL).fill(0); } // Evoloopを真ん中あたりに配置する let offset_y = Math.round((N_ROW - evo_loops.length) / 2); let offset_x = Math.round((N_COL - evo_loops[0].length) / 2); for(let y=0;y<evo_loops.length;y++){ for(let x=0;x<evo_loops[y].length;x++){ this.field[y + offset_y][x + offset_x - 10] = evo_loops[y][x]; } } } load_rule(){ this.rules = {}; let l = rule_str.split("\n"); for(let i=0;i<l.length;i++){ let neighbor = l[i].slice(0,5); let next_c = l[i].charAt(5); let [c,n,e,s,w] = Array.from(neighbor); // rotate 4 this.rules[c+n+e+s+w] = next_c; this.rules[c+w+n+e+s] = next_c; this.rules[c+s+w+n+e] = next_c; this.rules[c+e+s+w+n] = next_c; } } update(){ let next_field = new Array(N_ROW); for(let i=0;i<N_ROW;i++){ next_field[i] = new Array(N_COL).fill(0); } // 2次元平面はドラクエ的なドーナツ世界にしておきます for(let y=0;y<N_ROW;y++){ for(let x=0;x<N_COL;x++){ let c = "" + this.field[y][x]; let n = "" + this.field[(N_ROW + y - 1)%N_ROW][x]; let e = "" + this.field[y][(x + 1)%N_COL]; let s = "" + this.field[(y + 1)%N_ROW][x]; let w = "" + this.field[y][(N_COL + x - 1)%N_COL]; if((c+n+e+s+w) in this.rules){ next_field[y][x] = Number(this.rules[c+n+e+s+w]); }else{ next_field[y][x] = this.field[y][x]; } } } this.field = next_field; } render(ctx){ for(let y=0;y<N_ROW;y++){ for(let x=0;x<N_COL;x++){ ctx.beginPath(); ctx.fillStyle = state2color[this.field[y][x]]; ctx.rect(x*CELL_SIZE,y*CELL_SIZE,CELL_SIZE,CELL_SIZE); ctx.fill(); } } } } var evoloop = new Evoloop(); count = 0; function loop(timestamp) { context.clearRect(0, 0, WIDTH, HEIGHT); evoloop.render(context); evoloop.update(); context.fillStyle = "white"; context.font = "20px sans-serif"; context.fillText("Step : " + count, 10, 30); count++; window.requestAnimationFrame((ts) => loop(ts)); } window.requestAnimationFrame((ts) => loop(ts));
デモ