#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)