React实现生命游戏

功能简介

生命游戏是一个零玩家游戏。它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。

游戏规则:

  1. 每个细胞有两种状态-存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。
  2. 当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。
  3. 当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
  4. 当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。
  5. 当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。

这里通过React用不超过300行的代码实现了生命游戏,实现效果如下图所示:
效果图

在线预览

在线预览

游戏需求

游戏应包含的基本功能有:

  1. 玩家能够改变游戏状态,包括开始游戏、暂停游戏和结束游戏
  2. 玩家能够通过点击矩形世界中的方格切换细胞的存活状态
  3. 玩家能够控制细胞演化速度
  4. 玩家能够调整矩形世界的大小

实现思路

  1. 生命游戏实际上是一个反映二维数组状态变化的过程,数组中各元素下一刻的状态取决于当前时刻周围元素的值,因此需要一个数组用于表示当前各细胞的状态(cells)。
  2. 玩家可以控制游戏的状态(开始、暂停、结束),因此需要有一个用于表示游戏状态的元素(gameStatus)。
  3. 玩家可以通过点击切换细胞存活状态,因此方格中个元素应该实现onClick函数,切换数组中元素的状态(toggleCellState)。
  4. 玩家可以控制游戏的速率,可以想到用setInterval,需要一个用于表示时间的变量(interalTime)。
  5. 玩家可以调整矩形世界的大小,需要width、height进行表示(width/height)

综上,可以建立如下变量:

class Game extends React.Component{
    constructor(props){
    super(props);
    this.state = {
        width: 50,
        height: 30,
        gen: 0,
        interalTime: 20,
        gameStatus: START,
        cells: [],

        ...
    }
    ...
}
...

采用二维数组(cells)将元素映射到页面上。当数组元素为false时为其添加dead类,toggleCellState用于切换数组元素的值:

<div className = "clear-all">
  {
    this.state.cells.map((row, i) =>
      <div  key={i} className="game-row">
        {
          row.map((col, j) => <div onClick={this.toggleCellState.bind(this)} \
                    key={j} \
                    className={'cell ' + (this.state.cells[i][j] ? '' : 'dead')} \
                    id={i * this.state.width + j}></div>
        )}
      </div>
  )
}
</div>

toggleCellState-切换数组元素的值:

    toggleCellState(e){
        var i = parseInt(e.target.id);
        var iX = Math.floor(i / this.state.width);
        var iY = i % this.state.width;
        var arr = this.state.cells;
        arr[iX][iY] = !this.state.cells[iX][iY];
        this.setState({cells: (arr)});
      }

在游戏进行的过程中,实际上是Game类中数组状态不断变化的过程。根据游戏规则可以完成函数calNextStatus如下(这里主要需要考虑的是边界状态的情况,大多元素周围有8个元素,但处于上下左右四边的元素需要特殊考虑):

    calNextStatus(){
        if(this.state.gameStatus != START){
          return;
        }
        var oriArr = this.state.cells;
        var nextArr = oriArr;

        for(var i = 0; i < this.state.height; i++){
          for(var j = 0; j < this.state.width; j++){
                ...
            if(oriArr[i][j]){
              nextArr[i][j] = (count < 2 || count > 3) ? false : true;
            }else{
              nextArr[i][j] = (count == 3);
            }
          }
        }
        var curGen = this.state.gen;
        this.setState({
          gen: ++curGen,
          cells: nextArr
        });
      }

updateGameState-控制游戏的状态(开始、暂停、结束),更新gameStatus的值

updateGameState(param){
    if(param == STOP){
        var arr = [];
        for(var i = 0 ; i < this.state.height ; i++){
            arr[i] = [];
            for(var j = 0 ; j < this.state.width ; j++){
                arr[i][j] = false;
            }
        }
      this.setState({cells: arr});
    }
this.setState({gameStatus: param});
}

updateInterval-控制游戏的速率,更新定时器

updateInterval(param){
    clearInterval(this.state.timerId);
    switch(param){
        case SLOW:        
            this.setState({timerId: setInterval(this.calNextStatus, 600)});
            break;
        case MIDDLE:
            this.setState({timerId: setInterval(this.calNextStatus, 300)});
            break;
        case FAST:
            this.setState({timerId: setInterval(this.calNextStatus, 20)});
            break;
    }
}

updateSize-调整矩形世界的大小,修改width和height,并重新对cells进行初始化

updateSize(param){
    var tempArr = [];
    var a = 50, b = 30;
    if(param == MIDDLE){
        a = 70;
        b = 50;
    }
    if(param == LARGE){
        a = 100;
        b = 80;
    }
    for(var i = 0 ; i < b ; i++){
        tempArr[i] = [];
        for(var j = 0 ; j < a ; j++){
            tempArr[i][j] = Math.random() > initLiveRate;
        }
    }
    this.setState({cells: tempArr, width: a, height: b});
}

源码地址

我把代码整合成了一个html方便放在了github上,方便参考->地址