村农

莫笑农家腊酒浑,丰年留客足鸡豚。


生命游戏

冬冬

游戏规则介绍

生命游戏(Game of Life),或者叫它的全称John Conway's Game of Life。是英国数学家约翰·康威在1970年代所发明的一种元胞自动机。

所谓元胞自动机其实是一种离散的状态机,即无数个独立的格子,每个格子处于某种状态,然后所有格子按照预先设定好的规律进行状态演化。格子们可以是任意维度、任意形状、按任意规律排布的。 而生命游戏就是最简单的元胞自动机之一——在二维平面上的方格子(细胞),每个细胞有两种状态:死或活,而下一回合的状态完全受它周围8个细胞的状态而定。按照以下三条规则进行演化:

  1. 活细胞周围的细胞数如果小于2个或多于3个则会死亡;(离群或过度竞争导致死亡)
  2. 活细胞周围如果有2或3个细胞可以继续存活;(正常生存)
  3. 死细胞(空格)周围如果恰好有3个细胞则会诞生新的活细胞。(繁殖)

这三条规则简称B3/S23。如果调整规则对应的细胞数量,还能衍生出其他类型的自动机。

代码示例传送门

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>生命游戏</title>
    <style type="text/css">

    </style>
  </head>
    <canvas id="gameOfLive" width="1200" height="800"></canvas>
    <button id="start">开始</button>
    <button id="stop">停止</button>
  <body>

  </body>
  <script>
    var _canvas = document.getElementById('gameOfLive');
    var _canvasContent = _canvas.getContext('2d');
    var _cols = 120,
      _rows = 80,
      _canvasWidth = _canvas.width,
      _canvasHeight = _canvas.height,
      _unitWidth = _canvasWidth / _cols,
      _unitHeight = _canvasHeight / _rows;

    var cellsArr = [];

    var timePlay = null;

    initStage( _cols, _rows, _canvasWidth, _canvasHeight );
    initCells( _cols, _rows );

    function initStage () {
      _canvasContent.strokeStyle = 'rgba( 0, 51, 51, 0.8 )';
      _canvasContent.lineWidth = 0.1;

      _canvasContent.beginPath();
      for ( var i = 0; i <= _cols; i++ ) {
        _canvasContent.moveTo( i * _unitWidth, 0);
        _canvasContent.lineTo( i * _unitWidth, _canvasHeight);
      }

      for( var j = 0; j <= _rows; j++ ) {
        _canvasContent.moveTo( 0, j * _unitHeight);
        _canvasContent.lineTo( _canvasWidth, j * _unitHeight);
      }

      _canvasContent.stroke();
      _canvasContent.closePath();
    }

    function initCells ( cols, rows ) {
      for ( let i = 0; i < cols; i++ ) {
        cellsArr.push([]);
        for( let j = 0; j < rows; j++ ) {
          cellsArr[i].push( false );
        }
      }
    }

    function checkThisCellStatus ( x, y, temArr ) {
      let tempNumber = 0;
      for ( let i = -1; i <= 1; i++ ) {
        for ( let j = -1; j <= 1; j++ ) {
          if ( i == 0 && j == 0 ) {
            continue ;
          } else {
            if ( x + i >= 0 && x + i < _cols && y + j >= 0 && y + j < _rows ) {
              if( temArr[x+i][y+j] ) {
                tempNumber++;
              }
            }
          }
        }
      }

      if ( tempNumber > 3 || tempNumber < 2 ) {
        return false;
      } else if ( tempNumber == 3 ) {
        return true;
      } else {
        return temArr[x][y];
      }

    }

    function freshCanvas () {
      let temArr = [];
      for (let i = 0; i < _cols; i++ ) {
        let [...arr] = cellsArr[i];
        temArr.push(arr);
      }
      for ( let i = 0; i < _cols; i++ ) {
        for ( let j = 0; j < _rows; j++ ) {
          if ( checkThisCellStatus( i, j, temArr ) ) {
            drawThisCell( i, j );
          }  else {
            clearThisCell( i, j );
          }
        }
      }
    }

    function drawThisCell ( x, y ) {
      _canvasContent.fillStyle = '#FF0000';
      _canvasContent.fillRect( x * _unitWidth + 0.1, y * _unitHeight + 0.1, _unitWidth - 0.2, _unitHeight - 0.2 );
      _canvasContent.strokeRect( x * _unitWidth + 0.1, y * _unitHeight + 0.1, _unitWidth - 0.2, _unitHeight - 0.2 )
      cellsArr[x][y] = true;
    }

    function clearThisCell ( x, y ) {
      _canvasContent.fillStyle = '#FFFFFF';
      _canvasContent.fillRect( x * _unitWidth + 0.1, y * _unitHeight + 0.1, _unitWidth - 0.2, _unitHeight - 0.2 );
      _canvasContent.strokeRect( x * _unitWidth + 0.1, y * _unitHeight + 0.1, _unitWidth - 0.2, _unitHeight - 0.2 )
      cellsArr[x][y] = false;
    }

    document.getElementById('start').onclick = function () {
      timePlay = setInterval( () => {
        freshCanvas();
      }, 100 )
    }

    document.getElementById('stop').onclick = function () {
      clearInterval(timePlay)
    }

    _canvas.onmousedown = function ( e ) {
      var canvasY = e.pageY - _canvas.offsetTop;
      var canvasX = e.pageX - _canvas.offsetLeft;
      var martexX = Math.floor( canvasX / _unitWidth );
      var martexY = Math.floor( canvasY / _unitHeight );
      if ( cellsArr[martexX][martexY] ) {
        clearThisCell( martexX, martexY );
      } else {
        drawThisCell( martexX, martexY );
      }
    }

    // _canvas.onmousemove = function ( e ) {
    //   console.log(e);
    //   _canvasContent.strokeStyle = 'rgba(255, 100, 100, 0.8)';
    //   _canvasContent.lineWIdth = 5;
    //   _canvasContent.beginPath();
    //   _canvasContent.moveTo( 0, e.pageY - _canvas.offsetTop );
    //   _canvasContent.lineTo( _canvasWidth, e.pageY - _canvas.offsetTop );
    //   _canvasContent.moveTo( e.pageX - _canvas.offsetLeft, 0 );
    //   _canvasContent.lineTo( e.pageX - _canvas.offsetLeft, _canvasHeight );
    //   _canvasContent.stroke();
    //   _canvasContent.closePath();
    // }

  </script>
</html>