Search code examples
javaconways-game-of-life

Trying to implement Conway's Game of Life in Java but having trouble with counting the "living" cells around a cell


I'm a beginner in coding. I have this problem that I know already, but I can't seem to find the solution

Given a preset 5x5 2D array representing the board (. means dead cell and 0 means living cell. )

. . . . .
. . . . .
. . 0 . .
. . . . .
. . . . .

I have a method countLivingNeighbors that counts the "living" cells around a cell. When I visualize the number of living neighbors of each cell in a the same grid pattern, the expected output and the actual output are:

EXPECTED:           ACTUAL:
0 0 0 0 0           0 0 0 0 0
0 1 1 1 0           0 1 1 1 0
0 1 0 1 0           0 1 0 0 0
0 1 1 1 0           0 0 0 0 0
0 0 0 0 0           0 0 0 0 0

I know the problem is that the method countLivingNeighbors doesn't count a "living" cell if its position is on the left, above-right, above, or above-left of the current cell. But I'm pretty sure the logic of my code is alright, I think.

For reference, here's the code for the method:

// 'true' means living, 'false' means dead
static int countLivingNeighbors(boolean[][] board, int posX, int posY) {
       int counter = 0;

       // Iterate around the cell
       for (int j = -1; j <= 1; j++) {
           if (posY + j < 0 || posY + j >= board.length) continue;
           for (int i = -1; i <= 1; i++) {
               if (posX + i < 0 || posX + i >= board[0].length) continue;
               if (board[posY + j][posX + i]) ++counter;
           }
       }
       
       // It counts the current cell as well, so let's remove that.
       if (board[posY][posX]) --counter;
       return counter;

I don't think I have problems with the code except for this which stomps me for quite a while now. Any help is much appreciated.

Here's the current version of the entire code.

package GameOfLife;

import java.util.Random;

public class Main {

   public static void main(String[] args){
       int boardWidth = 5, boardHeight = 5;
       boolean[][] board = {{false, false, false, false, false},
                            {false, false, false, false, false},
                            {false, false, true , false, false},
                            {false, false, false, false, false},
                            {false, false, false, false, false}};

       renderBoard(board);

       System.out.println();

       board = getNextBoardState(board);
       renderBoard(board);
   }


   static boolean[][] createRandomBoard(int width, int height) {
       boolean[][] board = new boolean[height][width];

       for (int i = 0; i < height; i++) {
           for (int j = 0; j < width; j++) {
               switch (new Random().nextInt(2)) {
                   case 0 -> board[i][j] = false;
                   case 1 -> board[i][j] = true;
               }
           }
       }

       return board;
   }

   static void renderBoard(boolean[][] board) {
       for (boolean[] cellRows : board) {
           for (boolean cells : cellRows) {
               if (cells) System.out.print(" 0 ");
               else System.out.print(" . ");
           } System.out.println();
       }
   }

   static boolean[][] getNextBoardState(boolean[][] board) {
       boolean[][] nextBoard;
       nextBoard = board;

       for (int y = 0; y < board.length; y++) {
           for (int x = 0; x < board[0].length; x++) {
                int livingNeighbors = countLivingNeighbors(board, x, y);
                System.out.print(" " + livingNeighbors + " ");
                nextBoard[y][x] = setNextCellState(livingNeighbors, board[y][x]);
           } System.out.println();
       } System.out.println();

       return nextBoard;
   }

   static int countLivingNeighbors(boolean[][] board, int posX, int posY) {
       int counter = 0;

       for (int j = -1; j < 2; j++) {
           if (posY + j < 0 || posY + j >= board.length) continue;
           for (int i = -1; i < 2; i++) {
               if (posX + i < 0 || posX + i >= board[0].length) continue;
               if (board[posY + j][posX + i]) ++counter;
           }
       }

       if (board[posY][posX]) --counter;
       return counter;
   }

   static boolean setNextCellState(int livingNeighbors, boolean cellState) {
       if (cellState) {
           if (livingNeighbors <= 1 || livingNeighbors > 3) return false;
       } else {
           if (livingNeighbors == 3) return true;
       }

       return cellState;
   }
}

Solution

  • In the getNextBoardState method you are assigning nextBoard = board.

    static boolean[][] getNextBoardState(boolean[][] board) {
        boolean[][] nextBoard;
        nextBoard = board;
    

    It's natural to think that this should copy all of the values in the board array into a new array called nextBoard. But in fact, it creates a new pointer called nextBoard, which points to the same array as board. This means that when you change a value in nextBoard, you are in fact changing the same value pointed to by board.

    To get around this, make a method that copies values from the board into a new board. Here's a very simple example (which has some pitfalls of its own):

    private static boolean[][] copyBoard(boolean[][] board) {
        boolean[][] newBoard = new boolean[board.length][board[0].length];
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                newBoard[i][j] = board[i][j];
            }
        }
        return newBoard;
    }
    

    Then in getNextBoardState, replace

    boolean[][] nextBoard;
    nextBoard = board;
    

    with

    boolean[][] nextBoard = copyBoard(board);
    

    You should get the result you expect:

    0  0  0  0  0 
    0  1  1  1  0 
    0  1  0  1  0 
    0  1  1  1  0 
    0  0  0  0  0 
    

    Note: the copyBoard method assumes that board always has at least one inner array (board[0]) and that all of board's inner arrays have the same length as the array at board[0].