Files
adventofcode2023/day23/c/day23.c
2023-12-24 01:51:44 +01:00

220 lines
6.2 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define LINE_MAX_LENGTH 256
#define MAX_QUEUE 256*256
#define cmp(a,b) ((a)[0] == (b)[0] && (a)[1] == (b)[1])
#define abs(a) ((a) < 0 ? -(a) : (a))
typedef enum tile_type {
PATH,
FOREST,
SLOPE_UP,
SLOPE_DOWN,
SLOPE_LEFT,
SLOPE_RIGHT,
} tile_type_t;
int check_direction(int curr[2], int dir[2], uint8_t **visited, tile_type_t map[LINE_MAX_LENGTH][LINE_MAX_LENGTH]);
int highest_cost(int curr[2], tile_type_t map[LINE_MAX_LENGTH][LINE_MAX_LENGTH], int x, int y, uint8_t **visited, int cost, int finish[2], int ignore_slopes, int walkable);
int curr_max = 0;
int directions[4][2] = {
{0, -1},
{0, 1},
{-1, 0},
{1, 0}
};
int main() {
char *p, *buf, c;
buf = (char *)malloc(LINE_MAX_LENGTH);
memset(buf, 0, LINE_MAX_LENGTH);
p = buf;
tile_type_t map[LINE_MAX_LENGTH][LINE_MAX_LENGTH];
int x = 0, y = 0;
int walkable = 0;
while ((c = getchar()) != EOF) {
*p++ = c;
if (c == '\n') {
p = buf;
x = 0;
while (*p != '\n') {
switch (*p) {
case '.':
map[x][y] = PATH;
walkable++;
break;
case '#':
map[x][y] = FOREST;
break;
case '^':
map[x][y] = SLOPE_UP;
walkable++;
break;
case 'v':
map[x][y] = SLOPE_DOWN;
walkable++;
break;
case '<':
map[x][y] = SLOPE_LEFT;
walkable++;
break;
case '>':
map[x][y] = SLOPE_RIGHT;
walkable++;
break;
}
x++;
p++;
}
y++;
memset(buf, 0, LINE_MAX_LENGTH);
p = buf;
}
}
int start[2], finish[2];
start[1] = 0;
finish[1] = y - 1;
// Find start and finish
for (int i = 0; i < x; i++) {
if (map[i][0] == PATH) {
start[0] = i;
}
if (map[i][y - 1] == PATH) {
finish[0] = i;
}
}
uint8_t **visited = (uint8_t**)malloc(x * sizeof(uint8_t*));
for (uint8_t i = 0; i < x; i++) {
visited[i] = (uint8_t*)malloc(y * sizeof(uint8_t));
memset(visited[i], 0, y * sizeof(uint8_t));
}
printf("Part 1: %i\n", highest_cost(start, map, x, y, visited, 0, finish, 0, walkable));
for (uint8_t i = 0; i < x; i++) {
memset(visited[i], 0, y * sizeof(uint8_t));
}
printf("Part 2: %i\n", highest_cost(start, map, x, y, visited, 0, finish, 1, walkable));
free(buf);
for (int i = 0; i < y; i++) {
free(visited[i]);
}
free(visited);
}
int highest_cost(int curr[2], tile_type_t map[LINE_MAX_LENGTH][LINE_MAX_LENGTH], int x, int y, uint8_t **visited, int cost, int finish[2], int ignore_slopes, int walkable) {
if (cmp(curr, finish)) {
return cost;
}
// We will never reach the end
if ((walkable - cost) < (abs(finish[0] - curr[0]) + abs(finish[1] - curr[1])) || visited[finish[0]][finish[1]-1]) {
return -1;
}
visited[curr[0]][curr[1]] = 1;
int *options[4], options_num = 0, *dir, max = 0;
if (map[curr[0]][curr[1]] == SLOPE_UP && !ignore_slopes) {
dir = directions[0];
if (check_direction(curr, dir, visited, map)) {
options[0] = dir;
options_num = 1;
}
} else if (map[curr[0]][curr[1]] == SLOPE_DOWN && !ignore_slopes) {
dir = directions[1];
if (check_direction(curr, dir, visited, map)) {
options[0] = dir;
options_num = 1;
}
} else if (map[curr[0]][curr[1]] == SLOPE_LEFT && !ignore_slopes) {
dir = directions[2];
if (check_direction(curr, dir, visited, map)) {
options[0] = dir;
options_num = 1;
}
} else if (map[curr[0]][curr[1]] == SLOPE_RIGHT && !ignore_slopes) {
dir = directions[3];
if (check_direction(curr, dir, visited, map)) {
options[0] = dir;
options_num = 1;
}
} else {
for (int i = 0; i < 4; i++) {
int *dir = directions[i];
if (check_direction(curr, dir, visited, map)) {
options[options_num] = dir;
options_num++;
}
}
}
if (options_num == 0) {
return -1;
}
uint8_t ***visited_copies = (uint8_t***)malloc(options_num * sizeof(uint8_t**));
for (uint8_t i = 0; i < options_num - 1; i++) {
visited_copies[i] = (uint8_t**)malloc(x * sizeof(uint8_t*));
for (uint8_t j = 0; j < y; j++) {
visited_copies[i][j] = (uint8_t*)malloc(y * sizeof(uint8_t));
memcpy(visited_copies[i][j], visited[j], y * sizeof(uint8_t));
}
}
for (int i = 0; i < options_num; i++) {
int next[2];
next[0] = curr[0] + options[i][0];
next[1] = curr[1] + options[i][1];
int c;
if (i != options_num - 1) {
c = highest_cost(next, map, x, y, visited_copies[i], cost + 1, finish, ignore_slopes, walkable);
} else {
c = highest_cost(next, map, x, y, visited, cost + 1, finish, ignore_slopes, walkable);
}
if (c > max) {
max = c;
}
}
for (int i = 0; i < options_num - 1; i++) {
for (int j = 0; j < y; j++) {
free(visited_copies[i][j]);
}
free(visited_copies[i]);
}
free(visited_copies);
if (max > curr_max) {
curr_max = max;
printf("Current best: %i\n", curr_max);
}
return max;
}
int check_direction(int curr[2], int dir[2], uint8_t **visited, tile_type_t map[LINE_MAX_LENGTH][LINE_MAX_LENGTH]) {
int next_x = curr[0] + dir[0];
int next_y = curr[1] + dir[1];
if (next_y > 0 && !visited[next_x][next_y] && map[next_x][next_y] != FOREST) {
return 1;
}
return 0;
}