Some help with Conways Game Of Life

Alright so I’ve been working on the game of life and have run into a weird problem. There are squares deciding to draw alive for an unknown reason. I have used print to check what the state is at those steps and I know it is not the state being wrong.

`
public int gridSize = 50;

public float cellSize = 10;
public float spacing = 10;
public Cell[][] cells = new Cell[gridSize][gridSize];
public int startingCells = 3;
public boolean startPlaced = false;

public int timeBetweenTicks = 5;
public boolean isBetweenTicks = false;
public int startTime;
public int tickCount = 0;

public boolean gameActive = false;

void setup(){
background(19, 19, 19);
size(501, 600);

for(int col=0; col<gridSize; col++){
for(int row=0; row<gridSize; row++){
cells[col][row] = new Cell(col, row, 0);
}
}
}

void draw(){
clear();

fill(0, 200, 0);
text("Current Wait: ", 50, 550);
fill(0, 200, 0);
text("Ticks Since Start: ", 50, 570);

DrawSquares();

if(gameActive && !isBetweenTicks){

//cells[0][0].SetState(1);
if(!startPlaced){
  SetStartingCells();
}

if(tickCount >1){
  UpdateCells();
}

delay(1000);
startTime = second();
isBetweenTicks = true;
//delay(timeBetweenTicks * 1000);

}

HandleTime();

}

void UpdateCells(){
for(int col=0; col<gridSize; col++){
for(int row=0; row<gridSize; row++){
cells[col][row].UpdateState();
}
}
}

void SetStartingCells(){
for(int i=0; i<startingCells; i++){
int colum = (int)random(cells.length);
int row = (int)random(cells[0].length);
cells[colum][row].SetState(1);
}

startPlaced = true;
}

void HandleTime(){
if(isBetweenTicks){
int elapsedTime = second() - startTime;
//println(elapsedTime);
fill(0, 200, 0);
text(elapsedTime, 150, 550);
fill(0, 200, 0);
text(tickCount, 180, 570);

if(elapsedTime == timeBetweenTicks){
  isBetweenTicks = false;
  tickCount++;
  elapsedTime = 0;
}else if(elapsedTime < 0){
 startTime = second();
 elapsedTime = second() - startTime;
}

}
}

void DrawSquares(){
for(int col=0; col<gridSize; col++){
for(int row=0; row<gridSize; row++){
cells[col][row].DrawSquare();
}
}
}

void keyReleased(){

if(key == ‘s’){
//println(“S has been Released”);
gameActive = true;
}else if(key == ‘p’){
gameActive = false;
}
}

class Cell{
//col, row
int gridX, gridY;
//1 = alive, 0 = dead
int cellState = 0;
int aliveNeighbours = 0;

Cell(int gridPosX, int gridPosY, int state){
gridX = gridPosX;
gridY = gridPosY;
cellState = state;
}

void DrawSquare(){
stroke(0, 255, 0);

if(cellState == 0){
  //dead
  fill(0);
}else if (cellState == 1){
  //alive
  fill(255);
}

rect(gridX * spacing, gridY * spacing, cellSize, cellSize);

}

void SetState(int newState){
cellState = newState;
}

int GetState(){
return cellState;
}

void UpdateState(){

if(gridX < gridSize-1 && gridY < gridSize-1){
  if(gridX > 0 && gridY > 0){
  
    ArrayList<Cell> neighbours = new ArrayList<Cell>();
    //println(gridX + " , " + gridY);
    //top row
    neighbours.add(cells[gridX - 1][gridY + 1]);
    neighbours.add(cells[gridX][gridY + 1]);
    neighbours.add(cells[gridX + 1][gridY + 1]);
    
    //middle row
    neighbours.add(cells[gridX - 1][gridY]);
    //Cell mid2 = cells[gridX][gridY]; Ignoring ourself
    neighbours.add(cells[gridX + 1][gridY + 1]);
    
    //bottom row
    neighbours.add(cells[gridX - 1][gridY + 1]);
    neighbours.add(cells[gridX][gridY + 1]);
    neighbours.add(cells[gridX + 1][gridY + 1]);
    
    for(int i=0; i<neighbours.size(); i++){
      Cell curCell = neighbours.get(i);
      if(curCell.GetState() == 1){
        aliveNeighbours++;
      }
    }
    
    boolean survived = false;
    
    if(cellState == 1){
      if(aliveNeighbours == 2 || aliveNeighbours == 3){
        survived = true;
        //println("survived");
      }
    }else{
      if(aliveNeighbours == 3){
        survived = true;
        //println("survived");
      }
    }
    
    if(survived){
      SetState(1);
      //println("state set to 1");
    }else{
      SetState(0);
    }
    
      //println(aliveNeighbours + ", State = " + cellState);
      aliveNeighbours = 0;
    }
  }
  
  
}

}
`
Im a little lost and wouldn’t mind a helping hand

1 Like

Look carefully at your neighborhood offsets.

Also, and certainly less obvious:
With each call to UpdateState(), the contents of the grid can change.
You will want to preserve the previous state of the entire grid, calculating a “next state” for each cell, then update the cells all at once.
Updating at-once, your program will follow the classic game of life patterns.

Sorry to say but I cannot see the issue with my finding of neighbors. The only thing i can think of is that i used a list instead of a 2d array to look through them. As for the state i was mainly trying to figure it out with minimal help so i figured i had done that right.

Hi,

I can see several mistake in your check neighbor function:

  • For the top row, it should be gridY - 1 and not gridY + 1
  • For the middle row, the last check should be gridY not gridY + 1

Also, as @noahbuddy wrote, you need to create a copy of your grid before updating it. The reason is that if you update on the same grid, you may modify the state of a cell that will later be the neighbor of a next cell. And instead of checking the old state of that cell you will be using the new state which will be different.

Oh thanks, I feel embarrassed I didn’t see such a simple mistake. And I understand @jb4x that you would make a copy, so after each cell checks, it should update the board then the next cell checks? I feel as if I have done my 2d array wrong and should have used int instead of making a class because I don’t have a clue how to implement that

again thanks for the help so far and apologies for my lack of comprehension

@Ironbatman1 One option preserving the state is to hold a “target state” in each cell. After all cells are calculated then transfer every cell’s “target state” into its “current state”.

So like having the cell check the neighbours then holding what it wants to be in that target state then at the end once all cells are checked I send a call out to have the cells finalize it?

Hello again,

No need to apologies, you are here because you have questions =)

To expend on @noahbuddy’s idea, you need very little modification of your code.
You want to have to variables in your Cell class:

int cellState = 0; // Already exists
int nextCellState; // Will hold the state that the cell should have for the next turn

Then, when you check your neighbor, you check the current state, so cellState but you update the nextCellState variable.

After you are done check the new state for all your cells, you simply need to affect the new nextCellState value to the cellState variable.

Your code would look something like this:

for(int col=0; col<gridSize; col++){
  for(int row=0; row<gridSize; row++){
    cells[col][row].computeNextState(); // Update the nextCellState variable
  }
}

for(int col=0; col<gridSize; col++){
  for(int row=0; row<gridSize; row++){
    cells[col][row].updateState(); // Copy the nextCellState value to the cellState variable
    cells[col][row].display();     // Display the cell based on the cellState value
  }
}

Is this solved?

I would appreciate it if you could post your entire code

So clear() really seems to be messing up my scripts abillity to draw with the changes, is there an alternative?

The clear() function has no effect on your sketch:

This function only works on PGraphics objects created with the createGraphics() function.

from the reference.

Can you post the new version of your sketch with our comments implemented?

This will no longer draw cells at all lol

public int gridSize = 50;

public float cellSize = 10;
public float spacing = 10;
public Cell[][] cells = new Cell[gridSize][gridSize];
public int startingCells = 3;
public boolean startPlaced = false;

public int timeBetweenTicks = 5;
public boolean isBetweenTicks = false;
public int startTime;
public int tickCount = 0;

public boolean gameActive = false;



void setup(){
  background(19, 19, 19);
  size(501, 600);
  
  for(int col=0; col<gridSize; col++){
    for(int row=0; row<gridSize; row++){
      cells[col][row] = new Cell(col, row, 0);
    }
  }
}

void draw(){
  
  clear();
  fill(0, 200, 0);
  text("Current Wait: ", 50, 550);
  fill(0, 200, 0);
  text("Ticks Since Start: ", 50, 570);
      
  if(gameActive && !isBetweenTicks){     
      //cells[0][0].SetState(1);
      if(!startPlaced){
        SetStartingCells();
      }
      
       UpdateCells();
       for(int col=0; col<gridSize; col++){
        for(int row=0; row<gridSize; row++){
          cells[col][row].UpdateState();
          cells[col][row].DrawSquare();
        }
      }
      
      delay(1000);
      startTime = second();
      isBetweenTicks = true;
      print(isBetweenTicks);
      //delay(timeBetweenTicks * 1000);
    }
    
     HandleTime();
  }

void UpdateCells(){
  for(int col=0; col<gridSize; col++){
    for(int row=0; row<gridSize; row++){
      cells[col][row].ComputeNextCellState();
    }
  } 
}

void SetStartingCells(){
  for(int i=0; i<startingCells; i++){
    int colum = (int)random(cells.length);
    int row = (int)random(cells[0].length);
    cells[colum][row].SetState(1);
  }
  
  startPlaced = true;
}

void HandleTime(){
  if(isBetweenTicks){
    int elapsedTime = second() - startTime;
    //println(elapsedTime);
    fill(0, 200, 0);
    text(elapsedTime, 150, 550);
    fill(0, 200, 0);
    text(tickCount, 180, 570);
    
    if(elapsedTime == timeBetweenTicks){
      isBetweenTicks = false;
      tickCount++;
      elapsedTime = 0;
    }else if(elapsedTime < 0){
     startTime = second();
     elapsedTime = second() - startTime;
    }
  }
}

void keyReleased(){
  
  if(key == 's'){
      //println("S has been Released");
      gameActive = true;
    }else if(key == 'p'){
      gameActive = false;
    }
}

class Cell{
  //col, row
  int gridX, gridY;
  //1 = alive, 0 = dead
  int cellState = 0;
  int nextCellState;
  int aliveNeighbours = 0;
  
  Cell(int gridPosX, int gridPosY, int state){
    gridX = gridPosX;
    gridY = gridPosY;
    cellState = state;
  }
  
  void UpdateState(){
    SetState(nextCellState);
  }
  
  void DrawSquare(){
    push();
    stroke(0, 255, 0);
    
    if(cellState == 0){
      //dead
      fill(0);
    }else if (cellState == 1){
      //alive
      fill(255);
    }
    
    rect(gridX * spacing, gridY * spacing, cellSize, cellSize);
    pop();
  }
  
  void SetState(int newState){
    nextCellState = newState;
  }
  
  int GetState(){
    return cellState;
  }
  
  void ComputeNextCellState(){
    
    if(gridX < gridSize-1 && gridY < gridSize-1){
      if(gridX > 0 && gridY > 0){
      
        ArrayList<Cell> neighbours = new ArrayList<Cell>();
        //println(gridX + " , " + gridY);
        //top row
        neighbours.add(cells[gridX - 1][gridY - 1]);
        neighbours.add(cells[gridX][gridY - 1]);
        neighbours.add(cells[gridX + 1][gridY - 1]);
        
        //middle row
        neighbours.add(cells[gridX - 1][gridY]);
        //Cell mid2 = cells[gridX][gridY]; Ignoring ourself
        neighbours.add(cells[gridX + 1][gridY]);
        
        //bottom row
        neighbours.add(cells[gridX - 1][gridY + 1]);
        neighbours.add(cells[gridX][gridY + 1]);
        neighbours.add(cells[gridX + 1][gridY + 1]);
        
        for(int i=0; i<neighbours.size(); i++){
          Cell curCell = neighbours.get(i);
          if(curCell.GetState() == 1){
            aliveNeighbours++;
          }
        }
        
        boolean survived = false;
        
        if(cellState == 1){
          if(aliveNeighbours == 2 || aliveNeighbours == 3){
            survived = true;
            //println("survived");
          }
        }else{
          if(aliveNeighbours == 3){
            survived = true;
            //println("survived");
          }
        }
        
        if(survived){
          nextCellState = 1;
          //println("state set to 1");
        }else{
          nextCellState = 0;
        }
        
          //println(aliveNeighbours + ", State = " + cellState);
          aliveNeighbours = 0;
        }
      }
      
      
    }
  }

You are almost there.

Change this line in SetState():
nextCellState = newState;
to:
cellState = newState;

Then if you bring the clear function into the if (gameActive && !isBetweenTicks){ block, the display will only refresh when you are about to draw something new.

Since you are using random to place live cells at the start, place a bunch more. Chances are, there will be very few cells with neighbors and they will vanish.
startingCells = 150; // or however many you like

Works now! had to make a small change to how it displays but other than that it works thank you for the help! And I’m not sure if I should post it as I feel that would ruin part of having to make it if people copy it does that make sense or am I off my rocker?

Thank you to all that took time to help I appreciate it!!

1 Like

tbf I’m not sure it actually matters but you can get the answer from the answers here and from my original no?

1 Like