我正在为一个个人项目编写Conway的生命游戏细胞自动机的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();

规则逻辑在嵌套的for循环内部,Nsum是计算当前单元格的邻域和的函数.我说ncell [j][i]而不是ncell [i][j],因为在一个2d数组中,你首先寻址行.

我没有try 太多,但我想不出解决办法.救命啊

推荐答案

让我们先不要纠结行和列,因为(这是生活游戏的好处),这并不重要.唯一重要的是每个单元格的8个值around在做什么,所以只要我们坚持一个顺序,代码就会简单地做正确的事情.

这意味着我们可以摆脱这个Cell函数(注意,这不是你在JS中命名的方式.变量和函数使用lower CamelCase,类/构造函数使用UpperCamelCase,常量值使用UPPER_SNAKE_Case).

然后,让我们修正Nsum,因为它现在忽略了边缘,这不是生活游戏的工作方式:为了计算一个细胞有多少个邻居,我们需要将up to个8个值相加,但不是每个细胞都有8个邻居.例如,(0,0)在左边/上面没有任何东西.所以让我们重写一个循环:

function getNeighbourCount(i, j) {
  const cells = currentCells;
  let sum = 0;
  // run over a 3x3 block, centered on i,j:
  for (let u = -1; u <= 1; u++) {
    // ignore any illegal values, thanks to "continue";
    if (i + u < 0 || i + u >= cellCount) continue;
    for (let v = -1; v <= 1; v++) {
      // again, ignore any illegal values:
      if (j + v < 0 || j + v >= cellCount) continue;
      // and skip over [i][j] itself:
      if (u === 0 && v === 0) continue;
      sum += cells[i + u][j + v];
    }
  }
  return sum;
}

我们也可以利用这样一个事实,即我们know,我们设置我们的更新板为零之前,我们开始计算更新,所以我们不需要设置任何单元格为0:他们已经.

...
  // by copying the empty cells array, we start with
  // all-zeroes, so we don't need to set anything to 0.
  const next = structuredClone(emptyCells);

  for (let i = 0; i < cellCount; i++) {
    for (let j = 0; j < cellCount; j++) {
      // is this cell alive?
      const alive = currentCells[i][j] === 1;

      // we only need to calculate this once, not three times =)
      const neighbourCount = getNeighbourCount(i, j);

      if (alive && (neighbourCount === 2 || neighbourCount === 3)) {
        next[i][j] = 1;
      } else if (neighbourCount === 3) {
        next[i][j] = 1;
      }
    }
  }
...

因此,如果我们把所有这些放在一起,而不是使用画布,我们只使用一个预格式化的HTML元素,我们将网格打印到其中,我们得到:

const cellCount = 10;

// Let's define the same grid, but rather than harcoding it,
// let's just generate it off of a single number:
const emptyCells = [...new Array(cellCount)].map((_) => [...new Array(cellCount)].fill(0));

// Then, initialize the current cells from that empty grid.
let currentCells = structuredClone(emptyCells);

// To see things work, let's add a "glider"
[[0, 1],[1, 2],[2, 0],[2, 1],[2, 2]].forEach(([i, j]) => (currentCells[i][j] = 1));

// Then, our control logic: we'll have the sim run
// with a button to pause-resume the simulation.
let nextRun;
let paused = false;
toggle.addEventListener(`click`, () => {
  paused = !paused;
  if (paused) clearTimeout(nextRun);
  else runSimulation();
});

// And then: run the program!
showBoard();
runSimulation();

// It doesn't matter where we put functions in JS: the parser first
// reads in every function, and only *then* starts running, letting
// us organize things in terms of "the overall program" first, and
// then "the functions that our program relies on" after.

// draw our board with □ and ■ for dead and live cells, respectively.
function showBoard() {
  board.textContent = currentCells
    .map((row) => row.join(` `).replaceAll(`0`, `□`).replaceAll(`1`, `■`))
    .join(`\n`);
}

// our simulation just runs through the grid, updating
// an initially empty "next" grid based on the four
// Game of Life rules.
function runSimulation() {
  const next = structuredClone(emptyCells);
  for (let i = 0; i < cellCount; i++) {
    for (let j = 0; j < cellCount; j++) {
      const alive = currentCells[i][j] === 1;
      const neighbourCount = getNeighbourCount(i, j);
      if (alive && (neighbourCount === 2 || neighbourCount === 3)) {
        next[i][j] = 1;
      } else if (neighbourCount === 3) {
        next[i][j] = 1;
      }
    }
  }
  // update our grid, draw it, and then if we're not paused,
  // schedule the next call half a second into the future.
  currentCells = next;
  showBoard();
  if (!paused) {
    nextRun = setTimeout(runSimulation, 500);
  }
}

// In order to count how many neighbours we have, we need to
// sum *up to* eight values. This requires making sure that
// we check that a neighbour even exists, of course, because
// (0,0), for instance, has nothing to the left/above it.
function getNeighbourCount(i, j, cells = currentCells) {
  let sum = 0;
  for (let u = -1; u <= 1; u++) {
    if (i + u < 0 || i + u >= cellCount) continue;
    for (let v = -1; v <= 1; v++) {
      if (j + v < 0 || j + v >= cellCount) continue;
      if (u === 0 && v === 0) continue;
      sum += cells[i + u][j + v];
    }
  }
  return sum;
}
<pre id="board"></pre>
<button id="toggle">play/pause</button>

Javascript相关问答推荐

为什么我达到了时间限制!?LeetCode链接列表循环(已解决,但需要解释!)

如何在JavaScript中在文本内容中添加新行

对象和数字减法会抵消浏览器js中的数字

react/redux中的formData在expressjs中返回未定义的req.params.id

我开始使用/url?q=使用Cheerio

Cookie中未保存会话数据

为什么ngModel不能在最后一个版本的Angular 17上工作?'

如何将Openjphjs与next.js一起使用?

如何通过使用vanilla JS限制字体大小增加或减少两次来改变字体大小

覆盖加载器页面避免对页面上的元素进行操作

未加载css colored颜色 ,无法将div设置为可见和不可见

如何在Bootstrap中减少网格系统中单个div的宽度

使每个<;li>;元素的 colored颜色 与随机生成的 colored颜色 列表不同(不重复

有没有一种直接的方法可以深度嵌套在一个JavaScript对象中?

为什么延迟在我的laravel项目中不起作用?

将嵌套数组的元素乘以其深度,输出一个和

是否可以在不更改组件标识的情况下换出Reaction组件定义(以维护状态/引用等)?如果是这样的话,是如何做到的呢?

自动滚动功能在当前图像左侧显示上一张图像的一部分

Reaction即使在重新呈现后也会在方法内部保留局部值

ComponentWillReceiveProps仍在React 18.2.0中工作