You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript实现康威生命游戏规则失效,求助问题排查

康威生命游戏规则失效问题排查与修复

问题背景

正在开发JavaScript版本的康威生命游戏,核心逻辑是计算每个细胞的下一状态存入网格副本,完成所有计算后替换原网格状态,但规则未正常生效。以下是问题代码:

//10x10 grid
let ecells = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];

let cells = empty_cells;

let new_cells = cells;

let paused = true;

function Cell(x, y) {
    return cells[y][x];
}

function Nsum(i, j) {
    if (i >= 1 && j >= 1) {
        return Cell(i - 1, j) + Cell(i + 1, j) + Cell(i, j - 1) + Cell(i - 1, j - 1) + Cell(i + 1, j - 1) + Cell(i, j + 1) + Cell(i - 1, j + 1) + Cell(i + 1, j + 1);
    }
}

//One can manually change the state of the cells in the "cells" grid, 
//which works correctly. Then, one can run the CA by changing the "paused"
//value to false.

function simulation() {
    for (i = 0; i < cells[0].length; i++) {
        for (j = 0; j < cells.length; j++) {
            if (Cell(i, j)) {
                ctx.fillRect(20*i - 0.5, 20*j, 20, 20);
                if (!paused) {
                    if (Nsum(i, j) == 2 || Nsum(i, j) == 3) new_cells[j][i] = 1;
                    else new_cells[j][i] = 0;
                }
            }
            else {
                ctx.clearRect(20*i - 0.5, 20*j, 20, 20);
                if (!paused) {
                    if (Nsum(i, j) == 3) new_cells[j][i] = 1;
                    else new_cells[j][i] = 0;
                }
            }
        }
    }
    if (!paused) cells = new_cells;
    new_cells = empty_cells;
    requestAnimationFrame(simulation);
}

simulation();

问题诊断

代码存在以下几个关键错误:

  • 变量名拼写错误:let cells = empty_cells;中empty_cells未定义,应为ecells,导致初始网格异常。
  • 数组引用复用:new_cells = cells和new_cells = empty_cells直接赋值数组引用,修改new_cells会直接改变原cells或ecells,导致计算下一状态时使用中途修改的数值。
  • 邻域计算边界缺失:Nsum函数仅处理i >= 1 && j >= 1的情况,边缘、角落细胞会返回undefined,无法正确判断存活规则。
  • 全局循环变量:for循环中的i和j未用let/const声明,成为全局变量,可能引发循环逻辑异常。
  • 副本初始化错误:每次循环后重置new_cells时,未创建新的空网格副本,复用同一空数组引用导致状态覆盖混乱。

修复方案

以下是修正后的代码,关键修改点已标注:

//10x10 grid
const ecells = Array.from({ length: 10 }, () => Array(10).fill(0)); // 简洁创建空网格

let cells = [...ecells.map(row => [...row])]; // 深拷贝初始网格,避免引用污染
let paused = true;

// 边界安全的细胞状态获取,超出网格的细胞视为0
function getCellState(x, y) {
    if (x < 0 || x >= cells[0].length || y < 0 || y >= cells.length) {
        return 0;
    }
    return cells[y][x];
}

// 通用邻域存活细胞计算,覆盖所有位置
function getNeighborSum(i, j) {
    let sum = 0;
    // 遍历8个方向邻居
    for (let dx = -1; dx <= 1; dx++) {
        for (let dy = -1; dy <= 1; dy++) {
            if (dx === 0 && dy === 0) continue; // 跳过自身
            sum += getCellState(i + dx, j + dy);
        }
    }
    return sum;
}

function simulation() {
    // 每次循环创建全新的新状态网格
    const newCells = Array.from({ length: 10 }, () => Array(10).fill(0));

    // 局部循环变量,避免全局污染
    for (let i = 0; i < cells[0].length; i++) {
        for (let j = 0; j < cells.length; j++) {
            // 渲染当前细胞状态
            if (getCellState(i, j)) {
                ctx.fillRect(20*i - 0.5, 20*j, 20, 20);
            } else {
                ctx.clearRect(20*i - 0.5, 20*j, 20, 20);
            }

            // 非暂停状态下计算下一状态
            if (!paused) {
                const neighborSum = getNeighborSum(i, j);
                const currentState = getCellState(i, j);
                
                // 应用康威生命游戏规则
                newCells[j][i] = currentState === 1 
                    ? (neighborSum === 2 || neighborSum === 3 ? 1 : 0) 
                    : (neighborSum === 3 ? 1 : 0);
            }
        }
    }

    // 更新原网格状态
    if (!paused) {
        cells = newCells;
    }

    requestAnimationFrame(simulation);
}

simulation();

关键修改说明

  1. 深拷贝初始网格:用[...ecells.map(row => [...row])]创建独立的初始网格,避免后续修改影响原空网格。
  2. 边界安全处理:getCellState函数将网格外的细胞视为死亡状态,解决边缘细胞邻域计算问题。
  3. 通用邻域计算:通过双层循环遍历8个方向邻居,覆盖所有位置的细胞,无需单独判断边界。
  4. 独立新状态网格:每次循环创建全新的newCells数组,确保计算基于原网格的初始状态。
  5. 局部循环变量:用let声明i和j,避免全局变量污染。
  6. 清晰规则逻辑:简化规则判断代码,提升可读性。

内容的提问来源于stack exchange,提问作者Emmanuel Giouvanopoulos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:17:34