Wondering if someone could help me work out this little issue. I've written a function to count the living neighbours of a cell in Conway's Game of Life:
int countLivingNeighbours(int a[][GRID_WIDTH], int x, int y){
int count = 0, cx, cy;
for(cy = y - 1; cy <= y + 1; cy++){
for(cx = x - 1; cx <= x + 1; cx++){
if(a[cy][cx] == ALIVE){
count++;
}
}
}
// subtract 1 so it's not counting it's own cell
count--;
return count;
}
It takes a 2-dimensional array of all cells as an argument, along with the x and y co-ordinates of the cell we want to check.
When running the program using this function to count living neighbours, it results in a bit of a glitch. It's hard to describe, so I'll post an image of one final cell arrangement I had:
Normally, there would be visible patterns, shapes, and blocks, but instead we just get lines.
If anyone could help me, that would be great!
I'll also post the full code, in case I've misidentified the problem:
#include <SDL.h>
#include <stdio.h>
#include <stdbool.h>
#define CELL_SIZE 10
#define GRID_WIDTH 100
#define GRID_HEIGHT 100
typedef enum {ALIVE, DEAD} State;
// General functions
void quitAll(void);
// SDL related visual functions
void drawGrid(SDL_Renderer *r, int winWidth, int winHeight);
void drawCells(SDL_Renderer *r, int a[][GRID_WIDTH]);
// Game of Life functions
void updateCells(int a[][GRID_WIDTH]); // takes cells array as input
int countLivingNeighbours(int a[][GRID_WIDTH], int x, int y);
int main(int argc, char *argv[]){
// Initialise SDL
SDL_Init(SDL_INIT_VIDEO);
// Create window
int winWidth = GRID_WIDTH * CELL_SIZE;
int winHeight = GRID_HEIGHT * CELL_SIZE;
SDL_Window *window = SDL_CreateWindow(
"Game of Life", // Title
SDL_WINDOWPOS_CENTERED, // Initial window x position
SDL_WINDOWPOS_CENTERED, // Initial window y position
winWidth, // Window Width
winHeight, // Window Height
0 // Flags
);
if(window == NULL){
printf("Failed to create window. %s\n", SDL_GetError());
return 1;
}
// Create renderer
SDL_Renderer *renderer = SDL_CreateRenderer(
window, // Window
-1, // Monitor index (-1 for first available)
SDL_RENDERER_ACCELERATED // Flags
);
if(renderer == NULL){
printf("Failed to create renderer. %s\n", SDL_GetError());
return 1;
}
// Setup event handling + mouse co-ordinate handling
SDL_Event event;
int mouseX, mouseY;
bool mouse_left_down = false;
bool mouse_right_down = false;
// Set all cells to initial state of dead
int cells[GRID_HEIGHT][GRID_WIDTH];
int cx, cy;
for(cy = 0; cy < GRID_HEIGHT; cy++){
for(cx = 0; cx < GRID_WIDTH; cx++){
cells[cy][cx] = DEAD;
}
}
while(1){
// Handle events/input
while(SDL_PollEvent(&event) != 0){
switch(event.type){
case SDL_QUIT: // Check if user has quit
return 1;
case SDL_MOUSEBUTTONDOWN:
if(event.button.button == SDL_BUTTON_LEFT){
mouse_left_down = true;
break;
} else if(event.button.button == SDL_BUTTON_RIGHT){
mouse_right_down = true;
break;
}
case SDL_MOUSEBUTTONUP:
if(event.button.button == SDL_BUTTON_LEFT){
mouse_left_down = false;
break;
} else if(event.button.button == SDL_BUTTON_RIGHT){
mouse_right_down = false;
break;
}
// If user presses space, simulate a single change
case SDL_KEYDOWN:
if(event.key.keysym.sym == SDLK_SPACE){
updateCells(cells);
}
}
}
// Allow user to draw new living cells
if(mouse_left_down == true){
SDL_GetMouseState(&mouseX, &mouseY); // Update mouse position
cells[mouseY / CELL_SIZE][mouseX / CELL_SIZE] = ALIVE; // Set the clicked on cell to alive
}
// Allow user to kill cells
else if(mouse_right_down == true){
SDL_GetMouseState(&mouseX, &mouseY); // Update mouse position
cells[mouseY / CELL_SIZE][mouseX / CELL_SIZE] = DEAD; // Set the clicked on cell to alive
}
// Set screen colour to white
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// Render white to screen (clear screen)
SDL_RenderClear(renderer);
// Draw the grid and living cells
drawGrid(renderer, winWidth, winHeight);
drawCells(renderer, cells);
// Update screen
SDL_RenderPresent(renderer);
}
// Exit SDL and SDL_image
SDL_Quit();
return 0;
}
/*
1. Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
2. Any live cell with two or three live neighbours lives on to the next generation.
3. Any live cell with more than three live neighbours dies, as if by overpopulation.
4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
*/
void updateCells(int a[][GRID_WIDTH]){
int new[GRID_HEIGHT][GRID_WIDTH];
int cy, cx; // vertical count, horizontal count
for(cy = 0; cy < GRID_HEIGHT; cy++){
for(cx = 0; cx < GRID_WIDTH; cx++){
if(a[cy][cx] == ALIVE && countLivingNeighbours(a, cx, cy) < 2){
new[cy][cx] = DEAD;
}
// Any live cell with two or three live neighbours lives on to the next generation.
if(a[cy][cx] == ALIVE && (countLivingNeighbours(a, cx, cy) == 2 || countLivingNeighbours(a, cx, cy) == 3)){
new[cy][cx] = ALIVE;
}
// Any live cell with more than three live neighbours dies, as if by overpopulation.
else if(a[cy][cx] == ALIVE && countLivingNeighbours(a, cx, cy) > 3){
new[cy][cx] = DEAD;
}
// Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
else if(a[cy][cx] == DEAD && countLivingNeighbours(a, cx, cy) == 3){
new[cy][cx] = ALIVE;
}
else{
new[cy][cx] = DEAD;
}
}
}
// Update all cells into new states
for(cy = 0; cy < GRID_HEIGHT; cy++){
for(cx = 0; cx < GRID_WIDTH; cx++){
a[cy][cx] = new[cy][cx];
}
}
}
// THERE'S NO ERROR CHECKING HERE WHICH IS BAD
// Should ideally check if a cell even exists before checking its state
int countLivingNeighbours(int a[][GRID_WIDTH], int x, int y){
int count = 0, cx, cy;
for(cy = y - 1; cy <= y + 1; cy++){
for(cx = x - 1; cx <= x + 1; cx++){
if(a[cy][cx] == ALIVE){
count++;
}
}
}
// subtract 1 so it's not counting it's own cell
count--;
return count;
}
void drawGrid(SDL_Renderer *r, int winWidth, int winHeight){
// Draw vertical grid lines
for(int v = CELL_SIZE; v < winWidth; v += CELL_SIZE){
// Set draw colour to grey
SDL_SetRenderDrawColor(r, 110, 110, 110, 110);
// Draw vertical line
SDL_RenderDrawLine(r, v, 0, v, winHeight);
}
// Draw horizontal grid lines
for(int h = CELL_SIZE; h < winHeight; h += CELL_SIZE){
// Set draw colour to grey
SDL_SetRenderDrawColor(r, 110, 110, 110, 110);
// Draw horizontal line
SDL_RenderDrawLine(r, 0, h, winWidth, h);
}
}
void drawCells(SDL_Renderer *r, int a[][GRID_WIDTH]){
// Define cell width/height
SDL_Rect cellRect;
cellRect.w = CELL_SIZE + 1; // Same size as one cell +1 so it covers the grid line fully
cellRect.h = CELL_SIZE + 1; // Same size as one cell +1 so it covers the grid line fully
// Draw living cells
int cx, cy;
for(cy = 0; cy < GRID_HEIGHT; cy++){
for(cx = 0; cx < GRID_WIDTH; cx++){
if(a[cy][cx] == ALIVE){
// Set cell x/y pos
cellRect.x = cx * CELL_SIZE;
cellRect.y = cy * CELL_SIZE;
SDL_SetRenderDrawColor(r, 0, 0, 0, 0);
SDL_RenderFillRect(r, &cellRect);
}
}
}
}