スクリーンショット_2020-02-25_午後10

Vue + SVGでセル・オートマトンルール30を描く

セル・オートマトンについて触れる機会があり、実際に手を動かしてみた方が勉強になるかと、学習してみたのでそのメモnoteを残します。

セル・オートマトン?

ウィキペディアより
セル・オートマトン(英: cellular automaton、略称:CA)とは、格子状のセルと単純な規則による、離散的計算モデルである。計算可能性理論、数学、物理学、複雑適応系、数理生物学、微小構造モデリングなどの研究で利用される。

ざっくりと砕けた説明をするならば、「格子状のセルの色(状態:0 or 1)を、他のセルの色とあるルールに従って決めるもの」でしょうか...。実際に手を動かした方が理解が早いので、実際にVue(JavaScript)コードを書いてみましょう。

ルール30?

セル・オートマトンにはいくつかのルールがあり、その1つがルール30と呼ばれるものです。

画像1

ルール30を適応すると、上の画像のような入力に対する出力になります。Vueの関数(methods)にしてみるとこんな感じ:

methods: {
    rule30(p1, p2, p3) {
        if (p1 === 1 && p2 === 1 && p3 === 1) {
            return 0;
        }
        if (p1 === 1 && p2 === 1 && p3 === 0) {
            return 0;
        }
        if (p1 === 1 && p2 === 0 && p3 === 1) {
            return 0;
        }
        if (p1 === 1 && p2 === 0 && p3 === 0) {
            return 1;
        }
        if (p1 === 0 && p2 === 1 && p3 === 1) {
            return 1;
        }
        if (p1 === 0 && p2 === 1 && p3 === 0) {
            return 1;
        }
        if (p1 === 0 && p2 === 0 && p3 === 1) {
            return 1;
        }
        return 0;
    },
},

実際にSVGでセル・オートマトンを描いてみる

rule30の関数は用意できたので、実際にSVGでセル・オートマトンを描いてみましょう。

はじめにSVGで縦100コ × 横200コの格子(セル)を用意します。(SVGのviewBoxの設定は0 0 200 100)にします。

<template>
    <div>
        <svg viewBox="0 0 200 100" stroke="currentColor" stroke-width=".1" fill="none">
            <g v-for="(row, rowIndex) in 100" :key="rowIndex">
                <g v-for="(cell, cellIndex) in 200" :key="cellIndex">
                    <rect :x="cellIndex" :y="rowIndex" width="1" height="1">
                    </rect>
                </g>
            </g>
        </svg>
    </div>
</template>

ブラウザで表示するとこんな感じ(モアレが発生していますが...):

スクリーンショット 2020-02-25 午後11.26.16

マス目ができましたね!この1つ1つのマス目(SVGのrect要素)に、初期条件を設定して、セル・オートマトンのルール30を適応させましょう。

セルの初期条件を設定し、ルール30を描画する

今回は縦100コ × 横200コの格子を用意しました。初期条件は、1行目の200コの格子に設定します。1行目の中央の格子だけを黒に設定し、残りの199行はルール30を反映させます。

export default {
    data() {
        return {
            cells: [], // 縦100 × 横200の行列(2次元配列)を入れる変数
        };
    },
    methods: {
        // ルール30の関数を定義
        rule30(p1, p2, p3) {
            if (p1 === 1 && p2 === 1 && p3 === 1) {
                return 0;
            }
            if (p1 === 1 && p2 === 1 && p3 === 0) {
                return 0;
            }
            if (p1 === 1 && p2 === 0 && p3 === 1) {
                return 0;
            }
            if (p1 === 1 && p2 === 0 && p3 === 0) {
                return 1;
            }
            if (p1 === 0 && p2 === 1 && p3 === 1) {
                return 1;
            }
            if (p1 === 0 && p2 === 1 && p3 === 0) {
                return 1;
            }
            if (p1 === 0 && p2 === 0 && p3 === 1) {
                return 1;
            }
            return 0;
        },
    },
    created() {
        this.cells[0] = []; // 1行目の格子の情報を入れる配列(初期条件)を定義
        for (let i = 0; i < 200; i += 1) {
            this.cells[0][i] = i === 100 ? 1 : 0; // 横100番目の格子だけ1(黒)を設定
        }

        // 残りの199行分をループする
        for (let i = 1; i < 100; i += 1) {
            this.cells[i] = [];
            // i行目の200格子分をループする
            for (let j = 0; j < 200; j += 1) {
                // methodsで定義したrule30関数を利用する
                this.cells[i][j] = this.rule30(
                    this.cells[i - 1][j - 1], // 色を決める格子の左上の格子の情報を引数として渡す
                    this.cells[i - 1][j], // 色を決める格子の上の格子の情報を引数として渡す
                    this.cells[i - 1][j + 1], // 色を決める格子の右上の格子の情報を引数として渡す
                );
            }
        }
    },
}

Vueのコードはこれで完成です。あとはルール30とVueによって生成した二次元配列を、SVGのrect要素に反映させます。

<template>
    <div>
        <svg class="bg-white" viewBox="0 0 200 100"
           stroke="currentColor" stroke-width=".1" fill="none"
         >
             <g v-for="(row, rowIndex) in cells" :key="rowIndex">
                 <g v-for="(cell, cellIndex) in row" :key="cellIndex">
                     <rect :x="cellIndex" :y="rowIndex" width="1" height="1"
                         :fill="cell === 1 ? 'black' : 'none'"
                     ></rect>
                 </g>
             </g>
         </svg>
    </div>
</template>

これをブラウザで見てみると...?

スクリーンショット 2020-02-25 午後11.55.35

できた!こんな単純な関数でこんな不思議な形が出来上がります。

ルール90も描画してみる

ついでですが、ルール90も描画してみましょう。ルール90の関数はこんな感じ:

export default {
    data() {
        return {
            cells: [], // 縦100 × 横200の行列(2次元配列)を入れる変数
        };
    },
    methods: {
        rule90(p1, p2, p3) {
            if (p1 === 1 && p2 === 1 && p3 === 1) {
                return 0;
            }
            if (p1 === 1 && p2 === 1 && p3 === 0) {
                return 1;
            }
            if (p1 === 1 && p2 === 0 && p3 === 1) {
                return 0;
            }
            if (p1 === 1 && p2 === 0 && p3 === 0) {
                return 1;
            }
            if (p1 === 0 && p2 === 1 && p3 === 1) {
                return 1;
            }
            if (p1 === 0 && p2 === 1 && p3 === 0) {
                return 0;
            }
            if (p1 === 0 && p2 === 0 && p3 === 1) {
                return 1;
            }
            return 0;
        },
    },
    created() {
        this.cells[0] = [];
        for (let i = 0; i < 200; i += 1) {
            this.cells[0][i] = i === 100 ? 1 : 0;
        }

        for (let i = 1; i < 100; i += 1) {
            this.cells[i] = [];
            for (let j = 0; j < 200; j += 1) {
                this.cells[i][j] = this.rule90(
                    this.cells[i - 1][j - 1],
                    this.cells[i - 1][j],
                    this.cells[i - 1][j + 1],
                );
            }
        }
    },
},

こうすると...?

スクリーンショット 2020-02-26 午前0.04.26

Wow!

いいなと思ったら応援しよう!