0

I have two game of life implementations with a small difference in struct used:

typedef struct{
  char state;
  int neighbours;
} cell;

and

typedef struct{
  char current;
  char next;
  int neighbours;
} cell;

The bigger struct only needs few extra lines to work:

 for (int r = 0; r < rows; r++) {
      for(int c = 0; c < cols; c++) {
          if (field[r][c].next == ALIVE) {
              field[r][c].current = ALIVE;
          }
          else {
              field[r][c].current = DEAD;
          }
      }
   }
}

Yet if you run then, it's a totally different result. Why?

Code that works:


#include <stdio.h>
#include <stdlib.h>

#define ALIVE 'X'
#define DEAD '.'


typedef struct{
  char state;
  int neighbours;
} cell;


void initField(const int rows, const int cols, cell field[rows][cols]);
void printField(const int rows, const int cols, cell field[rows][cols]);
void getNeighbours(const int rows, const int cols, cell field[rows][cols]);
void updateField(const int rows, const int cols, cell field[rows][cols]);


int main(void) {

  //Keeps track for of user choice.
  char key;

  //World size.
  const int R = 20;
  const int C = 20;

  //Creates the RxC cell field.
  cell cells[R][C];

  initField(R, C, cells);

  do {
    printField(R, C, cells);
    scanf("%c", &key);
    updateField(R, C, cells);
  } while(key == '\n');

  return 0;
}


void initField(const int rows, const int cols, cell field[rows][cols]) {
  for (int r = 0 ; r < rows ; r++) {
    for (int c = 0 ; c < cols ; c++) {
      field[r][c].state = DEAD;
    }
  }
  field[0][1].state = ALIVE;
  field[1][2].state = ALIVE;
  field[2][0].state = ALIVE;
  field[2][1].state = ALIVE;
  field[2][2].state = ALIVE;
}

void printField(const int rows, const int cols, cell field[rows][cols]) {
  for (int r = 0; r < rows ; r++) {
    for (int c = 0; c < cols ; c++) {
      printf("%c ", field[r][c].state);
    }
    printf("\n");
  }
}

void getNeighbours(const int rows, const int cols, cell field[rows][cols]) {

  for(int rowCell = 0; rowCell < rows; rowCell++){
    for(int colCell = 0; colCell < cols; colCell++) {
      //Resets the neighbour count for all cells.
      field[rowCell][colCell].neighbours = 0;
      //Checks for alove neighbours "around" cell".
      for(int rl = -1; rl <= 1; rl++){
        for(int cl = -1; cl <= 1; cl++) {
          if(field[rowCell - rl][colCell - cl].state == ALIVE) {
            //Ignore the center cell to count as its own neighbour and ignore
            //the memory cells beyond the given size of the world for right and left.
            if(!(rl == 0 && cl == 0) && (rowCell-rl < rows) && (colCell-cl < cols) && (colCell-cl >= 0) && (rowCell-rl >= 0)) {
              field[rowCell][colCell].neighbours += 1;
            }
          }
        }
      }
    }
  }
}


void updateField(const int rows, const int cols, cell field[rows][cols]) {

  getNeighbours(rows, cols, field);

  for (int r = 0; r < rows; r++) {
    for (int c = 0; c < cols; c++) {
      if(field[r][c].neighbours == 3) {
        field[r][c].state = ALIVE;
      }
      else if(field[r][c].neighbours <= 1) {
        field[r][c].state = DEAD;
      }
      else if(field[r][c].neighbours >= 4) {
        field[r][c].state = DEAD;
      }
    }
 }
}

Code that DOES NOT:

 #include <stdio.h>
 #include <stdlib.h>

/* Constants, representation of states */
#define ALIVE 'X'
#define DEAD '.'

/* Declaration of data structure */
typedef struct{
  char current;
  char next;
  int neighbours;
} cell;


/* Declaration of functions */
void initField(const int rows, const int cols, cell field[rows][cols]);
void printField(const int rows, const int cols, cell field[rows][cols]);
void getNeighbours(const int rows, const int cols, cell field[rows][cols]);
void updateField(const int rows, const int cols, cell field[rows][cols]);


int main(void) {

  char key;

  //Rows and colum sizes.
  const int R = 20;
  const int C = 20;

  //Create an array of size 20x20 containin cells.
  cell cells[R][C];

  //Initates the game.
  initField(R, C, cells);

  //Prints field, menu and updates the game while the user presses enter.
  do {
    printField(R, C, cells);
    scanf("%c", &key);
    if(key == '\n') {
      updateField(R, C, cells);
    }
  } while(key == '\n');

  return 0;
}


void initField(const int rows, const int cols, cell field[rows][cols]) {
  for (int r = 0 ; r < rows ; r++) {
    for (int c = 0 ; c < cols ; c++) {
      field[r][c].current = DEAD;
    }
  }
  field[0][1].current = ALIVE;
  field[1][2].current = ALIVE;
  field[2][0].current = ALIVE;
  field[2][1].current = ALIVE;
  field[2][2].current = ALIVE;
}

void printField(const int rows, const int cols, cell field[rows][cols]) {

  for (int r = 0; r < rows ; r++) {
    for (int c = 0; c < cols ; c++) {
      printf("%c ", field[r][c].current);
    }
    printf("\n");
  }
}

void getNeighbours(const int rows, const int cols, cell field[rows][cols]) {

  for(int rowCell = 0; rowCell < rows; rowCell++){
    for(int colCell = 0; colCell < cols; colCell++) {
      //Resets the neighbour count for all cells.
      field[rowCell][colCell].neighbours = 0;
      //Checks for alove neighbours "around" cell".
      for(int rl = -1; rl <= 1; rl++){
        for(int cl = -1; cl <= 1; cl++) {
          if(field[rowCell - rl][colCell - cl].current == ALIVE) {
            //Ignore the center cell to count as its own neighbour and ignore
            //the data beyond the given size of the world for right and left.
            if(!(rl == 0 && cl == 0) && (rowCell-rl < rows) && (colCell-cl < cols) && (colCell-cl >= 0) && (rowCell-rl >= 0)) {
              field[rowCell][colCell].neighbours += 1;
            }
          }
        }
      }
    }
  }
}


void updateField(const int rows, const int cols, cell field[rows][cols]) {

  getNeighbours(rows, cols, field);
  for (int r = 0; r < rows; r++) {
    for (int c = 0; c < cols; c++) {
      if(field[r][c].neighbours == 3) {
        field[r][c].next = ALIVE;
      }
      else if(field[r][c].neighbours <= 1) {
        field[r][c].next = DEAD;
      }
      else if(field[r][c].neighbours >= 4) {
        field[r][c].next = DEAD;
      }
    }
  }

  for (int r = 0; r < rows; r++) {
      for(int c = 0; c < cols; c++) {
          if (field[r][c].next == ALIVE) {
              field[r][c].current = ALIVE;
          }
          else {
              field[r][c].current = DEAD;
          }
      }
  }
}
Senethys
  • 65
  • 9
  • 1
    Please provide more information. Describe your thoughts. Show the different results. Show the "few extra lines". Try for a [mcve]. As it is, your question is unclear. – Yunnosch Jan 10 '18 at 13:42
  • 2
    Perhaps you need to [learn how to debug your programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/)? – Some programmer dude Jan 10 '18 at 13:44
  • I understand that my question is unspecific. I've tried and tried to debug it and there is virtually nothing I can find. I wouldn't be here otherwise. I have read one of these btw. But ANY feedback would be helpful. @Yunnosch – Senethys Jan 10 '18 at 13:49
  • @Yunnosch Furthermore, I don't know how to reduce the program into a minimal setting since I don't know what causing the problem in the first place. – Senethys Jan 10 '18 at 13:51
  • 1
    In addition to my alltime favorite, linked by Some programmer dude, this might help to get you started. https://stackoverflow.com/questions/2069367/how-to-debug-using-gdb – Yunnosch Jan 10 '18 at 13:52
  • 1
    Does `if(field[rowCell - rl][colCell - cl].state == ALIVE) {` only access `field[][]` in bounds? I'd expect border issues requiring more code. On review, I am sure this accessing outside array bounds. – chux - Reinstate Monica Jan 10 '18 at 14:01
  • No it doesn't, and I'm aware of it. But the program works in this case (With the first defined struct above). @chux By works I mean perfectly behaves as it should be. – Senethys Jan 10 '18 at 14:04
  • since both sets of posted code seem to work (although the end result is different) the question I ask, is what exactly are you considering to be 'not working'? – user3629249 Jan 10 '18 at 15:36
  • 1
    The 'game of life' doesn't normally scroll the screen nor require the user to keep hitting some key – user3629249 Jan 10 '18 at 15:36
  • I know how the end result of world will be in advance. The second program is widely different, even though the "rules are the same". I am aware that this is not how usually GoL is implemented, the accurate portrayal of the game is not important here. @user3629249 – Senethys Jan 10 '18 at 15:41
  • the posted code (both versions) fails to handle the case when the current cell has exactly 2 live neighbors – user3629249 Jan 10 '18 at 15:56
  • It does, yet it the end result for the first program is correct. Why should it matter for the second program when I only change how I pass on status of the cells? – Senethys Jan 10 '18 at 16:04
  • 1
    it is poor programming practice to include header files those contents are not being used. Suggest removing the statement: `#include ` – user3629249 Jan 10 '18 at 16:04
  • 1
    regarding: `for( int rl = -1; rl <= 1; rl++ ) { for( int cl = -1; cl <= 1; cl++ ) { if(field[rowCell - rl][colCell - cl].state == ALIVE)` this is accessing beyond the bounds of the 'cell' For example: when `rowCell` = 0 and `colCell` = 0, then the check for the state is checking row -1 and col -1 And if the `.state` of that out-of-bounds cell happens to be `ALIVE` then the 'if' code block is entered. Suggest placing that 'if' statement inside the 'if' code block that checks for the boundaries – user3629249 Jan 10 '18 at 16:19
  • Thank you for the input! I actually need help how to avoid entering beyond the bounds in any case. It is still puzzling why the program works. It might be that it's highly unlikely that a cell can be alive where it has now been initiated. I'm wondering if this out of bounds problem is somehow connected to why the second program won't work. @user3629249 – Senethys Jan 10 '18 at 16:26
  • in function: `initField()`, in the second program, both `current` and `next` need to be properly initialized, probably to `DEAD` And when initializing the 5 `ALIVE` fields, the `next` also needs to be initialized to `ALIVE`. This additional initialization is needed because the `updateField()` is not handling the case where exactly two of the neighbors are `ALIVE` – user3629249 Jan 10 '18 at 16:36
  • Wow. You are totally right. I totally missed I had to initialize that too. I can't thank you enough! @user3629249 – Senethys Jan 10 '18 at 16:55

0 Answers0