Why are the generations in my Game of Life (using

2019-07-29 17:39发布

问题:

import processing.core.PApplet;
import static java.lang.System.out;

public class GoL2 extends PApplet {

int rectSideLength = 25; // rectSideLength = length of each side of the rectangles drawn that represent cells
int generation = 0;
int windowWidth = 1920;
int windowHeight = 950;

int[][] currentGeneration = new int[windowWidth][windowHeight]; // currentGeneration = 2D array to gold cell values of current generation
int[][] nextGeneration = new int[windowWidth][windowHeight]; // nextGeneration = 2D array to hold cell values of next generation

int sumOfNeighbors;
int temporarySumOfNeighbors;
int counter;

public static void main(String[] args) {
    PApplet.main("GoL2");

}

public void settings() {
    size(windowWidth, windowHeight);

}

int numRectWidth = width / rectSideLength; // numRectWidth = the number of rectangles wide that will fit in the x axis of window
int numRectHeight = height / rectSideLength; // numRectHeight = the number of rectangles that will fit in the y axis of window
                                                // The previous statements are here because they need the size of the frame to
                                                // be set in order to accurately set the variables, lest they end up equal to 100

/* public void setup() { 
 *  background(255); 
 *  frameRate(1); 
 *  for (int y = 0; y < windowHeight; y++) { // For each row, 
 *      for (int x = 0; x < windowWidth; x++) { // For each element in the current row, 
 *          currentGeneration[x][y] = (int) random(0, 2); // Set element (cell) equal to either 0 or 1 (on or off) 
 *      } 
 *  } 
 * } */

public void setup() {
    background(255);
    frameRate(1);
    for (int y = 0; y < windowHeight; y++) { // For each row,
        for (int x = 0; x < windowWidth; x++) { // For each element in the current row,
            currentGeneration[x][y] = 0; // Set element (cell) equal to either 0 or 1 (on or off)
        }
    }
    currentGeneration[25][25] = 1;
    currentGeneration[25][26] = 1;
    currentGeneration[25][27] = 1;
    currentGeneration[26][27] = 1;
    currentGeneration[27][26] = 1;

}

public void draw() {
    numRectWidth = width / rectSideLength;
    numRectHeight = height / rectSideLength;

    displayCurrentGeneration();
    try {
        Thread.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    fill(255, 20, 147);
    textSize(30);
    text(generation, 20, 30);

    textSize(10);
    text("25,25", 625, 645);
    text("24,27", 600, 695);
    text(generation, 580, 695);
    generation++;
    generateNextGeneration();

}

public void displayCurrentGeneration() {
    background(255);
    for (int y = 0; y < 950; y++) { // For each row,
        for (int x = 0; x < 1920; x++) { // For each element in the current row,
            if (currentGeneration[x][y] == 0) { // If element equals zero, make rectangle white
                fill(255);
                stroke(0);
            } else if (currentGeneration[x][y] == 1) { // If element equals one, make rectangle black
                fill(0);
                stroke(255);
            } else {
                out.println("Inappropriate value for currentGeneration[" + x + "][" + y + "]. Value: "
                        + currentGeneration[x][y] + ", generation: " + generation);
            }
            rect(x * rectSideLength, y * rectSideLength, rectSideLength, rectSideLength); // Display rectangle (cell)
        }
    }

    // out.println("Generation " + generation);

}

public void generateNextGeneration() {
    out.println("Generating gen " + generation);
    for (int y = 1; y < numRectHeight - 1; y++) { // For each row,
        for (int x = 1; x < numRectWidth - 1; x++) { // For each element in the current row,
            sumOfNeighbors = 0;
            sumOfNeighbors = getSumOfNeighbors(x, y);
            if (sumOfNeighbors != 2 && sumOfNeighbors != 3) { // Death
                nextGeneration[x][y] = 0;
            } else if (sumOfNeighbors == 3 && currentGeneration[x][y] == 0) { // Birth
                nextGeneration[x][y] = 1;
            } else if ((sumOfNeighbors == 2 || sumOfNeighbors == 3) && currentGeneration[x][y] == 1) { // Stasis
                nextGeneration[x][y] = 1;
            }
        }
    }
    currentGeneration = nextGeneration.clone();
}

public int getSumOfNeighbors(int xAxis, int yAxis) {
    temporarySumOfNeighbors = 0;

    for (int i = -1; i < 2; i++) {
        for (int j = -1; j < 2; j++) {
            if (xAxis == 24 && yAxis == 27 && j != 0 && i != 0) {
                out.println("X" + j + ", Y" + i + ":: " + currentGeneration[xAxis + j][yAxis + i]);
            } else if (xAxis == 24 && yAxis == 27 && j == 0 && i != 0) {
                out.println("X" + ", Y" + i + ":: " + currentGeneration[xAxis + j][yAxis + i]);
            } else if (xAxis == 24 && yAxis == 27 && j != 0 && i == 0) {
                out.println("X" + j + ", Y" + ":: " + currentGeneration[xAxis + j][yAxis + i]);
            } else if (xAxis == 24 && yAxis == 27 && j == 0 && i == 0) {
                out.println("X" + ", Y" + ":: " + currentGeneration[xAxis + j][yAxis + i]);
            }
            temporarySumOfNeighbors += currentGeneration[xAxis + j][yAxis + i];
        }
    }
    temporarySumOfNeighbors -= currentGeneration[xAxis][yAxis];
    if (temporarySumOfNeighbors > 8) {
        out.println("temporarySumOfNeighbors > 8: " + temporarySumOfNeighbors);
    }
    if (xAxis == 24 && yAxis == 27) {
        out.println("Generation: " + generation + "- " + xAxis + ", " + yAxis + ": " + temporarySumOfNeighbors);
    }

    return temporarySumOfNeighbors;

 }

}

http://pastebin.com/GH51hXzJ

I am a beginner attempting to code the Game of Life, and I am unsure how to find the source of my issues. I set the game to just start with a simple glider in setup, and believe I may have found the effects of the issue.

I put markers on the cells to help track them. If you watch cell (24,27) you will see at least an example of the issue. In the console, I print out the neighborhood of that cell throughout the run of the program. It appears to somehow detect the neighborhood that (24,27) will have in generation 2 in generation 1, and vice versa (assuming that the first generation is generation 0). I am unsure how to explain it, but if you examine the console output and look at the neighborhoods, you see that it detects generation 2's neighborhood in generation 1 and vice versa. That's why when (24,27) has 3 neighbors in generation 1, it only comes to life in generation 3 while in generation 2, it only has 2 neighbors.

Please let me know if you have any questions, I find it difficult to explain my problem.

The issue is explained more here: http://imgur.com/gallery/iRc07/new

Thank you

回答1:

This is the main source of your problem:

currentGeneration = nextGeneration.clone();

You might think that line will copy everything from nextGeneration into currentGeneration, and it does... but not in the way you're thinking it does.

The nextGeneration variable is a 2D array. In other words, it's an array of arrays. In other other words, the values contained by nextGeneration are arrays.

When you call the clone() function of an array, it copies the values of the old array into a new array. There's your problem: your values are arrays. So it's copying the arrays, not the values inside those second arrays.

Because of that, both nextGeneration and currentGeneration are pointing to the same arrays. So now when you calculate the next generation, you're changing the arrays of the current generation. This doesn't work, since the Game of Life calculation requires two separate arrays.

In other words, you're making a shallow copy of the arrays.

This might be easier to explain with a simpler program:

public class Test {
    public static void main(String... args){

        //create an array
        int[][] array = {{1, 2, 3}, {4, 5, 6}};

        //clone the array
        int[][] arrayTwo = array.clone();

        //change the original array
        array[0][0] = 99;

        //second array has also changed!
        System.out.println(arrayTwo[0][0]);
    }
}

Long story short: You should almost never use the clone() function.

You could fix your problem by making a deep copy of the array. There are libraries that handle this for you, or you could use serialization, or just write your own nested for loop.

But an even simpler (and I would argue more correct) solution would be: stop using class-level variables when you don't need them.

The clone() problem wouldn't be a problem, except you're using nextGeneration as a class-level variable. This means that it retains its value between calls to generateNextGeneration(). And since that value is pointing to the arrays inside currentGeneration, that's causing all of the problems.

You already handle this issue with your other variables: notice how you're resetting the sumOfNeighbors and temporarySumOfNeighbors before you use them. You could do the same thing with the nextGeneration variable.

But I would go a step further and get rid of all three of those class-level variables. Move their declarations to inside the functions that use them, that way you don't have to worry about them maintaining their values between function calls.

Two more notes while I'm at it:

You shouldn't really call Thread.sleep() from the draw() function (or any event function). Just set the frame rate and let Processing handle the timing for you.

You're using a ton of cells that you aren't drawing. Your array is 1920x950, but you're only drawing a small percentage of those cells. That's wasting a ton of CPU time on cells you never show. Like I said in your other question, you need to be more careful about distinguishing between pixel coordinates and array coordinates.

Anyway, this was a good question. I think you're getting closer. You just need to get rid of those extra cells and you'll be in pretty good shape. Good luck.

PS: I'm going to add a processing tag to your question. If you have any questions in the future, it's probably a good idea to make sure to include this tag. Otherwise I won't see it. :p