生命游戏 p5.js 无法更新单元格
Game of Life p5.js trouble updating cells
我在 p5.js 中实现了 Conway 的人生游戏。
我有一个函数可以计算给定细胞的存活邻居的数量,从我所做的测试看来它工作得很好。话虽如此,我在模拟时遇到了意想不到的行为:它没有遵循正确的模式。我从 3 个对齐的正方形(一个信号灯)开始,它应该旋转,但由于某种原因它们都死了。
维基百科显示了信号灯的行为方式:https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Examples_of_patterns
您可以将下面的源代码复制粘贴到 https://editor.p5js.org/ 中并亲自查看。
我怀疑这个问题与电路板(单元矩阵)的复制有关,因为所有单元的更新必须同时进行,因此我们保留前一个电路板并更新当前电路板棋盘基于之前的棋盘。
来源:
//size of cell in pixels.
const size = 20;
const canvasWidth = 400;
const canvasHeight = 400;
const n = canvasWidth / size;
const m = canvasHeight / size;
class Cell {
constructor(status, position) {
this.status = status;
this.position = position;
}
}
let board = [];
function setup() {
//initialise matrix.
for(let i=0; i < m; i++) {
board[i] = new Array(n);
}
//fill matrix of cells.
for(let i=0; i < m; i++ ) {
for(let j=0; j < n; j++ ) {
board[i][j] = new Cell(false, [i*size, j*size]);
}
}
console.log("start", floor(m /2), floor(n / 2))
//[x, y] positions.
board[floor(m /2)][floor(n / 2)].status = true;
board[floor(m /2)+1][floor(n / 2)].status = true;
board[floor(m /2)+2][floor(n / 2)].status = true;
createCanvas(canvasWidth, canvasHeight);
frameRate( 1 )
}
//I'm 99.99% sure this function works, all the tests shows it does.
function updateCell(i, j, previousBoard) {
let count = 0;
//check neighbourgh cells.
for(let k=-1; k < 2; k++) {
for(let p=-1; p < 2; p++) {
if( !(i + k < 0 || j + p < 0 || j + p >= n || i + k >= m) ) {
if(k != 0 || p != 0) {
if(previousBoard[i+k][j+p].status === true) {
count++;
}
}
}
}
}
console.log(count)
if((previousBoard[i][j].status === true) && (count < 2 || count > 3)) {
//console.log("false", i, j, count)
board[i][j].status = false;
} else if((count === 3) && (previousBoard[i][j].status === false)) { //if dead but 3 alive neighbours.
//alive
console.log("true", i, j, count)
board[i][j].status = true;
} else if((previousBoard[i][j].status === true) && (count === 2 || count === 3)) {
console.log("true", i, j, count)
board[i][j].status = true;
}
}
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
//console.log(board[i])
newMatrix[i] = board[i].slice()
}
//console.log(newMatrix)
return newMatrix
}
function draw() {
background(220);
//draw rectangles.
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
if (board[i][j].status === true) {
fill('black');
} else {
fill('white');
}
rect(board[i][j].position[0], board[i][j].position[1], size, size);
}
}
//slice copies previous array into a new reference, previousBoard and board are
//now independent.
let previousBoard = copyMatrix();
//updateCell(11, 9, previousBoard) //uncommenting this line creates weird results...
//console.log(previousBoard)
//update all cells based on previousBoard. (we'll modify board based on previous)
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
updateCell(i, j, previousBoard);
}
}
}
<html>
<head>
<Title>Gordon's Game of Life</Title>
<script src = "p5.js"></script>
<script src = "sketch.js"></script>
</head>
<body>
<h1>Gordon's Game of Life</h1>
</body>
</html>
问题是copyMatrix
需要深拷贝。
将复制矩阵更改为:
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
newMatrix[i] = [];
for (let j=0;j< n;j++){
newMatrix[i][j] = new Cell(board[i][j].status, board[i][j].position);
}
}
return newMatrix
}
这是您的代码,其中包含 copyMatrix
更改和添加的滑翔机以帮助演示行为。
//size of cell in pixels.
const size = 20;
const canvasWidth = 400;
const canvasHeight = 400;
const n = canvasWidth / size;
const m = canvasHeight / size;
class Cell {
constructor(status, position) {
this.status = status;
this.position = position;
}
}
let board = [];
function setup() {
//initialise matrix.
for(let i=0; i < m; i++) {
board[i] = new Array(n);
}
//fill matrix of cells.
for(let i=0; i < m; i++ ) {
for(let j=0; j < n; j++ ) {
board[i][j] = new Cell(false, [i*size, j*size]);
}
}
board[floor(m /2)][floor(n / 2)].status = true;
board[floor(m /2)+1][floor(n / 2)].status = true;
board[floor(m /2)+2][floor(n / 2)].status = true;
// glider
board[0][0].status = true;
board[0][2].status = true;
board[1][1].status = true;
board[1][2].status = true;
board[2][1].status = true;
createCanvas(canvasWidth, canvasHeight);
frameRate( 1 )
}
function updateCell(i, j, previousBoard) {
let count = 0;
//check neighbourgh cells.
for(let k=-1; k < 2; k++) {
for(let p=-1; p < 2; p++) {
if( !(i + k < 0 || j + p < 0 || j + p >= n || i + k >= m) ) {
if(k != 0 || p != 0) {
if(previousBoard[i+k][j+p].status === true) {
count++;
}
}
}
}
}
if((previousBoard[i][j].status) && (count < 2 || count > 3)) {
board[i][j].status = false;
} else if((count === 3) && (!previousBoard[i][j].status)) { //if dead but 3 alive neighbours.
board[i][j].status = true;
} else if((previousBoard[i][j].status) && (count === 2 || count === 3)) {
board[i][j].status = true;
}
}
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
newMatrix[i] = [];
for (let j=0;j< n;j++){
newMatrix[i][j] = new Cell(board[i][j].status, board[i][j].position);
}
}
return newMatrix
}
function draw() {
background(220);
//draw rectangles.
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
if (board[i][j].status === true) {
fill('black');
} else {
fill('white');
}
rect(board[i][j].position[0], board[i][j].position[1], size, size);
}
}
let previousBoard = copyMatrix();
//updateCell(11, 9, previousBoard) //uncommenting this line creates weird results...
//console.log(previousBoard)
//update all cells based on previousBoard. (we'll modify board based on previous)
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
updateCell(i, j, previousBoard);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>
使用滑翔机,此设置演示了所有三种可能的模式。我们从一个移动的 spaceship
滑翔机和一个 oscillator
开始,当滑翔机进入振荡器时,我们进行了几次迭代,打破了这两种模式,然后它稳定为 2x2 still life
。
我在 p5.js 中实现了 Conway 的人生游戏。 我有一个函数可以计算给定细胞的存活邻居的数量,从我所做的测试看来它工作得很好。话虽如此,我在模拟时遇到了意想不到的行为:它没有遵循正确的模式。我从 3 个对齐的正方形(一个信号灯)开始,它应该旋转,但由于某种原因它们都死了。
维基百科显示了信号灯的行为方式:https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Examples_of_patterns 您可以将下面的源代码复制粘贴到 https://editor.p5js.org/ 中并亲自查看。
我怀疑这个问题与电路板(单元矩阵)的复制有关,因为所有单元的更新必须同时进行,因此我们保留前一个电路板并更新当前电路板棋盘基于之前的棋盘。
来源:
//size of cell in pixels.
const size = 20;
const canvasWidth = 400;
const canvasHeight = 400;
const n = canvasWidth / size;
const m = canvasHeight / size;
class Cell {
constructor(status, position) {
this.status = status;
this.position = position;
}
}
let board = [];
function setup() {
//initialise matrix.
for(let i=0; i < m; i++) {
board[i] = new Array(n);
}
//fill matrix of cells.
for(let i=0; i < m; i++ ) {
for(let j=0; j < n; j++ ) {
board[i][j] = new Cell(false, [i*size, j*size]);
}
}
console.log("start", floor(m /2), floor(n / 2))
//[x, y] positions.
board[floor(m /2)][floor(n / 2)].status = true;
board[floor(m /2)+1][floor(n / 2)].status = true;
board[floor(m /2)+2][floor(n / 2)].status = true;
createCanvas(canvasWidth, canvasHeight);
frameRate( 1 )
}
//I'm 99.99% sure this function works, all the tests shows it does.
function updateCell(i, j, previousBoard) {
let count = 0;
//check neighbourgh cells.
for(let k=-1; k < 2; k++) {
for(let p=-1; p < 2; p++) {
if( !(i + k < 0 || j + p < 0 || j + p >= n || i + k >= m) ) {
if(k != 0 || p != 0) {
if(previousBoard[i+k][j+p].status === true) {
count++;
}
}
}
}
}
console.log(count)
if((previousBoard[i][j].status === true) && (count < 2 || count > 3)) {
//console.log("false", i, j, count)
board[i][j].status = false;
} else if((count === 3) && (previousBoard[i][j].status === false)) { //if dead but 3 alive neighbours.
//alive
console.log("true", i, j, count)
board[i][j].status = true;
} else if((previousBoard[i][j].status === true) && (count === 2 || count === 3)) {
console.log("true", i, j, count)
board[i][j].status = true;
}
}
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
//console.log(board[i])
newMatrix[i] = board[i].slice()
}
//console.log(newMatrix)
return newMatrix
}
function draw() {
background(220);
//draw rectangles.
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
if (board[i][j].status === true) {
fill('black');
} else {
fill('white');
}
rect(board[i][j].position[0], board[i][j].position[1], size, size);
}
}
//slice copies previous array into a new reference, previousBoard and board are
//now independent.
let previousBoard = copyMatrix();
//updateCell(11, 9, previousBoard) //uncommenting this line creates weird results...
//console.log(previousBoard)
//update all cells based on previousBoard. (we'll modify board based on previous)
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
updateCell(i, j, previousBoard);
}
}
}
<html>
<head>
<Title>Gordon's Game of Life</Title>
<script src = "p5.js"></script>
<script src = "sketch.js"></script>
</head>
<body>
<h1>Gordon's Game of Life</h1>
</body>
</html>
问题是copyMatrix
需要深拷贝。
将复制矩阵更改为:
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
newMatrix[i] = [];
for (let j=0;j< n;j++){
newMatrix[i][j] = new Cell(board[i][j].status, board[i][j].position);
}
}
return newMatrix
}
这是您的代码,其中包含 copyMatrix
更改和添加的滑翔机以帮助演示行为。
//size of cell in pixels.
const size = 20;
const canvasWidth = 400;
const canvasHeight = 400;
const n = canvasWidth / size;
const m = canvasHeight / size;
class Cell {
constructor(status, position) {
this.status = status;
this.position = position;
}
}
let board = [];
function setup() {
//initialise matrix.
for(let i=0; i < m; i++) {
board[i] = new Array(n);
}
//fill matrix of cells.
for(let i=0; i < m; i++ ) {
for(let j=0; j < n; j++ ) {
board[i][j] = new Cell(false, [i*size, j*size]);
}
}
board[floor(m /2)][floor(n / 2)].status = true;
board[floor(m /2)+1][floor(n / 2)].status = true;
board[floor(m /2)+2][floor(n / 2)].status = true;
// glider
board[0][0].status = true;
board[0][2].status = true;
board[1][1].status = true;
board[1][2].status = true;
board[2][1].status = true;
createCanvas(canvasWidth, canvasHeight);
frameRate( 1 )
}
function updateCell(i, j, previousBoard) {
let count = 0;
//check neighbourgh cells.
for(let k=-1; k < 2; k++) {
for(let p=-1; p < 2; p++) {
if( !(i + k < 0 || j + p < 0 || j + p >= n || i + k >= m) ) {
if(k != 0 || p != 0) {
if(previousBoard[i+k][j+p].status === true) {
count++;
}
}
}
}
}
if((previousBoard[i][j].status) && (count < 2 || count > 3)) {
board[i][j].status = false;
} else if((count === 3) && (!previousBoard[i][j].status)) { //if dead but 3 alive neighbours.
board[i][j].status = true;
} else if((previousBoard[i][j].status) && (count === 2 || count === 3)) {
board[i][j].status = true;
}
}
function copyMatrix() {
let newMatrix = []
for(let i=0; i < m; i++) {
newMatrix[i] = [];
for (let j=0;j< n;j++){
newMatrix[i][j] = new Cell(board[i][j].status, board[i][j].position);
}
}
return newMatrix
}
function draw() {
background(220);
//draw rectangles.
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
if (board[i][j].status === true) {
fill('black');
} else {
fill('white');
}
rect(board[i][j].position[0], board[i][j].position[1], size, size);
}
}
let previousBoard = copyMatrix();
//updateCell(11, 9, previousBoard) //uncommenting this line creates weird results...
//console.log(previousBoard)
//update all cells based on previousBoard. (we'll modify board based on previous)
for(let i=0; i < m; i++) {
for(let j=0; j < n; j++) {
updateCell(i, j, previousBoard);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>
使用滑翔机,此设置演示了所有三种可能的模式。我们从一个移动的 spaceship
滑翔机和一个 oscillator
开始,当滑翔机进入振荡器时,我们进行了几次迭代,打破了这两种模式,然后它稳定为 2x2 still life
。