Add files via upload

This commit is contained in:
Mattia Mascarello 2022-03-12 00:50:24 +01:00 committed by GitHub
parent 67a2c73583
commit 5a39e5d2b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 516 additions and 0 deletions

4
Makefile Normal file
View File

@ -0,0 +1,4 @@
all:
gcc main.c -std=c99 -o conway -g
debug:
gdb main

46
README.md Normal file
View 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
View 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
View 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
View 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
View 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;
}