Add files via upload
This commit is contained in:
parent
67a2c73583
commit
5a39e5d2b5
4
Makefile
Normal file
4
Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
all:
|
||||
gcc main.c -std=c99 -o conway -g
|
||||
debug:
|
||||
gdb main
|
46
README.md
Normal file
46
README.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Conway game in ANSI C
|
||||
|
||||
```
|
||||
Generation 0
|
||||
.@...@@@@..@.@..@@@@..@.@....@@@..@.@@@.@@@@.@@@@.
|
||||
.@.@@.@...@.@....@@.@.@.@.@.@....@@@@@@@@@@.@@.@..
|
||||
@@.@@.@@..@@.@..@@@...@...@....@@@.@@@.@.@..@...@@
|
||||
..@..@.@..@..@@.......@...@@...@..@.@@.@.@...@....
|
||||
.....@.@.@.....@@..@@..@.@@@@@@.@..@@.@@@@@@@....@
|
||||
@@@.@.@.@.@.....@@@...@..@..@.@@@@@.@....@.@@@@@..
|
||||
@@@@@@.@@@@@...@@@@@@.@@@...@@@@...@@...@@@.@@@..@
|
||||
.@@@@.@@.@.@..@.@..@@@..@..@.....@@@@@.@.@@.@..@@.
|
||||
..@@.@@..@@..@@@....@.@..@@.......@.@.@@..@.@.@@..
|
||||
@@.@.@.@@.@..@..@@.....@.@@..@....@.@@@@@.@@@@@@..
|
||||
@...@@@.@.@@.@@@.@.@.@.@.@.........@@@@.@.@@@@.@..
|
||||
@.@@@..@.@@.@@.@...@..@.@.@..@.@@.....@.@...@.@@..
|
||||
.@.@@@@.@@@.......@@..@.@...@@.@...@.@@@@.@@.@..@@
|
||||
@@@.@...@@@@.@....@@@.@@.@..@.@.@..@@.@.@.@@@@@@@.
|
||||
.@@@.@..@@..@..@@@@.@..@@@......@@@@.@@@..@@...@@@
|
||||
@@@@.........@.@.......@@.@@@@...@@.@@.@@@@@.@.@..
|
||||
@..@@@@@......@@....@@@.....@.....@@@..@..@...@..@
|
||||
@@..@..@.@@.@@..@@.@...@..@@@@.....@.@@........@@@
|
||||
.@@...@@@........@@@@.@..@....@@@@@.@.@......@@...
|
||||
@@.@@...@@...@@@@..@@.@@@@.....@.@.@@@@..@@.@.@.@@
|
||||
```
|
||||
|
||||
An *ANSI C* implementation of [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life)
|
||||
|
||||
Compile with `make`
|
||||
|
||||
## Example usages
|
||||
|
||||
* Basic (default is random)
|
||||
`./conway`
|
||||
* 20x20 calc grid, 5x10 view grid
|
||||
`./conway --calc 20x20 --view 5x10`
|
||||
* Change chars
|
||||
`./conway --dead-c - --alive-c Q`
|
||||
* No heading, get 10th gen
|
||||
`./conway --no-head --gen 10`
|
||||
* Input initial sequence
|
||||
`./conway --cells 5,5.6,6.4,7.5,7.6,7`
|
||||
|
||||
## Demonstration
|
||||
|
||||
[![asciicast](https://asciinema.org/a/PovKEcCgJLJKQJjOlfyHlTpCb.svg)](https://asciinema.org/a/PovKEcCgJLJKQJjOlfyHlTpCb)
|
137
args.c
Normal file
137
args.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* @file args.c
|
||||
* @author MatMasIt
|
||||
* @brief Argument Parsing for the conway Game of Life program
|
||||
* @version 0.1
|
||||
* @date 2022-03-12
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
// element considered, flags or vals
|
||||
|
||||
#define L_VAL 0 // Value
|
||||
#define L_CAL 2 // --calc rowsxcols
|
||||
#define T_CAL "--calc"
|
||||
#define L_VIE 3 // --view rowsxcols
|
||||
#define T_VIE "--view"
|
||||
#define L_GEN 4 // --gen
|
||||
#define T_GEN "--gen"
|
||||
#define L_NHG 5 // --no-head
|
||||
#define T_NHG "--no-head"
|
||||
#define L_DED 6 // --dead-c
|
||||
#define T_DED "--dead-c"
|
||||
#define L_ALC 7 // --alive-c
|
||||
#define T_ALC "--alive-c"
|
||||
#define L_CEL 9 // --cells
|
||||
#define T_CEL "--cells"
|
||||
|
||||
#define CR 20
|
||||
#define CC 50
|
||||
#define VR 20
|
||||
#define VC 50
|
||||
|
||||
#define DEADC '.'
|
||||
#define ALIVEC '@'
|
||||
|
||||
/**
|
||||
* @brief struct containing program arguments
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint64_t calcRows;
|
||||
uint64_t calcCols;
|
||||
// --calc rxc
|
||||
uint64_t viewRows;
|
||||
uint64_t viewCols;
|
||||
// --view rxc
|
||||
uint64_t requestedGen; // --gen
|
||||
bool headerShow; // --no-head
|
||||
char deadc; // --dead-c
|
||||
char alivec; // --alive-c
|
||||
bool interactive;
|
||||
char *cells; // x,y.x,y etc --cells
|
||||
} args;
|
||||
/**
|
||||
* @brief parse
|
||||
*
|
||||
* @param argc argument count
|
||||
* @param argv arguments pointer
|
||||
* @return args argument struct
|
||||
*/
|
||||
args parseArgs(int argc, char **argv)
|
||||
{
|
||||
args a;
|
||||
a.calcRows = CR;
|
||||
a.calcCols = CC;
|
||||
a.viewRows = VR;
|
||||
a.viewCols = VC;
|
||||
a.interactive = TRUE;
|
||||
a.requestedGen = 0;
|
||||
a.headerShow = TRUE;
|
||||
a.alivec = ALIVEC;
|
||||
a.deadc = DEADC;
|
||||
a.cells = NULL;
|
||||
uint8_t last;
|
||||
for (uint8_t i = 0; i < argc; i++)
|
||||
{
|
||||
char *s = argv[i];
|
||||
if (!strcmp(s, T_CAL))
|
||||
{
|
||||
last = L_CAL;
|
||||
}
|
||||
else if (!strcmp(s, T_VIE))
|
||||
{
|
||||
last = L_VIE;
|
||||
}
|
||||
else if (!strcmp(s, T_GEN))
|
||||
{
|
||||
last = L_GEN;
|
||||
}
|
||||
else if (!strcmp(s, T_NHG))
|
||||
{
|
||||
last = L_VAL;
|
||||
a.headerShow = FALSE;
|
||||
}
|
||||
else if (!strcmp(s, T_DED))
|
||||
{
|
||||
last = L_DED;
|
||||
}
|
||||
else if (!strcmp(s, T_ALC))
|
||||
{
|
||||
last = L_ALC;
|
||||
}
|
||||
else if (!strcmp(s, T_CEL))
|
||||
{
|
||||
last = L_CEL;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (last)
|
||||
{
|
||||
case L_CAL:
|
||||
sscanf(s, "%" PRIu64 "x%" PRIu64 "", &a.calcRows, &a.calcCols);
|
||||
break;
|
||||
case L_VIE:
|
||||
sscanf(s, "%" PRIu64 "x%" PRIu64 "", &a.viewRows, &a.viewCols);
|
||||
break;
|
||||
case L_GEN:
|
||||
sscanf(s, "%" PRIu64 "", &a.requestedGen);
|
||||
a.interactive = FALSE;
|
||||
break;
|
||||
case L_DED:
|
||||
sscanf(s, "%c", &a.deadc);
|
||||
break;
|
||||
case L_ALC:
|
||||
sscanf(s, "%c", &a.alivec);
|
||||
break;
|
||||
case L_CEL:
|
||||
a.cells = s;
|
||||
break;
|
||||
}
|
||||
last = L_VAL;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
203
conway.c
Normal file
203
conway.c
Normal file
|
@ -0,0 +1,203 @@
|
|||
/**
|
||||
* @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;
|
||||
}
|
35
display.c
Normal file
35
display.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* @file display.c
|
||||
* @author MatMasIt
|
||||
* @brief Handles displaying of the game
|
||||
* @version 0.1
|
||||
* @date 2022-03-12
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @brief print the screen matrix
|
||||
*
|
||||
* @param g Game
|
||||
* @param a Arguments
|
||||
*/
|
||||
void printScreen(Game *g, args a)
|
||||
{
|
||||
char c;
|
||||
bool error;
|
||||
if (a.headerShow)
|
||||
printf("Generation %" PRIu64 "\n", g->generation);
|
||||
for (uint64_t y = 0; y < g->viewRows; y++)
|
||||
{
|
||||
for (uint64_t x = 0; x < g->viewCols; x++)
|
||||
{
|
||||
if (getCell(g, x, y, &error) == DEAD)
|
||||
c = a.deadc;
|
||||
else
|
||||
c = a.alivec;
|
||||
printf("%c", c);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
91
main.c
Normal file
91
main.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* @file main.c
|
||||
* @author MatMasIt
|
||||
* @brief The Conway game of life in ANSI C
|
||||
* @version 0.1
|
||||
* @date 2022-03-12
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#define bool uint8_t
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define ALIVE TRUE
|
||||
#define DEAD FALSE
|
||||
#include "args.c"
|
||||
#include "conway.c"
|
||||
#include "display.c"
|
||||
/**
|
||||
* @brief The main function
|
||||
*
|
||||
* @param argc argument count
|
||||
* @param argv arguments pointer
|
||||
* @return int program result
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
args ar = parseArgs(argc, argv);
|
||||
char t;
|
||||
bool error;
|
||||
Game g = gameFromArgs(ar, &error);
|
||||
if (error)
|
||||
{
|
||||
printf("Initialization error: check arguments\n");
|
||||
exit(1);
|
||||
}
|
||||
uint64_t i = 0;
|
||||
if (ar.cells == NULL)
|
||||
{
|
||||
randomPopulate(&g);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *curLine = ar.cells;
|
||||
while (curLine)
|
||||
{
|
||||
const char *nextLine = strchr(curLine, '.');
|
||||
uint64_t x, y;
|
||||
int curLineLen = nextLine ? (nextLine - curLine) : strlen(curLine);
|
||||
char *tempStr = (char *)malloc(curLineLen + 1);
|
||||
if (tempStr)
|
||||
{
|
||||
memcpy(tempStr, curLine, curLineLen);
|
||||
tempStr[curLineLen] = '\0'; // NUL-terminate!
|
||||
sscanf(tempStr, "%" PRIu64 ",%" PRIu64 "", &x, &y);
|
||||
setCell(&g, x, y, ALIVE);
|
||||
free(tempStr);
|
||||
}
|
||||
else
|
||||
printf("Memory allocation error\n");
|
||||
|
||||
curLine = nextLine ? (nextLine + 1) : NULL;
|
||||
}
|
||||
}
|
||||
while (TRUE)
|
||||
{
|
||||
if (ar.requestedGen != 0)
|
||||
{
|
||||
if (i == ar.requestedGen)
|
||||
{
|
||||
printScreen(&g, ar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printScreen(&g, ar);
|
||||
}
|
||||
if (ar.interactive)
|
||||
scanf("%c", &t);
|
||||
progress(&g);
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user