Игра жизни — грани не меняются

#java #arrays #conways-game-of-life

#java #массивы #конвейс-игра жизни

Вопрос:

Я увидел «Игру жизни» Конвея и захотел создать свою собственную. Однако ячейки по краям каким-то образом не следуют правилам и просто остаются живыми (или мертвыми) все время. Кто-нибудь знает, где я допустил ошибку? Это мой код: (Я загрузил только класс, в котором применил правила. Если для решения проблемы нужны другие классы, я могу загрузить и их)

 import java.util.Arrays;
/**
 * The class Grid does the initialization of the game of life and the application of the rules. 
 * It is a 2D array of the type Cell. It saves the cells and uses a copy of it to apply the rules.
 *
 */
public class Grid
{
    public int col;
    public int row;
    public int x,y;
    public Cell [][] array; //2D array of the type Cell
    public Cell [][] arraycopy; 

    /**
     * This constructor is to create the initial generation of cells and set the state of random 20% to true (=alive).
     * @param col   the number of columns 
     * @param row   the number of rows
     *
     */
    public Grid(int col, int row)
    {
        this.col = col;
        this.row = row;
        this.array =  new Cell [col][row];
        //Loops through every spot in the 2D array 
        for (int x = 0; x < col; x  )
        {
            for (int y=0; y < row; y  )
            {
                //set randomly 20% of the cells' state to "true"
                if (Math.random() <= 0.2) 
                {
                    Cell cell = new Cell (true);
                    this.array[x][y]= cell;
                }
                else 
                {
                    Cell cell = new Cell (false);
                    this.array[x][y]= cell;
                }
            }
        }
    }

    /**
     * This method will count the alive cells in a 3*3 neighboorhood and apply the rules of life. 
     * This method uses arraycopy.
     *
     */
    public void lifeSteps()
    {        
        //Works with a copy of the array and the cells
        this.arraycopy =  new Cell [col][row];
        for (int x = 0; x < col; x  )
        {
            for (int y=0; y < row; y  ) 
            {
                this.arraycopy [x][y] = new Cell(this.array[x][y].getState());
            }
        }
        //Looping through the cells, but the cells at the edge are skipped
        for (int x = 1; x < col-1; x  ) 
        {
            for (int y= 1; y < row-1; y  )
            {
                //Looping through all the neighbors
                int numNeighborsAlive = 0;
                for (int i = x-1; i <= x 1; i  ) 
                {
                    for (int j = y-1; j <= y 1; j  ) 
                    {
                        //In a 3x3 neighborhood the middle cell needs to be skipped
                        if ((x != i) amp;amp; (y != j))
                        {
                            //Only the cells that are alive (true) are added
                            if (arraycopy [i][j].getState() == true) 
                            {
                                numNeighborsAlive  = 1;
                            }
                        }
                    }
                }
                //Apply the rules of life
                if ((array [x][y].getState()) amp;amp; (numNeighborsAlive < 2 || numNeighborsAlive >3))  //Loneliness and Overpopulation
                {
                    array[x][y].setState(false); 
                } 
                else if ((array [x][y].getState() == false) amp;amp; (numNeighborsAlive ==3)) //Birth
                {
                    array[x][y].setState(true); 
                } 
                else 
                { //stasis
                }
            }
        }
    }

    /**
     * This method will return the statement for the array.
     * @return  the 2D array of the type Cell
     */
    public Cell[][] returnGrid ()
    {
        return this.array;
    }

    /**
     * This method will test if everything is working well by printing zeros and ones. 
     *
     */
    public void printTest()
    { 
        System.out.println("t"); // a new line
        for (int x = 0; x < col; x  )
        {
            for (int y=0; y < row; y  )
            {
                // assigns 1 if the cell is alive and 0 if it is dead
                if (array[x][y].getState() == true)
                {
                    System.out.print("1");
                }
                else 
                {
                    System.out.print("0");
                }
            }
            System.out.println(""); // will be displayed as colums and rows
        }
        System.out.println("t"); // a new line
    }
}
  

Комментарии:

1. Я думаю, что ваш комментарий //Looping through the cells, but the cells at the edge are skipped может быть связан 😉

Ответ №1:

Границы не обрабатываются, поэтому всегда есть 8 соседей.

Можно обрабатывать все позиции массива и определять соседей. Они могут быть запрограммированы в ячейке при инициализации или — как показано ниже — определены динамически.

Для этого я использую вектор смещения с дельтой x и дельтой y в -1, 0, 1.

 private static final int[][] MIDDLE_NEIGHBORS = 
{
    {-1, -1}, {-1, 0}, {-1, 1},
    {0, -1}, {0, 1},
    {1, -1}, {1, 0}, {1, 1}
};
private static final int[][] LEFT_TOP_NEIGHBORS = 
{
    {0, 1},
    {1, 0}, {1, 1}
};
...
int[][] neighborDeltaXY(int x, int y, int col, int row) {
    if (1 < x amp;amp; x < col-1 amp;amp; 1 < y amp;amp; y < row-1) {
        return MIDDLE_NEIGHBORS;
    }
    if (x == 0 amp;amp; row == 0) {
        return LEFT_TOP_NEIGHBORS;
    }
    ...
}
  

Или вам может быть удобнее с вложенными условиями:

 int[][] neighborDeltaXY(int x, int y, int col, int row) {
    if (x == 0) {
        if (y == 0) {
            return LEFT_TOP_NEIGHBORS;
        } else if (y == row - 1) {
            return ...;
        } else {
            return ...;
        }
    } else if (x == col - 1) {
        if (y == 0) {
            return ...;
        } else if (y == row - 1) {
            return ...;
        } else {
            return ...;
        }
    } else {
        if (y == 0) {
            return ...;
        } else if (y == row - 1) {
            return ...;
        } else {
            return MIDDLE_NEIGHBORS;
        }
    }
}
  

Полезно иметь метод, выполняющий фактический подсчет на доске:

 int countNeighborsAlive(Cell[][] old, int x, int y, int col, int row) {
    int numNeighborsAlive = 0;
    int[][] neighbors = neighborDeltaXY(x, y, col, row);
    for (int[] neighbor : neighbors) {
        int xn = x   neighbor[0];
        int yn = y   neighbor[1];
        if (old[xn][yn].getState()) {
              numNeighborsAlive;
        }
    }
    return numNeighborsAlive;
}
  

Таким образом, временной цикл становится проще:

     for (int x = 0; x < col; x  ) {
        for (int y= 0; y < row; y  ) {
            int numNeighborsAlive = countNeighborAlive(arraycopy, x, y, col, row);
            ... numNeighborsAlive
  

В game of life можно было бы создавать разные геометрии, имея над y == 0 строку y == — 1, так что мир обтекает границы.

Также возможно взять исходную доску и поместить ее в сетку большего размера, чтобы видимые ячейки находились в [1, col — 1), [1, row — 1). Тогда невидимые границы всегда остаются равными 0. Не очень хорошо для game of life, но другие игры, основанные на сетке, настолько замкнуты.

 Instead            Do             Or
x  = 1;              x;           x  
if (c == true)     if (c)
if (d == false)    if (!d)