はじめに
aidairyさんの記事↓に刺激されて自分でも作ってみたくなったのでラングトンのループを再現してみます。
ラングトンの自己複製オートマトン - 人工知能に関する断創録
aidairyさんの記事ではPython/PyGameを使用しているのですが、Webで見られる方が楽かなーということで、今回はHTML5/Canvasでプログラムを作成しました。プログラムについてはaidiaryさんのものと下記のブログを参考にしています。
JavaScriptとcanvasでアニメーションを作る
プログラム
作成したプログラムは以下の通りです。
<html> <body> <script type="text/javascript" src="langton_rule.js"></script> <script type="text/javascript" src="langtons-loops.js"></script> </body> </html>
↑のサイトにはラングトンのループの遷移規則があります。
遷移規則をコピペして以下のように文字列にしておきます。
const langton_rule_str = `000000
000012
000020
...
702525
702720`;
メインのプログラムが以下になります。ピュアなJavaScriptだとPythonのようにタプルがないのでちょっと苦し紛れの実装となりました。
const WIDTH = 400; const HEIGHT = 400; 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 langtons_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"}; class Langton{ 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); } // Langtons Loopを配置する let offset_y = Math.round((N_ROW - langtons_loops.length) / 2); let offset_x = Math.round((N_COL - langtons_loops[0].length) / 2); for(let y=0;y<langtons_loops.length;y++){ for(let x=0;x<langtons_loops[y].length;x++){ this.field[y + offset_y][x + offset_x - 10] = langtons_loops[y][x]; } } } load_rule(){ this.rules = {}; let l = langton_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); // rotate4 let [c,n,e,s,w] = Array.from(neighbor); 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); } for(let y=1;y<N_ROW-1;y++){ for(let x=1;x<N_COL-1;x++){ let c = "" + this.field[y][x]; let n = "" + this.field[y - 1][x]; let e = "" + this.field[y][x + 1]; let s = "" + this.field[y + 1][x]; let w = "" + this.field[y][x - 1]; 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 langton = new Langton(); function loop(timestamp) { context.clearRect(0, 0, WIDTH, HEIGHT); langton.render(context); langton.update(); window.requestAnimationFrame((ts) => loop(ts)); } window.requestAnimationFrame((ts) => loop(ts));
結果
実行の様子を動画にしてみました。
動画作成の都合で WIDTH=630, HEIGHT=340,CELL_SIZE=4 として実行しています。
頑張って増えてるの可愛い。
参考文献・参考サイト
JavaScript
- 【JavaScript入門】連想配列(Dictionary)の取得/追加/ソートまとめ | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
- JavaScript まとめ - 文字列操作
- Array.prototype.slice() - JavaScript | MDN
- String.prototype.split() - JavaScript | MDN
- JavaScript 文字列を途中で改行する
- 2次元配列の初期化(ES2015) - Qiita
- 文字列を一文字ずつ分割して配列に入れる[Javascript] - Qiita
- JavaScriptで文字列を数値に変換する:Number(), parseInt(), parseFloat() | UX MILK
- JavaScriptの配列・連想配列のキーの存在チェック方法 | てらこや.work
- Math.round() - JavaScript | MDN