使用 java 计算康威生命游戏中的下一帧

Calculating next frame in conways game of life using java

试图创造一个 Conways 生活游戏,但显然形状并不像它们必须的那样。也许有人可以帮我找到问题。

例如滑翔机:

- X - - - - 
- - X X - - 
- X X - - -
- - - - - - 

变成这个

- - X X - - 
- X - - - - 
X X X - - - 
- X X X - - 

但应该是这样的:

- - X - - -
- - - X - -
- X X X - -
- - - - - -

我的代码是这样的

public Frame(int x, int y) {

     setWidth(x);
     setHeight(y);

     if (x<1)
         frame = null;
      else if (y<1)
         frame = null;
      else {

       frame = new String [x][y];

         for (int i=0; i<frame.length; i++) {
            for (int j=0; j<frame[i].length; j++) {

               frame [i][j] = DEAD;
            }
         }
      } // else
   } // construktor


 public Integer getNeighbourCount(int x, int y) {

    Frame cell = new Frame(getHeight(), getWidth());
    int counter = 0;

    if(frame[x][y].equals(ALIVE))
    {
        counter = counter - 1;
    }
    for(int i=x-1; i<=x+1;i++){

        if(i<frame.length && i>0){

            for(int j=y-1; j<=y+1;j++){

                if(j<frame[i].length && j>0){

                    if (frame[i][j]==ALIVE) {
                        counter++;

                    }

                }
        }
        }
    }

    return counter;

}



public Frame nextFrame() {

    // Returns next frame


    Frame cell = new Frame(getWidth(), getHeight());
    //cell.frame = new String[getWidth()][getHeight()];

    for(int i = 0; i < frame.length; i++){
        for(int j =0; j <frame[i].length;j++){

            int n = getNeighbourCount(i,j);

                if(cell.frame[i][j]==null) {

                    cell.frame[i][j] = DEAD;
                }
               if (isAlive(i, j) && n < 2 || n > 3) {
                   cell.frame[i][j] = DEAD;
               }
               if (isAlive(i, j) && n == 3 || n == 2){
                   cell.frame[i][j] = ALIVE;
               }
               if(!isAlive(i, j) && n == 3) {          
                   cell.frame[i][j] = ALIVE;
               }
               if(isAlive(i, j) && n > 3){

                   cell.frame[i][j] = DEAD;
               }

               frame[i][j] = cell.frame[i][j];
        }

        }

    cell.toString();
    return cell;
}

    `

完整代码http://pastebin.com/LMwz724H

我认为问题在于您在遍历循环时正在复制新值。这意味着邻居正在使用下一个刻度而不是当前刻度的值。

您可以通过等到计算新框架中的所有新值来解决此问题:cell.frame,然后再次遍历框架并从 cell.frame 复制到 frame

另一种方法(在我看来更好)是在构建过程中避免克隆框架。然后,您可以更改 nextFrame 方法来创建 frame 的克隆,并使用该克隆在 frame.

中设置新值

您在遍历网格时正在更改 DEAD 和 ALIVE 帧。您需要存储应该死亡或复活的坐标,然后再执行。

将坐标存储在两个ArrayLists中(死的,活的)。第一和第二个位置是x和y轴,根据他们是否应该活着来改变那些坐标。

这是一个有效的解决方案 - 对每个单元格使用 enum 并使 i/j 和 x/y 正确(我认为)。它肯定会生成正确的第一次迭代:

static class GameOfLife {

    final int w;
    final int h;
    State[][] frame;

    enum State {

        Dead, Alive;
    }

    public GameOfLife(int w, int h) {
        this.w = w;
        this.h = h;
        frame = new State[h][w];
    }

    public void alive(int x, int y) {
        frame[y][x] = State.Alive;
    }

    public void tick() {
        frame = nextGeneration();
    }

    private int surroundingPopulation(int x, int y) {
        int pop = 0;
        for (int i = y - 1; i <= y + 1; i++) {
            for (int j = x - 1; j <= x + 1; j++) {
                // On frame - vertically.
                if ((i >= 0 && i < h)
                        // On frame horizontally.
                        && (j >= 0 && j < w)
                        // Alive
                        && (frame[i][j] == State.Alive)
                        // Not the center.
                        && (i != y || j != x)) {
                    pop += 1;
                }
            }

        }
        return pop;
    }

    private State[][] nextGeneration() {
        State[][] next = new State[h][w];
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                int pop = surroundingPopulation(x, y);
                // Any live cell
                if (frame[y][x] == State.Alive) {
                    if (pop < 2) {
                        // ... with fewer than two live neighbours dies, as if caused by under-population.
                        next[y][x] = State.Dead;
                    } else if (pop > 3) {
                        // ... with more than three live neighbours dies, as if by overcrowding.
                        next[y][x] = State.Dead;
                    } else {
                        // ... with two or three live neighbours lives on to the next generation.
                        next[y][x] = State.Alive;
                    }
                } else {
                    // Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
                    if (pop == 3) {
                        next[y][x] = State.Alive;
                    }
                }
            }
        }
        return next;
    }

    @Override
    public String toString() {
        StringBuilder s = new StringBuilder();
        for (State[] row : frame) {
            for (State c : row) {
                s.append(c == State.Alive ? "X" : " ");
            }
            s.append("\r\n");
        }
        return s.toString();
    }
}

public void test() {
    GameOfLife g = new GameOfLife(6, 6);
    g.alive(1, 0);
    g.alive(2, 1);
    g.alive(3, 1);
    g.alive(1, 2);
    g.alive(2, 2);
    System.out.println("Before:\r\n" + g);
    g.tick();
    System.out.println("After:\r\n" + g);
}

这是我不久前写的一个简单测试的片段。正如其他人所提到的,不要在仍在阅读的同时更改活动板上的值。相反,克隆电路板并在读取当前电路板的同时对副本进行更改。

我遇到过几次的另一个问题是遍历 y,然后为每个 y 遍历 x,但在访问一个点时引用 x,y。感觉回到了前面:)

// Rules:
// 1) Any live cell with fewer than two live neighbours dies, as if caused by under-population.
// 2) Any live cell with two or three live neighbours lives on to the next generation.
// 3) Any live cell with more than three live neighbours dies, as if by overcrowding.
// 4) Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
void mutateGrid() {

    // Copy existing grid into the next generation's grid
    boolean[][] mutatedGrid = new boolean[gridXWidth][gridYHeight];
    for (int i = 0; i < gridXWidth; i++) {
        System.arraycopy(grid[i], 0, mutatedGrid[i], 0, gridYHeight);
    }
    // Start mutation rules
    for (int y = 0; y < gridYHeight; y++) {
        for (int x = 0; x < gridXWidth; x++) {
            int liveNeighbours = countLiveNeighbours(x,y);
            if (liveNeighbours < 2 || liveNeighbours > 3) {
                mutatedGrid[x][y] = false;
            }
            else if (liveNeighbours == 3) {
                mutatedGrid[x][y] = true;
            }
        }
    }
    grid = mutatedGrid;
}

int countLiveNeighbours(int x, int y) {
    int count = 0;
    for (int j = y-1; j <= y+1; j++) {
        for (int i = x-1; i <= x+1; i++) {
            if (i < 0 || j < 0 || i >= gridXWidth || j >= gridYHeight){
                continue;
            }
            if (grid[i][j]) {
                count++; 
            }
        }
    }
    count -= grid[x][y]?1:0; // remove self from count
    return count;
}