infinite-gol/life.c

301 lines
7.2 KiB
C

#include "canvas.h"
#include <time.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "player.h"
#define MAX_STDIN_SIZE 1024
int readFromStdin(Canvas *world, int *w, int *h)
{
int y = 0;
char line[MAX_STDIN_SIZE];
fprintf(stderr, "Waiting for world input.\n Ctrl+D to stop. '.' is dead, '#' is alive\n\n");
while (!feof(stdin)) {
while (fgets(line, MAX_STDIN_SIZE, stdin) == NULL && !feof(stdin));
if (feof(stdin))
break;
// Process Input
for (int x = 0; x < MAX_STDIN_SIZE; x++) {
char ch = line[x];
if (ch == '\0')
break;
else if (ch == '#')
canvasSetCell(world, x, y, true);
else if (ch == '.')
canvasSetCell(world, x, y, false);
else if (ch == '\n') // row number is here in case of long lines in files
y++;
else {
fprintf(stderr, "Syntax error on line %d character %d ('%c')\n", x, y, ch);
return 1;
}
if (x > (*w))
(*w) = x;
}
}
(*h) = y;
return 0;
}
#define TEST(cond) if (!(cond)) { printf("Test failed: " #cond "\n"); return false;}
bool run_tests()
{
Canvas *world = createCanvas();
canvasSetCell(world, -1, -1, true);
canvasSetCell(world, -1, 0, true);
canvasSetCell(world, 4, -1, true);
canvasSetCell(world, 0, -1, true);
canvasSetCell(world, 0, 0, true);
Chunk *cursor = world->center;
int neighbours[CHUNK_SIZE][CHUNK_SIZE];
TEST(cursor);
TEST(cursor->x == 0);
TEST(cursor->y == 0);
TEST(cursor->around[ED_T]);
TEST(cursor->around[ED_T]->current[0 + (CHUNK_SIZE - 1) * CHUNK_SIZE]);
TEST(cursor->around[ED_L]);
TEST(cursor->around[ED_TL]);
TEST(!cursor->around[ED_TR]);
TEST(!cursor->around[ED_R]);
TEST(!cursor->around[ED_BR]);
TEST(!cursor->around[ED_B]);
TEST(!cursor->around[ED_BL]);
{
chunkCollectNeighbours(cursor, neighbours);
int expected[CHUNK_SIZE][CHUNK_SIZE] = {
{3, 2, 0, 1, 1, 1, 0, 0},
{2, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0}
};
for (int x = 0; x < CHUNK_SIZE; ++x)
for (int y = 0; y < CHUNK_SIZE; ++y) {
if (neighbours[y][x] != expected[y][x])
fprintf(stderr, "Check (%d, %d), got %d expected %d\n",
x, y, neighbours[y][x], expected[y][x]);
TEST(neighbours[y][x] == expected[y][x]);
}
}
cursor = cursor->around[ED_T];
TEST(cursor);
TEST(cursor->around[ED_B]);
TEST(cursor->around[ED_L]);
TEST(cursor->around[ED_BL]);
TEST(!cursor->around[ED_TL]);
TEST(!cursor->around[ED_T]);
TEST(!cursor->around[ED_TR]);
TEST(!cursor->around[ED_R]);
TEST(!cursor->around[ED_BR]);
{
chunkCollectNeighbours(cursor, neighbours);
int expected[CHUNK_SIZE][CHUNK_SIZE] = {
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{2, 1, 0, 1, 1, 1, 0, 0},
{3, 2, 0, 1, 0, 1, 0, 0}
};
for (int x = 0; x < CHUNK_SIZE; ++x)
for (int y = 0; y < CHUNK_SIZE; ++y) {
if (neighbours[y][x] != expected[y][x])
fprintf(stderr, "Check (%d, %d), got %d expected %d\n",
x, y, neighbours[y][x], expected[y][x]);
TEST(neighbours[y][x] == expected[y][x]);
}
}
cursor = cursor->around[ED_L];
TEST(cursor);
TEST(cursor->around[ED_B]);
TEST(!cursor->around[ED_L]);
TEST(!cursor->around[ED_BL]);
TEST(!cursor->around[ED_TL]);
TEST(!cursor->around[ED_T]);
TEST(!cursor->around[ED_TR]);
TEST(cursor->around[ED_R]);
TEST(cursor->around[ED_BR]);
{
chunkCollectNeighbours(cursor, neighbours);
int expected[CHUNK_SIZE][CHUNK_SIZE] = {
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 1, 2},
{0, 0, 0, 0, 0, 0, 2, 3}
};
for (int x = 0; x < CHUNK_SIZE; ++x)
for (int y = 0; y < CHUNK_SIZE; ++y) {
if (neighbours[y][x] != expected[y][x])
fprintf(stderr, "Check (%d, %d), got %d expected %d\n",
x, y, neighbours[y][x], expected[y][x]);
TEST(neighbours[y][x] == expected[y][x]);
}
}
cursor = cursor->around[ED_B];
TEST(cursor);
TEST(!cursor->around[ED_B]);
TEST(!cursor->around[ED_L]);
TEST(!cursor->around[ED_BL]);
TEST(!cursor->around[ED_TL]);
TEST(cursor->around[ED_T]);
TEST(cursor->around[ED_TR]);
TEST(cursor->around[ED_R]);
TEST(!cursor->around[ED_BR]);
{
chunkCollectNeighbours(cursor, neighbours);
int expected[CHUNK_SIZE][CHUNK_SIZE] = {
{0, 0, 0, 0, 0, 0, 2, 3},
{0, 0, 0, 0, 0, 0, 1, 2},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0}
};
for (int x = 0; x < CHUNK_SIZE; ++x)
for (int y = 0; y < CHUNK_SIZE; ++y) {
if (neighbours[y][x] != expected[y][x])
fprintf(stderr, "Check (%d, %d), got %d expected %d\n",
x, y, neighbours[y][x], expected[y][x]);
TEST(neighbours[y][x] == expected[y][x]);
}
}
fprintf(stderr, "All auto tests passed!\n");
return true;
}
void usage()
{
fprintf(stderr, "USAGE:\n"
" ./life\n"
" ./life --player\n"
" ./life --player --no-stdin\n"
" ./life --tests\n"); // , ./life n, where n is the number of steps, defaults to 1.
}
typedef enum {
EM_Auto = 0,
EM_Stdout,
EM_Player
} OutputMode;
int main(int n, char *args[n])
{
// Parse command line input
int steps = -1;
char *ruleset = "B3/S23";
OutputMode mode_output = EM_Auto;
bool mode_from_stdin = true;
bool mode_enable_tests = false;
for (int i = 1; i < n; ++i) {
char *arg = args[i];
if (strlen(arg) >= 2 && strncmp("--", arg, 2) == 0) {
arg += 2;
if (strcmp(arg, "tests") == 0) {
mode_enable_tests = true;
} else if (strcmp(arg, "player") == 0) {
mode_output = EM_Player;
} else if (strcmp(arg, "stdout") == 0) {
mode_output = EM_Stdout;
} else if (strcmp(arg, "no-stdin") == 0) {
mode_from_stdin = false;
} else if (strcmp(arg, "stdin") == 0) {
mode_from_stdin = true;
} else {
fprintf(stderr, "Invalid switch: %s\n", arg);
usage();
return 1;
}
} else {
fprintf(stderr, "Mode: %s\n", arg);
char *ptr = NULL;
long a = strtol(arg, &ptr, 10);
if (ptr == arg + strlen(arg) && a >= 0) {
fprintf(stderr, "Set steps from command line: %ld.\n", a);
steps = a;
} else if (toupper(arg[0]) == 'B') {
fprintf(stderr, "Set ruleset from command line: '%s'.\n", arg);
ruleset = arg;
} else {
fprintf(stderr, "Not a valid number or ruleset: '%s'.\n", arg);
usage();
return 1;
}
}
}
srand(time(NULL));
// Do tests?
if (mode_enable_tests) {
if (!run_tests())
return 1;
}
if (mode_output != EM_Auto || !mode_enable_tests) {
Canvas *world = createCanvas();
if (!canvasParseRuleset(world, ruleset))
return 1;
// Read from stdin
int w = 0;
int h = 0;
if (mode_from_stdin) {
int ret = readFromStdin(world, &w, &h);
if (ret > 0)
return ret;
}
if (mode_output == EM_Player) {
if (steps < 0)
steps = 0;
for (int i = 0; i < steps; ++i) {
canvasStep(world);
canvasGrow(world);
}
return runPlayer(world);
} else {
if (steps < 0)
steps = 1;
for (int i = 0; i < steps; ++i) {
canvasStep(world);
canvasGrow(world);
}
chunkPrint(world->center, w, h);
return 0;
}
}
return 0;
}