From 5a39e5d2b536c3c732caa9bd2707acc6f46f7f97 Mon Sep 17 00:00:00 2001 From: MatMasIt Date: Sat, 12 Mar 2022 00:50:24 +0100 Subject: [PATCH] Add files via upload --- Makefile | 4 ++ README.md | 46 +++++++++++++ args.c | 137 ++++++++++++++++++++++++++++++++++++ conway.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ display.c | 35 ++++++++++ main.c | 91 ++++++++++++++++++++++++ 6 files changed, 516 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 args.c create mode 100644 conway.c create mode 100644 display.c create mode 100644 main.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2c7d9d8 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +all: + gcc main.c -std=c99 -o conway -g +debug: + gdb main diff --git a/README.md b/README.md new file mode 100644 index 0000000..267d7fe --- /dev/null +++ b/README.md @@ -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) diff --git a/args.c b/args.c new file mode 100644 index 0000000..fbe9be8 --- /dev/null +++ b/args.c @@ -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; +} diff --git a/conway.c b/conway.c new file mode 100644 index 0000000..fe13ec6 --- /dev/null +++ b/conway.c @@ -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; +} \ No newline at end of file diff --git a/display.c b/display.c new file mode 100644 index 0000000..0b32303 --- /dev/null +++ b/display.c @@ -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"); + } +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..618d8f3 --- /dev/null +++ b/main.c @@ -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 +#include +#include +#include +#include +#include +#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; +}