仮想計算機構

IT業界と無縁な派遣社員のブログ

CanvasによるByl's loop

はじめに

先日はCanvasを使ってラングトンのループを再現しました。

今回はよりシンプルな構造をもつ自己複製オートマトン、Bylのループを実装します。

  • Bylループ


Bylのループは当時Trinity Western大学の研究者だったJohn Bylが1989年に発表したモデルで、状態数6、初期配置12セルというコンパクトなものでした。フォンノイマン近傍から次のセル状態が決まる点はラングトンのループと同じです。自己複製の様子などもラングトンのループと似ているのですが、これは後ほど確認してみましょう。
また、Bylのループの初期配置の詳細は下記論文の図3をご覧ください。
http://fab.cba.mit.edu/classes/865.18/replication/Byl.pdf

プログラム

作成したプログラムは以下の通りです。

<html>
    <body>
        <script type="text/javascript" src="byl_rule.js"></script>
        <script type="text/javascript" src="byls_loops.js"></script>
    </body>
</html>

GitHub - GollyGang/ruletablerepository: Previously at https://code.google.com/p/ruletablerepository
↑のサイトにはラングトンのループの遷移規則があります。
Bylのループの遷移規則を記したファイルがあるので、そこからコピペして以下のように文字列で格納します。

const byl_rule_str = `000000
000010
000031
...
220533
221552`;

以下、メインのプログラムとなります。

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 = 5;
const N_ROW = Math.round(HEIGHT / CELL_SIZE);
const N_COL = Math.round(WIDTH / CELL_SIZE);

const byls_loops = [
    [0,2,2,0],
    [2,3,1,2],
    [2,3,4,2],
    [0,2,5,0]
];

const state2color = {"0":"black", "1":"blue", "2":"red", "3":"green",
                     "4":"yellow", "5":"magenta", "6":"white", "7":"cyan"};

class Byl{
    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);
        }
        // Byls Loopを配置する
        let offset_y = Math.round((N_ROW - byls_loops.length) / 2);
        let offset_x = Math.round((N_COL - byls_loops[0].length) / 2);

        for(let y=0;y<byls_loops.length;y++){
            for(let x=0;x<byls_loops[y].length;x++){
                this.field[y + offset_y][x + offset_x - 10] = byls_loops[y][x];
            }
        }
    }
    load_rule(){
       this.rules = {};
       let l = byl_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 byl = new Byl();

function loop(timestamp) {
  context.clearRect(0, 0, WIDTH, HEIGHT);
  byl.render(context);
  byl.update();
  window.requestAnimationFrame((ts) => loop(ts));
}

window.requestAnimationFrame((ts) => loop(ts));

結果

前述のプログラムを実行した様子をScreencastifyで録画しました。

ラングトンのループから感じた生き物っぽさが少し薄まっているように思いました。生き物っぽさとは何ぞやと問われると困ってしまいますが。