前回のライフゲームをちょっとだけES2015で書き換えてみた


2017年10月29日
With
前回のライフゲームをちょっとだけES2015で書き換えてみた はコメントを受け付けていません。

「ジェネラティブ・アート」を読みながら、掲載されているProcessing向けのコードをp5.jsで動くように書き換えるという作業は数カ月前に集中してやっていたのですが、つい最近、ES2015での便利そうな書き方をいくつか知ることができたので、前回の「コンウェイのライフゲーム」のコードの一部をES2015対応に書き換えてみました。

下のデモ、動作は前のエントリのものとまったく同じですが、コードは一部ES2015準拠に書き換えています。

●[DEMO] Conway’s Game of Life for p5.js ES2015一部対応版(クリックすると別タブが開きます)

コードは、以下のようになりました。前回のものと比べて、10行弱ほど短くなっています。

const _cellSize = 10;
let _cellArray;
let _numX, _numY;

function setup() {
    createCanvas(600, 600);
    frameRate(10);
    _numX = floor(width / _cellSize);
    _numY = floor(height / _cellSize);
    _cellArray = Array.from(new Array(_numX), () => new Array(_numY).fill(0));
    restart();
}

function restart() {
    for (let x = 0; x < _numX; x++) {
        for (let y = 0; y < _numY; y++) {
            let newCell = new Cell(x, y);
            _cellArray[x][y] = newCell;
        }
    }
    for (let x = 0; x < _numX; x++) {
        for (let y = 0; y < _numY; y++) {

            let above = y - 1;
            let below = y + 1;
            let left = x - 1;
            let right = x + 1;

            if (above < 0) { above = _numY - 1; }
            if (below == _numY) { below = 0; }
            if (left < 0) { left = _numX - 1; }
            if (right == _numX) { right = 0; }

            _cellArray[x][y].addNeighbour(_cellArray[left][above]);
            _cellArray[x][y].addNeighbour(_cellArray[left][y]);
            _cellArray[x][y].addNeighbour(_cellArray[left][below]);
            _cellArray[x][y].addNeighbour(_cellArray[x][below]);
            _cellArray[x][y].addNeighbour(_cellArray[right][below]);
            _cellArray[x][y].addNeighbour(_cellArray[right][y]);
            _cellArray[x][y].addNeighbour(_cellArray[right][above]);
            _cellArray[x][y].addNeighbour(_cellArray[x][above]);
        }
    }
}

function draw() {
    background(200);
    _cellArray.forEach(cellX => cellX.forEach(cellXY => { cellXY.calcNextState(); }));
    translate(_cellSize / 2, _cellSize / 2);
    _cellArray.forEach(cellX => cellX.forEach(cellXY => { cellXY.drawMe(); }));
}

function mousePressed() {
    restart();
}

class Cell {
    constructor(ex, why) {
        this.x = ex * _cellSize;
        this.y = why * _cellSize;
        if (random(2) > 1) {
            this.nextState = true;
        } else {
            this.nextState = false;
        }
        this.state = this.nextState;
        this.neighbours = [];
    }

    addNeighbour(cell) {
        this.neighbours.push(cell);
    }

    calcNextState() {
        let liveCount = 0;
        for (let i = 0; i < this.neighbours.length; i++) {
            if (this.neighbours[i].state == true) {
                liveCount++;
            }
        }
        if (this.state == true) {
            if ((liveCount == 2) || (liveCount == 3)) {
                this.nextState = true;
            } else {
                this.nextState = false;
            }
        } else {
            if (liveCount == 3) {
                this.nextState = true;
            } else {
                this.nextState = false;
            }
        }
    }

    drawMe() {
        this.state = this.nextState;
        stroke(0);
        strokeWeight(1);
        if (this.state == true) {
            fill(0);
        } else {
            fill(255);
        }
        ellipse(this.x, this.y, _cellSize, _cellSize);
    }
}

書き換えのポイントとしては、

1.変数宣言の「var」を「const」「let」に
2.Cellオブジェクトの定義を「class」を使ったものに
3.setup()にある_cellArray(2次元配列)の初期化処理を「Array.from」とアロー関数を使ったものに(10行目)
4.draw()にある世代更新メソッドと描画の実行を「Array.forEach」とアロー関数を使ったものに(48、50行目)

といった感じです。オブジェクト定義に「class」が使えるのはいいですね。JavaScriptに詳しい方の間では、classの利用に関していろいろと議論もあるようなのですが、コンストラクタとメソッドを同じブレースのくくりの中でシンプルにまとめられるのは、僕としてはスッキリしているように思います。

それに輪をかけて画期的に感じたのは3と4です。2次元配列の初期化処理と、そこに収めたオブジェクトが持つメソッドの順次実行の部分なんですが、以前は二重のfor文を使って4行ほどで書いていた部分が、全部1行になりました。この手の処理は特にp5.jsを使ったスケッチでは良く使うと思うので、今後は積極的にこのスタイルで書いていこうと思います。

Arrayのメソッドについては「MDN web docs」の「Array」の項を見ながら、おっかなびっくり使ってみたのですが、問題なく動いているようなのでたぶん合っているんでしょう(いいかげん)。恐らく、その他の処理の部分についても、もっとスマートな書き方があるのだと思いますが、ひとまず今回はこのくらいで。

にしても、MDNを見ていて思ったのですが、Arrayのメソッドって、うまく使うと幸せになれそうなものがいっぱいありますね。必要なタイミングで随時覚えて、使いこなせるようになりたいです。