生命游戏 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