用table表格实现生命游戏

304 阅读2分钟

项目在线网址

zhao-lian.github.io/font-end-mi…

有兴趣的可以试一试

什么是生命游戏

百度百科是这样说明的:

生命游戏是一个零玩家游戏,它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去;相反,如果周围活细胞过少,这个细胞会因太孤单而死去。

具体的规则如下:

  1. 如果一个细胞周围有3个细胞为生(一个细胞周围共有8个细胞),则该细胞为生(即该细胞若原先为死,则转为生,若原先为生,则保持不变);
  2. 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变;
  3. 在其它情况下,该细胞为死(即该细胞若原先为生,则转为死,若原先为死,则保持不变)。

玩法

只需要设定好初始存活的细胞,点击开始,然后就可以欣赏生命的演化之旅。

特殊生命

滑翔机

这是一个可以不断朝着右下角运动的生命

滑翔机枪

制造滑翔机的工厂,可以不断生成滑翔机

具体实现

绘制地图

看到方格,就想到了表格,那就用<table>吧! 生成元素时,将行号保存到<tr>的自定义属性中,将列号保存到每个<td>中。

数据

数据保存到二维数组中,正好每个值对应一个细胞的状态。

var row = parseInt((window.innerHeight - 45) / 15);
var col = parseInt(window.innerWidth / 15);
var tableList = [];
var tableRowList = [];
var table = document.createElement('table');
var tableRow = document.createElement('tr');
var cell = document.createElement('td');
// 初始化地图
for (var i = 0; i < col; i++) {
    cell.dataset.col = i;
    tableRow.appendChild(cell.cloneNode());
    tableRowList.push(0);
}
for (var i = 0; i < row; i++) {
    tableRow.dataset.row = i;
    table.appendChild(tableRow.cloneNode(true));
    tableList.push(tableRowList.slice());
}
document.body.appendChild(table);

点击改变状态

点击细胞切换细胞的状态,根据不同状态设定不同的背景颜色

function change(e) {
    if (e.target.nodeName == 'TD') {
        var row = e.target.parentNode.dataset.row;
        var col = e.target.dataset.col;
        if (tableList[row][col] == 0) {
            tableList[row][col] = 1;
        } else {
            tableList[row][col] = 0;
        }
        e.target.style.backgroundColor = colorList[tableList[row][col]];
    }
}
table.addEventListener('click', change);

拖动改变状态

一个一个点效率还是太低了,于是我就想到了拖动鼠标,按下鼠标添加事件,抬起鼠标移除事件。

这里需要注意两点:

  1. 同一个细胞上可以触发多次mousemove,需要添加一层判断,如果本次事件跟上次事件是同一个target,则不改变状态
  2. 鼠标mouseup一定要绑定在document上,否则按住鼠标离开浏览器,然后再松开,是不会触发mouseup
var preTarget = null;
function moveChange(e) {
    if (e.target != preTarget) {
        change(e);
        preTarget = e.target;
    }
}
document.addEventListener('mousedown', function () {
    table.addEventListener('mousemove', moveChange);
})
document.addEventListener('mouseup', function () {
    table.removeEventListener('mousemove', moveChange);
})

计算下一回合的状态

需要遍历每个细胞周围八个细胞的本回合状态,将计算完成的下一回合数据存储在一个新的二维数组中。

function compute() {
    // 初始化新数组
    var tableListNext = [];
    for (var i = 0; i < row; i++) {
        var arr = [];
        for (var j = 0; j < col; j++) {
            arr.push(0);
        }
        tableListNext.push(arr);
    }
    // 遍历确定状态
    for (var i = 0; i < tableList.length; i++) {
        for (var j = 0; j < tableList[i].length; j++) {
            var count = 0;
            for (var k = -1; k <= 1; k++) {
                for (var l = -1; l <= 1; l++) {
                    if (k == 0 && l == 0) {
                        continue;
                    } else {
                        if (i + k < 0 || j + l < 0 || i + k >= tableList.length || j + l >= tableList[i]
                            .length) {
                            continue;
                        } else {
                            count += tableList[i + k][j + l];
                        }
                    }
                }
            }
            // 确定生存或者死亡
            if (tableList[i][j] == 1) {
                if (count < 2 || count > 3) {
                    tableListNext[i][j] = 0;
                } else {
                    tableListNext[i][j] = 1;
                }
            } else {
                if (count == 3) {
                    tableListNext[i][j] = 1;
                } else {
                    tableListNext[i][j] = 0;
                }
            }
        }
    }
    tableList = tableListNext;
    render();
    }

渲染

将数据同步到每个细胞的背景状态

function render() {
    for (var i = 0; i < tableList.length; i++) {
        for (var j = 0; j < tableList[i].length; j++) {
            table.children[i].children[j].style.backgroundColor = colorList[tableList[i][j]];
        }
    }
}