MENU

blog
スタッフブログ

dot
ライフゲームを作ってみた
技術

ライフゲームを作ってみた

こんにちは、クリエイティブSecの長谷川です。
今回は、ふとライフゲームというものを作るのはどんな感じなのか?と疑問に思ったこともあり
かなりシンプルなものになりましたが、パパっと作ってみたので紹介します。

そもそもライフゲームとは?

ライフゲームって何?という方もおられると思うのでWikipediaから
概要を抜粋したものを下記に記載します。

ライフゲーム (Conway’s Game of Life[1]) は1970年イギリス数学者ジョン・ホートン・コンウェイ (John Horton Conway) が考案した数理モデルである。単純なルールから複雑な結果が生成され、パズルミニスケープの要素を持っている。生命の誕生、進化淘汰などのプロセスを連想させるパターンも存在し、シミュレーションゲームと分類される場合がある。

生物集団においては、過疎でも過密でも個体の生存に適さないという個体群生態学的な側面を背景に持つ。セル・オートマトンのもっともよく知られた例でもある。

https://ja.wikipedia.org/wiki/ライフゲーム

これだけだと、ゲームのイメージがわかないと思いますが
以下の図のように、升目がたくさんある盤面上で生命がいるブロックとそうでないブロックがあり
世代を進めることにルールに従って、生命の誕生、生存、死滅をシミュレーションしてくものとなります。

ライフゲームのルール

ライフゲームのルールは以下の4つしかなく、いたってシンプルなものになります。

  1. 死んでいるセルに隣接する生きたセルがちょうど3つあれば、次の世代が誕生する。
  2. 生きているセルに隣接する生きたセルが2つか3つならば、次の世代でも生存する。
  3. 生きているセルに隣接する生きたセルが1つ以下ならば、過疎により死滅する。
  4. 生きているセルに隣接する生きたセルが4つ以上ならば、過密により死滅する。

実際に作ったものがこちら

初期状態は、すべて死滅したセルになっていますので
マウスで任意の場所をクリックしたり、クリックしながらドラッグしてセルを塗りつぶしてください。
その後、Startボタンを押すとシミュレーションが始まり、世代を進めていきます。

ソースコードについては、今回Vue.jsで作っていますが
機能もわりとシンプルなので、そこまでのボリュームにはなりませんでした。

vue createコマンドでデフォルトで生成されるmain.jsやApp.vueのソースコードは省略しますが
ざっと書いたコードは以下の通りです。

<template>
    <h1>LifeGame</h1>
    <p>第 <span v-text="generation"></span> 世代</p>
    <table id="board">
        <tr v-for="y in rowNum" :key="'row-' + y">
            <td v-for="x in colNum" :key="'cell' + y * colNum + x"
                @click="toggleState((y - 1) * colNum + x - 1)" @mouseenter="mouseEnter($event, (y - 1) * colNum + x - 1)"
                :class="cellState[(y - 1) * colNum + x - 1] ? 'live' : ''">
            </td>
        </tr>
    </table>
    <div class="buttons">
        <button type="button" class="btn btn-primary" @click="start" :disabled="isStarted">Start</button>
        <button type="button" class="btn btn-primary" @click="stop" :disabled="!isStarted">Stop</button>
        <button type="button" class="btn btn-primary" @click="clear">clear</button>
    </div>
</template>

<script>
export default {
    name: 'LifeGame',
    data() {
        return {
            generation: 0,
            rowNum: 20,
            colNum: 50,
            intervalVar: null,
            cellState: new Array(20 * 50).fill(false),
            isStarted: false,
        }
    },
    mounted() {
    },
    methods: {
        start() {
            // 0.1秒に1回実行する
            this.isStarted = true
            this.intervalVar = setInterval(this.check, 100)
        },
        stop() {
            // 進行を止める
            clearInterval(this.intervalVar)
            this.isStarted = false
        },
        clear() {
            // リセットする
            this.stop()
            this.generation = 0
            this.cellState = new Array(20 * 50).fill(false)
        },
        toggleState(index) {
            this.cellState[index] = !this.cellState[index]
        },
        mouseEnter(event, index) {
            // クリック状態であれば
            if (event.buttons != 1) {
                return;
            }
            this.cellState[index] = true;
        },
        check() {
            const newCellState = this.cellState.slice();
            for (var y = 1; y <= this.rowNum; y++) {
                for (var x = 1; x <= this.colNum; x++) {
                    var aliveCount = 0;

                    for (var dy = -1; dy <= 1; dy++) {
                        for (var dx = -1; dx <= 1; dx++) {
                            if (dy == 0 && dx == 0) {
                                continue;
                            } else if (y + dy < 1 || y + dy > this.rowNum || x + dx < 1 || x + dx > this.colNum) {
                                continue;
                            }

                            if (this.cellState[(y + dy - 1) * this.colNum + x + dx - 1]) {
                                aliveCount++;
                            }
                        }
                    }

                    if (aliveCount == 3) {
                        newCellState[(y - 1) * this.colNum + x - 1] = true;
                    } else if (aliveCount != 2) {
                        newCellState[(y - 1) * this.colNum + x - 1] = false;
                    }
                }
            }

            if (newCellState.toString() == this.cellState.toString()) {
                this.stop()
            }
            this.cellState = newCellState
            this.generation++;
        }
    },
}
</script>

<style>
#board {
    border: solid 1px black;
    border-collapse: collapse;
    margin: 0 auto;
}

#board td {
    border: solid 1px black;
    width: .5em;
    height: .5em;
}

#board td.live {
    background-color: black;
}
</style>

ライフゲームの初期配置にはパターンがある

概要で紹介したWikipediaにもありますが
ライフゲームでは固定物体や振動子、移動物体、繁殖型といったパターンがあります。
これらを試してみるのも面白いと思います。

最後に

今回、ライフゲームを作ってみましたが、思ったほど複雑な内容ではなく
わりと簡単に作成することができました。

ただ、ほかのサイトでは先ほど紹介したパターンを簡単に設置できるボタンがあったり
進行速度の間隔を調整出来たり、利便性を図ったものもありますので
その辺も作成すると、もうちょっと手間がかかるかとは思いますが
基本ロジック自体は、理解さえできればそこまで複雑ではないと思います。

すでにいろんな方が作っているものですが、これを自分が作れるだろうかと挑戦してみるのも面白いですね。

それでは今回は、この辺で。

dot
dot
PAGETOP