Search code examples
javaarraysmultidimensional-arrayconways-game-of-life

I'm recreating John Conway's Game Of Life. However, my checks for the rules of the game aren't correctly detecting surrounding lives. Why is this?


I've experimented for a long time, but I just can't see where my error is in the check for surrounding lives! Can you guys please take a look at my code and see where my error is? Everything in the code is fully functional outside of the check for surrounding lives.

P.S. Sorry for the messed up formatting. I'm using Sublime, and this is the best the auto-formatting will do.

For those of you who are unfamiliar, here are the rules for the Game Of Life: https://bitstorm.org/gameoflife/

import java.util.*;
import java.lang.*;
import java.io.*;

class Life
{
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);
        String userin = in.nextLine();
        int x = 8;
        int y = 8;      
        int [][] visualize;
        if(userin.equals("glider"))
        {
            visualize = new int [][]{
                //0 is used as a boundary, 1 represents a dead cell, 2 represents an alive cell
                {0, 0, 0, 0, 0, 0, 0, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 1, 1, 1, 2, 1, 1, 0},
                {0, 1, 1, 1, 1, 2, 1, 0},
                {0, 1, 1, 2, 2, 2, 1, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 0, 0, 0, 0, 0, 0, 0}
            };
            for (int i = 0; i <= 7; i++)
            {
                for (int j = 0; j <= 7; j++)
                {
                    if (visualize[i][j] == 2) {
                        System.out.print("*");
                    } else if (visualize[i][j] == 1)
                    {
                        System.out.print(".");
                    } else if (visualize[i][j] == 0)
                    {
                        System.out.print("_");
                    }
                }
                System.out.println();
            }
            nextGen(visualize, x, y);
        } else if(userin.equals("own"))
        {
            visualize = new int [8][8];
            for(int o = 1; o <= 6; o++) //Starting it a pos 1 means pos 0 is automaically filled with a "0", which is used as the boundary
            {
                visualize[o] = new int[x];
                for(int p = 1; p <= 6; p++) //Starting it a pos 1 means pos 0 is automaically filled with a "0", which is used as the boundary
                {
                    visualize[o][p] = in.nextInt();
                }
                System.out.println(0);
                System.out.println();
            }
            for (int i = 0; i <= 7; i++)
            {
                for (int j = 0; j <= 7; j++)
                {
                    if(visualize[i][j] == 2) {
                        System.out.print("*");
                    } else if (visualize[i][j] == 1)
                    {
                        System.out.print(".");
                    } else if (visualize[i][j] == 0)
                    {
                        System.out.print("_");
                    }
                }
                System.out.println();
            }
            nextGen(visualize, x, y);
        }
        else if(userin.equals("test"))
        {
            visualize = new int [][]{
                {0, 0, 0, 0, 0, 0, 0, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 1, 2, 2, 1, 1, 1, 0},
                {0, 1, 2, 1, 1, 1, 1, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 1, 1, 1, 1, 1, 1, 0},
                {0, 0, 0, 0, 0, 0, 0, 0}
            };
            for (int i = 0; i <= 7; i++)
            {
                for (int j = 0; j <= 7; j++)
                {
                    if (visualize[i][j] == 2) {
                        System.out.print("*");
                    } else if (visualize[i][j] == 1)
                    {
                        System.out.print(".");
                    } else if (visualize[i][j] == 0)
                    {
                        System.out.print("_");
                    }
                }
                System.out.println("_");
            }
            nextGen(visualize, x, y);
        }
    }
    static void nextGen(int visualize[][], int x, int y)
    {
        int[][] life = new int[x][y];
        int alive = 0;
        //Starts at array-point 1,1
        int startX = 1;
        int startY = 1;
        System.out.println();
        System.out.println();
        System.out.println("________");
        System.out.print("_");
        for(int repeat = startX; repeat <= 6; repeat++)
        {

            while(startY <= 6)
            {
                for(int k = startX; k <=6; k++)
                {
                    for(int l = startY; l <=6; l++)
                    {
                        //BEGIN CHECK FOR SURROUNDING LIVES
                        if(!(visualize[startX + 1][startY] == 0))
                        {
        if(visualize[startX + 1][startY] == 2) //Right 1
        {
            alive++;
        }
    }
    if(!(visualize[startX][startY + 1] == 0))
    {
        if(visualize[startX][startY + 1] == 2) //Up 1
        {
            alive++;
        }
    }
    if(!(visualize[startX + 1][startY + 1] == 0))
    {
        if(visualize[startX + 1][startY + 1] == 2) // Right 1, Up 1
        {
            alive++;
        }
    }
    if(!(visualize[startX - 1][startY] == 0))
    {
            if(visualize[startX - 1][startY] == 2) //Left 1
            {
                alive++;
            }       
        }
        if(!(visualize[startX][startY - 1] == 0))
        {
            if(visualize[startX][startY - 1] == 2) // Down 1
            {
                alive++;
            }
        }
        if(!(visualize[startX - 1][startY - 1] == 0))
        {
            if(visualize[startX - 1][startY - 1] == 2) //Left 1, Down 1
            {
                alive++;
            }
        }
        if(!(visualize[startX + 1][startY - 1] == 0))
        {
        if(visualize[startX + 1][startY - 1] == 2) //Right 1, Down 1
        {
            alive++;
        }
    }
    if(!(visualize[startX - 1][startY - 1] == 0))
    {
            if(visualize[startX - 1][startY - 1] == 2) //Left 1, Down 1
            {
                alive++;
            }
        }
        //CHECKS IF THERE ARE EXACTLY 3 LIVES AROUND AN AREA. IF NOT, IT KILLS THAT AREA
        if(alive == 3)
        {
            visualize[k][l] = 2;

        } else {
            visualize[k][l] = 1;
        }
    }
}

if (visualize[startX][startY] == 2) {
    System.out.print("*");
} else if (visualize[startX][startY] == 1)
{
    System.out.print(".");
}   
//Performs the check going down the Y-axis
startY++;
}
System.out.println("_");
System.out.print("_");
//After
startX++;
startY = 1;

}
System.out.println("_______");
}
}

Solution

  • Well, let's be honest, there are several things wrong here:

    You have unnecessary and confusing loops

    You have four nested loops in your nextGen method. You only need two, to loop through each cell in the grid. You don't need the outermost two, only the inner two, so get rid of the outer two. Also get rid of the variables repeat, startX and startY, as you don't need them.

    You will also need to go through all expressions such as visualize[startX + 1][startY] and replace startX with k and startY with l.

    Your square check logic is wrong

    After replacing startX and startY with k and l, the following lines appear twice in your code:

                        if (!(visualize[k - 1][l - 1] == 0)) {
                            if (visualize[k - 1][l - 1] == 2) //Left 1, Down 1
                            {
                                alive++;
                            }
                        }
    

    You are therefore counting one of the neighbouring eight cells twice, and one of them not at all.

    To fix this, replace one of the occurrences of this code with the following:

                        if (!(visualize[k - 1][l + 1] == 0)) {
                            if (visualize[k - 1][l + 1] == 2) //Left 1, Up 1
                            {
                                alive++;
                            }
                        }
    

    You're modifying the current generation while computing the next generation

    With Life you can't modify the current generation of the world while attempting to work out what the next generation is. Otherwise you could mark as dead a cell that would be necessary to keep another cell alive. Instead, you have to create a second array and record the state of the next generation in that.

    You've created a second array, life, but you don't seem to be using it at all. So, let's use that. Once you've figured out whether a cell should be alive or dead, assign that value to life[k][l] instead of visualize[k][l].

    You also print out a little grid of * and . characters representing the state of the next generation. This will have to use life rather than visualize.

    You have got the rules of Life wrong

    Your code currently aims to mark any square as alive if it has exactly three neighbours, and dead otherwise. This is not how Life works.

    A live cell stays alive if it has two or three neighbours, otherwise it dies. An empty cell with exactly three neighbours becomes alive.

    The following code implements this logic:

                if (visualize[k][l] == 2 && (alive == 2 || alive == 3)) {
                    // Live cell stays alive if 2 or 3 neighbours
                    life[k][l] = 2;
                }
                else if (visualize[k][l] == 1 && alive == 3) {
                    // Dead cell becomes live if 3 neighbours
                    life[k][l] = 2;
                }
                else {
                    // Anything else: cell either dies or stays dead.
                    life[k][l] = 1;
                }
    

    You are forgetting to reset the alive counter

    You set the variable alive to 0 at the start of nextGen, but when counting the number of alive neighbours for each cell you don't reset it to zero. This means alive is therefore counting the number of alive neighbours of every cell encountered so far. It won't be long before this gets beyond 3 and everything ends up dead.

    You need to reset alive back to zero at the start of the l loop.


    I made all of these changes to your nextGen method and it appeared to work, in that it displayed what I would expect to see one generation on from a glider. Here's what I ended up with (IntelliJ has formatted it a bit so it isn't quite formatted the same way as your code was):

        static void nextGen(int visualize[][], int x, int y) {
            int[][] life = new int[x][y];
            int alive = 0;
    
            System.out.println();
            System.out.println();
            System.out.println("________");
            System.out.print("_");
    
            for (int k = 1; k <= 6; k++) {
                for (int l = 1; l <= 6; l++) {
                    alive = 0;
                    //BEGIN CHECK FOR SURROUNDING LIVES
                    if (!(visualize[k + 1][l] == 0)) {
                        if (visualize[k + 1][l] == 2) //Right 1
                        {
                            alive++;
                        }
                    }
                    if (!(visualize[k][l + 1] == 0)) {
                        if (visualize[k][l + 1] == 2) //Up 1
                        {
                            alive++;
                        }
                    }
                    if (!(visualize[k + 1][l + 1] == 0)) {
                        if (visualize[k + 1][l + 1] == 2) // Right 1, Up 1
                        {
                            alive++;
                        }
                    }
                    if (!(visualize[k - 1][l] == 0)) {
                        if (visualize[k - 1][l] == 2) //Left 1
                        {
                            alive++;
                        }
                    }
                    if (!(visualize[k][l - 1] == 0)) {
                        if (visualize[k][l - 1] == 2) // Down 1
                        {
                            alive++;
                        }
                    }
                    if (!(visualize[k - 1][l - 1] == 0)) {
                        if (visualize[k - 1][l - 1] == 2) //Left 1, Down 1
                        {
                            alive++;
                        }
                    }
                    if (!(visualize[k + 1][l - 1] == 0)) {
                        if (visualize[k + 1][l - 1] == 2) //Right 1, Down 1
                        {
                            alive++;
                        }
                    }
                    if (!(visualize[k - 1][l + 1] == 0)) {
                        if (visualize[k - 1][l + 1] == 2) //Left 1, Up 1
                        {
                            alive++;
                        }
                    }
    
                    if (visualize[k][l] == 2 && (alive == 2 || alive == 3)) {
                        life[k][l] = 2;
                    }
                    else if (visualize[k][l] == 1 && alive == 3) {
                        life[k][l] = 2;
                    }
                    else {
                        life[k][l] = 1;
                    }
    
                    if (life[k][l] == 2) {
                        System.out.print("*");
                    }
                    else if (life[k][l] == 1) {
                        System.out.print(".");
                    }
                }
    
                System.out.println("_");
                System.out.print("_");
            }
            System.out.println("_______");
    
            // Copy the 'life' array back to 'visualize', so that the
            // next generation could be calculated from it.
            for (int i = 1; i < 6; ++i)
            {
                visualize[i] = life[i];
            }
        }
    

    Please take the time to understand the changes, and why this code works.

    Finally, you write code like this eight times:

                    if (!(visualize[k + 1][l] == 0)) {
                        if (visualize[k + 1][l] == 2) //Right 1
                        {
                            alive++;
                        }
                    }
    

    Instead of writing if (!(something == 0)) ... you can write if (something != 0). However, you can simplify this code even further: if a cell is equal to 2, it is automatically not equal to 0 as well, so you can just write the following instead:

                    if (visualize[k + 1][l] == 2) //Right 1
                    {
                        alive++;
                    }
    

    Also, the Left, Right Up and Down comments are wrong. The k loop runs over the rows (i.e. it is the y-coordinate) and the l loop runs over the cells in each row (the x-coordinate). Also, k + 1 is down one cell, not up one cell.