203 lines
4.7 KiB
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;
|
||
|
}
|