conway/conway.c
2023-05-29 05:49:14 -04:00

162 lines
4.7 KiB
C

/**
* Yet another goddamn implementation of Conway's Game of Life.
* I'm under the impression that every programmer must do this at some point,
* and since I've been programming for a long time I guess I should get it out
* of the way...
*
* (c) 2023 A.M. Rowsell
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// so many damn includes
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <wchar.h>
#include <locale.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
void printGrid(int gridX, int gridY, int **arr, int generation) {
char title[16];
snprintf(title, 16, "Generation %d", generation);
int spaces = ((float)gridX / 2.0F) - (strlen(title) / 2.0);
if (spaces <= 0) {
wprintf(L"%s\n", title);
} else {
for(int i = 0; i < spaces; i++) {
wprintf(L" ");
}
wprintf(L"%s\n", title);
}
wprintf(L"%lc", 0x2554UL); // prints ╔
for (int j = 0; j < gridX; j++) {
wprintf(L"%lc", 0x2550UL); // prints ═
}
wprintf(L"%lc", 0x2557UL); // prints ╗
wprintf(L"\n");
for (int j = 0; j < gridY; j++) {
wprintf(L"%lc", 0x2551UL); // prints ║
for (int i = 0; i < gridX; i++) {
wprintf(L"%lc", ((arr[i][j] == 1) ? 0x0023UL : 0x0020UL));
}
wprintf(L"%lc\n", 0x2551UL); // prints ║
}
wprintf(L"%lc", 0x255AUL); // prints ╚
for (int j = 0; j < gridX; j++) {
wprintf(L"%lc", 0x2550UL);
}
wprintf(L"%lc", 0x255DUL); // prints ╝
return;
}
void doGeneration(int gridX, int gridY, int **arr) {
// add up all live cells from [x-1][y-1] to [x+1][y+1]
// if total is 3, cell becomes alive
// if total is 4, cell retains state
// any other, cell dies
// also, all x/y are modulo gridX gridY so we get wraparound
// temporary array to hold processed generation
int *tempArr[gridX];
for(int i = 0; i < gridX; i++) {
tempArr[i] = (int *)malloc(sizeof(int)*gridY);
}
for(int i = 0; i < gridX; i++) {
for(int j = 0; j < gridY; j++) {
tempArr[i][j] = arr[i][j];
}
}
// total keeps track of live neighbours
int total = 0;
for(int i = 0; i < gridY; i++) {
// start of a line
for(int j = 0; j < gridX; j++) {
total = 0;
// start of 9-cell eval
for(int k = -1; k < 2; k++) {
for(int l = -1; l < 2; l++) {
total += (arr[(j+k)%gridX][(i+l)%gridY] == 1) ? 1 : 0;
}
}
if(total == 3) {
tempArr[j][i] = 1;
} else if (total == 4) {
tempArr[j][i] = arr[j][i];
} else {
tempArr[j][i] = 0;
}
}
}
for(int i = 0; i < gridX; i++) {
for(int j = 0; j < gridY; j++) {
arr[i][j] = tempArr[i][j];
}
}
// free all that memory
for(int i = 0; i < gridX; i++) {
free(tempArr[i]);
}
return;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: conway <xsize> <ysize> [seed]\n");
exit(255);
}
setlocale(LC_CTYPE, "");
int gridX = 0, gridY = 0, seed = 0, generation = 0;
for (int i = argc - 1; i > 0; i--) {
switch (i) {
case 1:
gridX = atoi(argv[i]);
break;
case 2:
gridY = atoi(argv[i]);
break;
case 3:
seed = atoi(argv[i]);
break;
}
}
if (seed == 0) {
time_t t;
srand((unsigned)time(&t));
} else {
srand(seed);
}
// dynamically allocate the 2D array, could be quite large!
int *arr[gridX];
for (int i = 0; i < gridX; i++) {
arr[i] = (int*)malloc(gridY * sizeof(int));
}
// seed the array
for (int i = 0; i < gridX; i++) {
for (int j = 0; j < gridY; j++) {
arr[i][j] = (rand() % 2 == 0) ? 1 : 0;
}
}
// display initial grid
printGrid(gridX, gridY, arr, generation);
// loop doing gens until user decides to quit
char choice[] = "y";
while(strcmp(choice, "y") == 0) {
wprintf(L"\n");
generation++;
doGeneration(gridX, gridY, arr);
printGrid(gridX, gridY, arr, generation);
wprintf(L"\nDo another generation? [y/n]: ");
sprintf(choice, "%c", tolower(getchar()));
getchar(); // consome the newline
}
// clean up allocated memory
for(int i = 0; i < gridX; i++) {
free(arr[i]);
}
return 0;
}