conway/conway.c

203 lines
4.7 KiB
C

/**
* @file conway.c
* @author MatMasIt
* @brief Conway game of life main game functions
* @version 0.1
* @date 2022-03-12
*
* @copyright Copyright (c) 2022
*
*/
/**
* @brief Game struct
*
*/
typedef struct
{
uint64_t generation;
uint64_t rows;
uint64_t cols;
uint64_t viewRows;
uint64_t viewCols;
bool *data;
} Game;
/**
* @brief instantiate a new game
*
* @param calcRows rows of the calculation matrix
* @param calcCols cols of the calculation matrix
* @param viewRows rows of the viewport matrix
* @param viewCols cols of the viewport matrix
* @param error error pointer
* @return Game game struct
*/
Game newGame(uint64_t calcRows, uint64_t calcCols, uint64_t viewRows, uint64_t viewCols, bool *error)
{
Game g;
if (viewRows > calcRows || viewCols > calcCols)
{
*error = TRUE;
return g;
}
g.generation = 0;
g.rows = calcRows;
g.cols = calcCols;
g.viewRows = viewRows;
g.viewCols = viewCols;
g.data = calloc(1, g.rows * g.cols * sizeof(bool));
if (g.data == NULL)
{
*error = TRUE;
return g;
}
*error = FALSE;
return g;
}
/**
* @brief Check if coordinates are valid
*
* @param g Game
* @param x x-coord
* @param y y-cord
* @return true if valid
* @return false if not valid
*/
bool checkCoords(Game *g, uint64_t x, uint64_t y)
{
if (x > g->cols - 1 || y > g->rows - 1)
return FALSE;
return TRUE;
}
/**
* @brief Set a cell's value
*
* @param g Game
* @param x x-coord
* @param y y-coord
* @param isAlive aliveness status
* @return true: success, false: error
*/
bool setCell(Game *g, uint64_t x, uint64_t y, bool isAlive)
{
if (!checkCoords(g, x, y))
return FALSE;
g->data[x + y * g->cols] = isAlive;
return TRUE;
}
/**
* @brief Get a cell's valie
*
* @param g Game
* @param x x-coord
* @param y y-coord
* @param error error pointer
* @return true: alive, false: not-alive
*/
bool getCell(Game *g, uint64_t x, uint64_t y, bool *error)
{
if (!checkCoords(g, x, y))
{
*error = TRUE;
return ALIVE;
}
*error = FALSE;
return g->data[x + y * g->cols];
}
/**
* @brief Count the number of alive neigbours for a cell
*
* @param g Game
* @param x x-coord
* @param y y-coord
* @return int number of alive neigbours
*/
int countLiveNeighbours(Game *g, uint64_t x, uint64_t y)
{
uint8_t alive = 0;
bool error = FALSE;
if (getCell(g, x - 1, y - 1, &error) == ALIVE && !error)
alive++;
if (getCell(g, x - 1, y, &error) == ALIVE && !error)
alive++;
if (getCell(g, x - 1, y + 1, &error) == ALIVE && !error)
alive++;
if (getCell(g, x, y - 1, &error) == ALIVE && !error)
alive++;
if (getCell(g, x, y + 1, &error) == ALIVE && !error)
alive++;
if (getCell(g, x + 1, y - 1, &error) == ALIVE && !error)
alive++;
if (getCell(g, x + 1, y, &error) == ALIVE && !error)
alive++;
if (getCell(g, x + 1, y + 1, &error) == ALIVE && !error)
alive++;
return alive;
}
/**
* @brief Progress game by calculating the new matrix and increasing generation
*
* @param g Game
*/
void progress(Game *g)
{
bool error;
Game c = newGame(g->rows, g->cols, g->viewRows, g->viewCols, &error); // new temp field to apply all rules instantly
uint8_t liveN;
bool val;
memcpy(c.data, g->data, g->rows * g->cols * sizeof(bool));
c.generation = g->generation + 1;
for (uint64_t y = 0; y < g->rows; y++)
{
for (uint64_t x = 0; x < g->cols; x++)
{
val = getCell(g, x, y, &error);
liveN = countLiveNeighbours(g, x, y);
if (val == DEAD && liveN == 3)
setCell(&c, x, y, ALIVE); // Any dead cell with three live neighbours becomes a live cell
else if (val == ALIVE && (liveN == 2 || liveN == 3))
setCell(&c, x, y, ALIVE); // Any live cell with two or three live neighbours survives
else
setCell(&c, x, y, DEAD); // All other cells either stay dead or die
}
}
free(g->data);
// copy struct and free previous field
*g = c;
}
/**
* @brief Set random population
*
* @param g Game
*/
void randomPopulate(Game *g)
{
srand(time(NULL));
for (uint64_t y = 0; y < g->rows; y++)
{
for (uint64_t x = 0; x < g->cols; x++)
{
if (rand() % 2)
setCell(g, x, y, ALIVE);
else
setCell(g, x, y, DEAD);
}
}
}
/**
* @brief Build a game from args struct
*
* @param a the args struct
* @param error error pointer
* @return Game Game
*/
Game gameFromArgs(args a, bool *error)
{
Game c = newGame(a.calcRows, a.calcCols, a.viewRows, a.viewCols, error);
return c;
}