Files
adventofcode2023/day18/c/day18.c
2023-12-22 13:34:50 +01:00

370 lines
12 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>
#define LINE_MAX_LENGTH 256
#define MAX_ENTRIES 2048
#define MAX_QUEUE_LEN 256
#define MAX_EDGES 1024
#define MAX_BREAKPOINTS 512
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
typedef struct entry {
char direction;
long long count;
char rgb_string[10];
} entry_t;
typedef struct zone {
long long upper_left_corner[2];
long long lower_right_corner[2];
uint8_t outside;
} zone_t;
int cmp(const void* a, const void* b);
int on_edge(long long edges[MAX_EDGES][2][2], int edges_num, long long side[2][2]);
unsigned long long lake_volume(entry_t entries[], int entries_num);
void decode_rgbs(entry_t entries[], int entries_num);
int main() {
char *p, *buf, c;
buf = (char *)malloc(LINE_MAX_LENGTH);
memset(buf, 0, LINE_MAX_LENGTH);
p = buf;
entry_t entries[MAX_ENTRIES];
memset(entries, 0, MAX_ENTRIES * sizeof(entry_t));
int entries_num = 0;
while ((c = getchar()) != EOF) {
*p++ = c;
if (c == '\n') {
sscanf(buf, "%c %lli %s", &entries[entries_num].direction, &entries[entries_num].count, entries[entries_num].rgb_string);
entries_num++;
memset(buf, 0, LINE_MAX_LENGTH);
p = buf;
}
}
unsigned long long part1 = lake_volume(entries, entries_num);
decode_rgbs(entries, entries_num);
unsigned long long part2 = lake_volume(entries, entries_num);
printf("%llu\n", part1);
printf("%llu\n", part2);
free(buf);
}
int cmp(const void* a, const void* b) {
return *(long long*)a - *(long long*)b;
}
int on_edge(long long edges[MAX_EDGES][2][2], int edges_num, long long side[2][2]) {
for (int i = 0; i < edges_num; i++) {
if (edges[i][0][0] == side[0][0] && edges[i][0][0] == edges[i][1][0] && side[0][0] == side[1][0] && edges[i][0][1] <= side[0][1] && edges[i][1][1] >= side[1][1]) {
return 1;
}
if (edges[i][0][1] == side[0][1] && edges[i][0][1] == edges[i][1][1] && side[0][1] == side[1][1] && edges[i][0][0] <= side[0][0] && edges[i][1][0] >= side[1][0]) {
return 1;
}
}
return 0;
}
unsigned long long lake_volume(entry_t entries[], int entries_num) {
long long x = 0, y = 0, dir[2];
long long edges[MAX_EDGES][2][2];
memset(edges, 0, MAX_EDGES * 2 * 2 * sizeof(long long));
int edges_num = 0;
for (int i = 0; i < entries_num; i++) {
switch (entries[i].direction) {
case 'R':
dir[0] = 1;
dir[1] = 0;
edges[edges_num][0][0] = x;
edges[edges_num][0][1] = y;
edges[edges_num][1][0] = x + entries[i].count * dir[0];
edges[edges_num][1][1] = y + entries[i].count * dir[1];
edges_num++;
break;
case 'L':
dir[0] = -1;
dir[1] = 0;
edges[edges_num][1][0] = x;
edges[edges_num][1][1] = y;
edges[edges_num][0][0] = x + entries[i].count * dir[0];
edges[edges_num][0][1] = y + entries[i].count * dir[1];
edges_num++;
break;
case 'U':
dir[0] = 0;
dir[1] = -1;
edges[edges_num][1][0] = x;
edges[edges_num][1][1] = y;
edges[edges_num][0][0] = x + entries[i].count * dir[0];
edges[edges_num][0][1] = y + entries[i].count * dir[1];
edges_num++;
break;
case 'D':
dir[0] = 0;
dir[1] = 1;
edges[edges_num][0][0] = x;
edges[edges_num][0][1] = y;
edges[edges_num][1][0] = x + entries[i].count * dir[0];
edges[edges_num][1][1] = y + entries[i].count * dir[1];
edges_num++;
break;
}
x += entries[i].count * dir[0];
y += entries[i].count * dir[1];
}
// Find smallest and largest x and y
long long min_x = LLONG_MAX, max_x = LLONG_MIN, min_y = LLONG_MAX, max_y = LLONG_MIN;
for (int i = 0; i < edges_num; i++) {
if (edges[i][0][0] < min_x) {
min_x = edges[i][0][0];
}
if (edges[i][1][0] > max_x) {
max_x = edges[i][1][0];
}
if (edges[i][0][1] < min_y) {
min_y = edges[i][0][1];
}
if (edges[i][1][1] > max_y) {
max_y = edges[i][1][1];
}
}
// X breakpoints
long long x_breakpoints[MAX_BREAKPOINTS], x_breakpoints_num = 0;
for (int i = 0; i < edges_num; i++) {
int found = 0;
for (int j = 0; j < x_breakpoints_num; j++) {
if (edges[i][0][0] == x_breakpoints[j]) {
found = 1;
break;
}
}
if (!found) {
x_breakpoints[x_breakpoints_num] = edges[i][0][0];
x_breakpoints_num++;
}
found = 0;
for (int j = 0; j < x_breakpoints_num; j++) {
if (edges[i][1][0] == x_breakpoints[j]) {
found = 1;
break;
}
}
if (!found) {
x_breakpoints[x_breakpoints_num] = edges[i][1][0];
x_breakpoints_num++;
}
}
qsort(x_breakpoints, x_breakpoints_num, sizeof(x_breakpoints[0]), &cmp);
// Y breakpoints
long long y_breakpoints[MAX_BREAKPOINTS], y_breakpoints_num = 0;
for (int i = 0; i < edges_num; i++) {
int found = 0;
for (int j = 0; j < y_breakpoints_num; j++) {
if (edges[i][0][1] == y_breakpoints[j]) {
found = 1;
break;
}
}
if (!found) {
y_breakpoints[y_breakpoints_num] = edges[i][0][1];
y_breakpoints_num++;
}
found = 0;
for (int j = 0; j < y_breakpoints_num; j++) {
if (edges[i][1][1] == y_breakpoints[j]) {
found = 1;
break;
}
}
if (!found) {
y_breakpoints[y_breakpoints_num] = edges[i][1][1];
y_breakpoints_num++;
}
}
qsort(y_breakpoints, y_breakpoints_num, sizeof(y_breakpoints[0]), &cmp);
zone_t **zones = (zone_t**)malloc((x_breakpoints_num - 1) * sizeof(zone_t*));
for (int i = 0; i < x_breakpoints_num - 1; i++) {
zones[i] = (zone_t*)malloc((y_breakpoints_num - 1) * sizeof(zone_t));
memset(zones[i], 0, (y_breakpoints_num - 1) * sizeof(zone_t));
}
zone_t *curr;
for (int i = 0; i < y_breakpoints_num - 1; i++) {
for (int j = 0; j < x_breakpoints_num - 1; j++) {
curr = &zones[j][i];
curr->upper_left_corner[0] = x_breakpoints[j];
curr->upper_left_corner[1] = y_breakpoints[i];
curr->lower_right_corner[0] = x_breakpoints[j + 1];
curr->lower_right_corner[1] = y_breakpoints[i + 1];
}
}
// Mark zones as outside
int found = 1;
while (found) {
found = 0;
for (int i = 0; i < y_breakpoints_num - 1; i++) {
for (int j = 0; j < x_breakpoints_num - 1; j++) {
if (zones[j][i].outside) {
continue;
}
// LEFT
long long side[2][2];
side[0][0] = zones[j][i].upper_left_corner[0];
side[0][1] = zones[j][i].upper_left_corner[1];
side[1][0] = zones[j][i].upper_left_corner[0];
side[1][1] = zones[j][i].lower_right_corner[1];
if (j == 0 && !on_edge(edges, edges_num, side)) {
zones[j][i].outside = 1;
found = 1;
}
if (j > 0 && zones[j-1][i].outside && !on_edge(edges, edges_num, side)) {
zones[j][i].outside = 1;
found = 1;
}
// RIGHT
side[0][0] = zones[j][i].lower_right_corner[0];
side[0][1] = zones[j][i].upper_left_corner[1];
side[1][0] = zones[j][i].lower_right_corner[0];
side[1][1] = zones[j][i].lower_right_corner[1];
if (j == x_breakpoints_num - 2 && !on_edge(edges, edges_num, side)) {
zones[j][i].outside = 1;
found = 1;
}
if (j < x_breakpoints_num - 2 && zones[j+1][i].outside && !on_edge(edges, edges_num, side)) {
zones[j][i].outside = 1;
found = 1;
}
// UP
side[0][0] = zones[j][i].upper_left_corner[0];
side[0][1] = zones[j][i].upper_left_corner[1];
side[1][0] = zones[j][i].lower_right_corner[0];
side[1][1] = zones[j][i].upper_left_corner[1];
if (i == 0 && !on_edge(edges, edges_num, side)) {
zones[j][i].outside = 1;
found = 1;
}
if (i > 0 && zones[j][i-1].outside && !on_edge(edges, edges_num, side)) {
zones[j][i].outside = 1;
found = 1;
}
// DOWN
side[0][0] = zones[j][i].upper_left_corner[0];
side[0][1] = zones[j][i].lower_right_corner[1];
side[1][0] = zones[j][i].lower_right_corner[0];
side[1][1] = zones[j][i].lower_right_corner[1];
if (i == y_breakpoints_num - 2 && !on_edge(edges, edges_num, side)) {
zones[j][i].outside = 1;
found = 1;
}
if (i < y_breakpoints_num - 2 && zones[j][i+1].outside && !on_edge(edges, edges_num, side)) {
zones[j][i].outside = 1;
found = 1;
}
}
}
}
unsigned long long sum = 0;
unsigned long long out = 0;
for (int i = 0; i < y_breakpoints_num - 1; i++) {
for (int j = 0; j < x_breakpoints_num - 1; j++) {
if (zones[j][i].outside) {
out++;
}
}
}
for (int i = 0; i < y_breakpoints_num - 1; i++) {
for (int j = 0; j < x_breakpoints_num - 1; j++) {
if (zones[j][i].outside) {
continue;
}
sum += (zones[j][i].lower_right_corner[0] - zones[j][i].upper_left_corner[0] - 1) * (zones[j][i].lower_right_corner[1] - zones[j][i].upper_left_corner[1] - 1);
// RIGHT
long long side[2][2];
side[0][0] = zones[j][i].lower_right_corner[0];
side[0][1] = zones[j][i].upper_left_corner[1];
side[1][0] = zones[j][i].lower_right_corner[0];
side[1][1] = zones[j][i].lower_right_corner[1];
if (!on_edge(edges, edges_num, side)) {
sum += side[1][1] - side[0][1] - 1;
}
// DOWN
side[0][1] = zones[j][i].lower_right_corner[1];
side[0][0] = zones[j][i].upper_left_corner[0];
side[1][0] = zones[j][i].lower_right_corner[0];
side[1][1] = zones[j][i].lower_right_corner[1];
if (!on_edge(edges, edges_num, side)) {
sum += side[1][0] - side[0][0] - 1;
}
// CORNER
side[0][1] = zones[j][i].lower_right_corner[1];
side[0][0] = zones[j][i].lower_right_corner[0];
side[1][0] = zones[j][i].lower_right_corner[0];
side[1][1] = zones[j][i].lower_right_corner[1];
if (!on_edge(edges, edges_num, side)) {
sum++;
}
}
}
for (int i = 0; i < edges_num; i++) {
sum += (edges[i][1][0] - edges[i][0][0]) + (edges[i][1][1] - edges[i][0][1]);
}
for (int i = 0; i < x_breakpoints_num - 1; i++) {
free(zones[i]);
}
free(zones);
return sum;
}
void decode_rgbs(entry_t entries[], int entries_num) {
for (int i = 0; i < entries_num; i++) {
char value[6], *p;
memset(value, 0, 6);
p = entries[i].rgb_string;
while(*p != '#') p++;
p++;
strncpy(value, p, 5);
sscanf(value, "%llx", &entries[i].count);
switch(entries[i].rgb_string[7]) {
case '0':
entries[i].direction = 'R';
break;
case '1':
entries[i].direction = 'D';
break;
case '2':
entries[i].direction = 'L';
break;
case '3':
entries[i].direction = 'U';
break;
}
}
}