Advent of Code 2018
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.
 
 
 

282 lines
7.5 KiB

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
struct size2d {
unsigned w;
unsigned h;
};
struct buffer {
void *data;
size_t len;
size_t alloc;
unsigned element;
};
void *xrealloc(void *ptr, size_t size)
{
void *res = realloc(ptr, size);
if (!res) {
perror("realloc");
exit(1);
}
return res;
}
struct buffer buffer_alloc(const unsigned element_size)
{
struct buffer buffer;
buffer.len = 0;
buffer.alloc = 0x10;
buffer.element = element_size;
buffer.data = xrealloc(NULL, buffer.alloc * buffer.element);
return buffer;
}
void buffer_append(struct buffer *buffer, void *data)
{
if (buffer->alloc <= buffer->len) {
buffer->alloc *= 2;
buffer->data = xrealloc(buffer->data, buffer->alloc * buffer->element);
}
memcpy((char *)buffer->data + buffer->len++ * buffer->element, data, buffer->element);
}
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(sizeof(char));
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;
}
uint32_t adler32(const unsigned char *data, const size_t len)
{
// Shamelessly copied from wikipedia...
uint32_t a = 1, b = 0;
for (size_t index = 0; index < len; index++) {
a = (a + data[index]) % 65521;
b = (b + a) % 65521;
}
return (b << 16) | a;
}
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);
struct buffer hashes = buffer_alloc(sizeof(uint32_t));
unsigned loop_found = 0;
unsigned loop_iterlen = 0;
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 (!loop_found) {
uint32_t hash = adler32((unsigned char *)acres, size.w * size.h);
for (unsigned i = 0; i < hashes.len; i++) {
uint32_t check = ((uint32_t *)hashes.data)[i];
if (check == hash) {
loop_found = i + 1;
break;
}
}
if (loop_found) {
loop_iterlen = (i - loop_found);
minutes = (minutes - i) % loop_iterlen + i;
} else {
buffer_append(&hashes, &hash);
}
}
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++;
}
}
if (loop_found) printf("Loop starting frame: %u (length %u)\n", loop_found, loop_iterlen);
printf("Resource value: %u\n", wood * lumber);
free(hashes.data);
free(next);
free(acres);
return 0;
}