2024-12-07 00:17:31 -06:00

249 lines
7.1 KiB
C

#include <stdbool.h>
#include <stddef.h>
#include "printf.h"
#include "parse.h"
#include "solution.h"
static struct position find_guard(const char * input, int length,
int stride)
{
int x = 0;
int y = 0;
for (int i = 0; i < length; i++) {
if (input[i] == '^') {
break;
}
x += 1;
if (x >= stride) {
x = 0;
y += 1;
}
}
return (struct position){x, y};
}
static bool position_inside_map(int width, int height,
int x, int y)
{
return
x >= 0 &&
y >= 0 &&
x < width &&
y < height ;
}
static bool position_contains_obstacle(const char * input,
int stride,
int width, int height,
int x, int y)
{
if (!position_inside_map(width, height, x, y))
return false;
char c = input[y * stride + x];
return c == '#';
}
static enum facing turn(enum facing facing)
{
switch (facing) {
case UP: return RIGHT;
case RIGHT: return DOWN;
case DOWN: return LEFT;
case LEFT: return UP;
}
__builtin_unreachable();
}
static struct position move(enum facing facing, int x, int y)
{
switch (facing) {
case UP: return (struct position){x , y - 1};
case DOWN: return (struct position){x , y + 1};
case LEFT: return (struct position){x - 1, y };
case RIGHT: return (struct position){x + 1, y };
}
__builtin_unreachable();
}
static void guard_move(struct guard * guard, bool collision)
{
if (collision) {
guard->facing = turn(guard->facing);
} else {
guard->position = move(guard->facing, guard->position.x, guard->position.y);
}
}
static bool guard_collision(const char * input,
int stride,
int width, int height,
const struct guard * guard)
{
struct position position = move(guard->facing, guard->position.x, guard->position.y);
return position_contains_obstacle(input, stride, width, height, position.x, position.y);
}
static bool guard_collision_obstacle(const char * input,
int stride,
int width, int height,
const struct guard * guard,
struct position obstacle)
{
struct position position = move(guard->facing, guard->position.x, guard->position.y);
return (position.x == obstacle.x && position.y == obstacle.y)
|| position_contains_obstacle(input, stride, width, height, position.x, position.y);
}
static int sum(unsigned char * l, int length)
{
int acc = 0;
for (int i = 0; i < length; i++) {
acc += (int)(l[i] > 0);
}
return acc;
}
struct solution_state day6_state = {0};
void state_init(int part,
const char * input,
int stride,
int width,
int height,
unsigned char * visited)
{
day6_state.part = part;
day6_state.input = input;
day6_state.stride = stride;
day6_state.width = width;
day6_state.height = height;
day6_state.visited = visited;
}
int day6_part1(const char * input, int length)
{
int stride = parse_stride(input, length);
int height = parse_height(input, length);
int width = stride - 1;
unsigned char visited[height * width];
for (int i = 0; i < width * height; i++) visited[i] = 0;
struct guard guard;
guard.facing = UP;
guard.position = find_guard(input, length, stride);
state_init(1, input, stride, width, height, visited);
while (true) {
bool guard_inside_map = position_inside_map(width, height,
guard.position.x, guard.position.y);
if (!guard_inside_map)
break;
visited[guard.position.y * width + guard.position.x] |= (1 << (int)guard.facing);
bool collision = guard_collision(input,
stride,
width, height,
&guard);
guard_move(&guard, collision);
}
return sum(visited, height * width);
}
static bool speculative_obstacle_causes_loop(const char * input,
int stride,
int width, int height,
unsigned char * visited,
struct position origin,
struct position obstacle)
{
// reset visited
for (int i = 0; i < width * height; i++) visited[i] = 0;
struct guard guard;
guard.facing = UP;
guard.position = origin;
while (true) {
bool guard_inside_map = position_inside_map(width, height,
guard.position.x, guard.position.y);
if (!guard_inside_map)
break;
// is this a loop?
if (visited[guard.position.y * width + guard.position.x] & (1 << (int)guard.facing))
return true;
visited[guard.position.y * width + guard.position.x] |= (1 << (int)guard.facing);
bool collision = guard_collision_obstacle(input,
stride,
width, height,
&guard,
obstacle);
guard_move(&guard, collision);
}
return false;
}
int day6_part2(const char * input, int length)
{
int stride = parse_stride(input, length);
int height = parse_height(input, length);
int width = stride - 1;
unsigned char visited[height * width];
for (int i = 0; i < width * height; i++) visited[i] = 0;
struct position origin = find_guard(input, length, stride);
struct guard guard;
guard.facing = UP;
guard.position = origin;
state_init(2, input, stride, width, height, visited);
unsigned char obstacles[height * width];
for (int i = 0; i < width * height; i++) obstacles[i] = 0;
unsigned char loops[height * width];
for (int i = 0; i < width * height; i++) loops[i] = 0;
while (true) {
bool guard_inside_map = position_inside_map(width, height,
guard.position.x, guard.position.y);
if (!guard_inside_map)
break;
bool collision = guard_collision(input,
stride,
width, height,
&guard);
if (!collision) {
struct position obstacle = move(guard.facing, guard.position.x, guard.position.y);
if (obstacles[obstacle.y * width + obstacle.x] == 0) {
obstacles[obstacle.y * width + obstacle.x] = 1;
bool loop = speculative_obstacle_causes_loop(input,
stride,
width, height,
visited,
origin,
obstacle);
loops[obstacle.y * width + obstacle.x] |= loop;
}
}
guard_move(&guard, collision);
}
return sum(loops, height * width);
}