You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
6.2 KiB
243 lines
6.2 KiB
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <time.h>
|
|
|
|
struct size2d {
|
|
unsigned w;
|
|
unsigned h;
|
|
};
|
|
|
|
struct buffer {
|
|
char *data;
|
|
size_t len;
|
|
size_t alloc;
|
|
};
|
|
|
|
void *xrealloc(void *ptr, size_t size)
|
|
{
|
|
void *res = realloc(ptr, size);
|
|
if (!res) {
|
|
perror("realloc");
|
|
exit(1);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
struct buffer buffer_alloc()
|
|
{
|
|
struct buffer buffer;
|
|
buffer.len = 0;
|
|
buffer.alloc = 0x10;
|
|
buffer.data = xrealloc(NULL, buffer.alloc);
|
|
return buffer;
|
|
}
|
|
|
|
void buffer_append(struct buffer *buffer, char c)
|
|
{
|
|
if (buffer->alloc <= buffer->len) {
|
|
buffer->alloc *= 2;
|
|
buffer->data = xrealloc(buffer->data, buffer->alloc);
|
|
}
|
|
buffer->data[buffer->len++] = c;
|
|
}
|
|
|
|
char *parse_acres(const char *fname, struct size2d *size)
|
|
{
|
|
FILE *f = fopen(fname, "r");
|
|
if (!f) {
|
|
perror(fname);
|
|
return NULL;
|
|
}
|
|
|
|
struct buffer buffer = buffer_alloc();
|
|
size->w = 0;
|
|
size->h = 0;
|
|
|
|
unsigned width = 0;
|
|
int c;
|
|
|
|
flockfile(f);
|
|
while ((c = getc_unlocked(f)) != EOF) {
|
|
if (c == '\n') {
|
|
if (!size->w) {
|
|
size->w = width;
|
|
} else {
|
|
if (size->w != width) {
|
|
fprintf(stderr, "ERROR: Not each line has the same width\n");
|
|
goto error;
|
|
}
|
|
}
|
|
width = 0;
|
|
size->h++;
|
|
continue;
|
|
}
|
|
|
|
if (c != '.' && c != '|' && c != '#') {
|
|
fprintf(stderr, "ERROR: Unknown character: '%c'\n", c);
|
|
goto error;
|
|
}
|
|
|
|
buffer_append(&buffer, c);
|
|
width++;
|
|
}
|
|
funlockfile(f);
|
|
fclose(f);
|
|
|
|
return realloc(buffer.data, buffer.len);
|
|
|
|
error:
|
|
funlockfile(f);
|
|
fclose(f);
|
|
free(buffer.data);
|
|
return NULL;
|
|
}
|
|
|
|
void advance_frame(char *acres, char *next, const struct size2d size)
|
|
{
|
|
for (unsigned y = 0; y < size.h; y++) {
|
|
for (unsigned x = 0; x < size.w; x++) {
|
|
char *a = next + y * size.w + x;
|
|
|
|
char surrounding[8] = {'\0'};
|
|
if (y > 0) surrounding[0] = acres[(y - 1) * size.w + x];
|
|
if (x > 0) surrounding[1] = acres[y * size.w + (x - 1)];
|
|
if (y + 1 < size.h) surrounding[2] = acres[(y + 1) * size.w + x];
|
|
if (x + 1 < size.w) surrounding[3] = acres[y * size.w + (x + 1)];
|
|
if (y > 0 && x + 1 < size.w)
|
|
surrounding[4] = acres[(y - 1) * size.w + (x + 1)];
|
|
if (y > 0 && x > 0)
|
|
surrounding[5] = acres[(y - 1) * size.w + (x - 1)];
|
|
if (y + 1 < size.h && x > 0)
|
|
surrounding[6] = acres[(y + 1) * size.w + (x - 1)];
|
|
if (y + 1 < size.h && x + 1 < size.w)
|
|
surrounding[7] = acres[(y + 1) * size.w + (x + 1)];
|
|
|
|
unsigned tally = 0;
|
|
unsigned tally2 = 0;
|
|
char new = 0;
|
|
|
|
switch (*a) {
|
|
case '.':
|
|
case '|':
|
|
new = (*a == '.' ? '|' : '#');
|
|
for (char *c = surrounding; c < surrounding + 8; c++) {
|
|
if (*c == new) tally++;
|
|
}
|
|
if (tally >= 3) *a = new;
|
|
break;
|
|
case '#':
|
|
for (char *c = surrounding; c < surrounding + 8; c++) {
|
|
if (*c == '#') tally++;
|
|
if (*c == '|') tally2++;
|
|
}
|
|
if (!(tally >= 1 && tally2 >= 1)) *a = '.';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int render_bitmap(const char *fname, const char *acres, const struct size2d size)
|
|
{
|
|
FILE *f = fopen(fname, "w");
|
|
if (!f) {
|
|
perror(fname);
|
|
return 1;
|
|
}
|
|
|
|
flockfile(f);
|
|
fprintf(f, "P6\n%u %u\n255\n", size.w, size.h);
|
|
|
|
for (unsigned y = 0; y < size.h; y++) {
|
|
for (unsigned x = 0; x < size.w; x++) {
|
|
char color[3] = {255};
|
|
|
|
switch (acres[y * size.w + x]) {
|
|
case '.':
|
|
// Yellow
|
|
color[0] = 205;
|
|
color[1] = 255;
|
|
color[2] = 110;
|
|
break;
|
|
case '|':
|
|
// Green
|
|
color[0] = 0;
|
|
color[1] = 255;
|
|
color[2] = 0;
|
|
break;
|
|
case '#':
|
|
// Brown
|
|
color[0] = 192;
|
|
color[1] = 125;
|
|
color[2] = 0;
|
|
break;
|
|
}
|
|
|
|
fwrite_unlocked(color, 3, 1, f);
|
|
}
|
|
}
|
|
|
|
funlockfile(f);
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct size2d size;
|
|
char *acres = parse_acres("input", &size);
|
|
if (!acres) return 1;
|
|
|
|
unsigned minutes = 10;
|
|
int render = 0;
|
|
if (argc > 1) minutes = strtol(argv[1], NULL, 0);
|
|
if (argc > 2 && strcmp(argv[2], "-render") == 0) render = 1;
|
|
|
|
char *next = xrealloc(NULL, size.w * size.h);
|
|
memcpy(next, acres, size.w * size.h);
|
|
|
|
char *fname = NULL;
|
|
clock_t start = clock();
|
|
for (unsigned i = 0; i <= minutes; i++) {
|
|
if (i != 0) {
|
|
advance_frame(acres, next, size);
|
|
memcpy(acres, next, size.w * size.h);
|
|
|
|
if (i % 10000 == 0) {
|
|
double framespers = 10000 / ((double)(clock() - start) / CLOCKS_PER_SEC);
|
|
unsigned eta = (double)(minutes - i) / framespers;
|
|
fprintf(stderr, "Progress: %.2f%% (speed: %.2f frames/s, ETA: %u:%u:%u)\n",
|
|
(double)i / minutes * 100, framespers,
|
|
eta / 60 / 60, (eta / 60) % 60, eta % 60);
|
|
start = clock();
|
|
}
|
|
}
|
|
|
|
if (render) {
|
|
if (asprintf(&fname, "out/frame%04u.ppm", i) == -1) {
|
|
perror("asprintf");
|
|
break;
|
|
}
|
|
if (render_bitmap(fname, acres, size)) break;
|
|
free(fname);
|
|
}
|
|
}
|
|
|
|
unsigned wood = 0;
|
|
unsigned lumber = 0;
|
|
for (unsigned y = 0; y < size.h; y++) {
|
|
for (unsigned x = 0; x < size.w; x++) {
|
|
char c = acres[y * size.w + x];
|
|
if (c == '|') wood++;
|
|
if (c == '#') lumber++;
|
|
}
|
|
}
|
|
|
|
printf("Resource value: %u\n", wood * lumber);
|
|
|
|
free(next);
|
|
free(acres);
|
|
return 0;
|
|
}
|
|
|