222 lines
6.3 KiB
C
222 lines
6.3 KiB
C
#include "aoc.h"
|
|
|
|
static bool
|
|
can_push(Grid *grid, i32 x, i32 y, i32 dx, i32 dy) {
|
|
i32 index = y * grid->width + x;
|
|
i32 next_index = (y + dy) * grid->width + (x + dx);
|
|
u8 cell = grid->grid[index];
|
|
u8 next_cell = grid->grid[index];
|
|
switch (next_cell) {
|
|
case '.': return true;
|
|
case '#': return false;
|
|
case '@':
|
|
case 'O': return can_push(grid, x + dx, y + dy, dx, dy);
|
|
case '[':
|
|
if (dy == 0) return can_push(grid, x + dx, y, dx, dy);
|
|
else return can_push(grid, x, y + dy, dx, dy) && can_push(grid, x + 1, y + dy, dx, dy);
|
|
case ']':
|
|
if (dy == 0) return can_push(grid, x + dx, y, dx, dy);
|
|
else return can_push(grid, x, y + dy, dx, dy) && can_push(grid, x - 1, y + dy, dx, dy);
|
|
default: NOT_REACHABLE();
|
|
}
|
|
}
|
|
|
|
static bool
|
|
push_block(Grid *grid, i32 x, i32 y, i32 dx, i32 dy) {
|
|
if (!can_push(grid, x, y, dx, dy)) return false;
|
|
|
|
u8 cell = grid_at(grid, x, y);
|
|
if (cell == '.') {
|
|
return true;
|
|
}
|
|
else if (cell == '#') {
|
|
return false;
|
|
}
|
|
else if (cell == 'O' || cell == '@') {
|
|
i32 nx = x + dx;
|
|
i32 ny = y + dy;
|
|
if (push_block(grid, nx, ny, dx, dy)) {
|
|
ASSERT(can_push(grid, x, y, dx, dy));
|
|
grid->grid[ny * grid->width + nx] = grid->grid[y * grid->width + x];
|
|
grid->grid[y * grid->width + x] = '.';
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
else if (cell == '[' || cell == ']') {
|
|
i32 nx = x + dx;
|
|
i32 ny = y + dy;
|
|
if (dy == 0) {
|
|
if (push_block(grid, nx, ny, dx, dy)) {
|
|
ASSERT(can_push(grid, x, y, dx, dy));
|
|
grid->grid[ny * grid->width + nx] = grid->grid[y * grid->width + x];
|
|
grid->grid[y * grid->width + x] = '.';
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(dy != 0);
|
|
i32 partner_x = (cell == '[') ? nx + 1 : nx - 1;
|
|
if (push_block(grid, nx, ny, dx, dy)
|
|
&& push_block(grid, partner_x, ny, dx, dy))
|
|
{
|
|
ASSERT(can_push(grid, x, y, dx, dy));
|
|
grid->grid[ny * grid->width + nx] = grid->grid[y * grid->width + x];
|
|
grid->grid[ny * grid->width + partner_x] = grid->grid[y * grid->width + partner_x];
|
|
grid->grid[y * grid->width + x] = '.';
|
|
grid->grid[y * grid->width + partner_x] = '.';
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else NOT_REACHABLE();
|
|
}
|
|
|
|
static void
|
|
print_grid(Grid *grid) {
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
putchar(grid_at(grid, x, y));
|
|
}
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
static Grid
|
|
widen(Arena *arena, Grid *grid) {
|
|
Grid result = {
|
|
.width = 2 * grid->width,
|
|
.height = grid->height,
|
|
.grid = ARENA_ALLOC_ARRAY(arena, u8, 2 * grid->width * grid->height)
|
|
};
|
|
u8 *ptr = result.grid;
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
u8 cell = grid_at(grid, x, y);
|
|
if (cell == '.') {
|
|
*ptr++ = '.';
|
|
*ptr++ = '.';
|
|
}
|
|
else if (cell == '#') {
|
|
*ptr++ = '#';
|
|
*ptr++ = '#';
|
|
}
|
|
else if (cell == 'O') {
|
|
*ptr++ = '[';
|
|
*ptr++ = ']';
|
|
}
|
|
else if (cell == '@') {
|
|
*ptr++ = '@';
|
|
*ptr++ = '.';
|
|
}
|
|
else {
|
|
NOT_REACHABLE();
|
|
}
|
|
}
|
|
}
|
|
ASSERT(ptr == result.grid + result.width * result.height);
|
|
return result;
|
|
}
|
|
|
|
static i32
|
|
compute_total(Grid *grid) {
|
|
i32 result = 0;
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
u8 cell = grid_at(grid, x, y);
|
|
if (cell == 'O' || cell == '[') {
|
|
result += 100 * y + x;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
find_robot(Grid *grid, i32 *x, i32 *y) {
|
|
i32 robot_x = -1, robot_y = -1;
|
|
for (i32 y = 0; y < grid->height; y++) {
|
|
for (i32 x = 0; x < grid->width; x++) {
|
|
if (grid_at(grid, x, y) == '@') {
|
|
robot_x = x;
|
|
robot_y = y;
|
|
}
|
|
}
|
|
}
|
|
ASSERT(robot_x != -1 && robot_y != -1);
|
|
*x = robot_x;
|
|
*y = robot_y;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
Arena *arena = make_arena(Megabytes(1));
|
|
str input = read_file(arena, argv[1]);
|
|
|
|
str grid_input, instructions_str;
|
|
str_split(input, STR("\n\n"), &grid_input, &instructions_str);
|
|
|
|
Grid original = parse_grid(grid_input, arena);
|
|
Grid grid = parse_grid(grid_input, arena);
|
|
|
|
i32 robot_x, robot_y;
|
|
find_robot(&grid, &robot_x, &robot_y);
|
|
|
|
u8 *instructions = ARENA_ALLOC_ARRAY(arena, u8, instructions_str.len);
|
|
isize instruction_count = 0;
|
|
for (isize i = 0; i < instructions_str.len; i++) {
|
|
u8 ch = instructions_str.data[i];
|
|
if (ch != '\n') {
|
|
instructions[instruction_count++] = ch;
|
|
}
|
|
}
|
|
|
|
// Part 1
|
|
for (isize i = 0; i < instruction_count; i++) {
|
|
u8 ch = instructions[i];
|
|
i32 dx = 0, dy = 0;
|
|
if (ch == '<') dx = -1;
|
|
else if (ch == '>') dx = 1;
|
|
else if (ch == '^') dy = -1;
|
|
else if (ch == 'v') dy = 1;
|
|
else NOT_REACHABLE();
|
|
|
|
if (push_block(&grid, robot_x, robot_y, dx, dy)) {
|
|
robot_x += dx;
|
|
robot_y += dy;
|
|
}
|
|
}
|
|
|
|
i32 part_1 = compute_total(&grid);
|
|
printf("%d\n", part_1);
|
|
|
|
// Part 2
|
|
Grid wide_grid = widen(arena, &original);
|
|
find_robot(&wide_grid, &robot_x, &robot_y);
|
|
|
|
for (isize i = 0; i < instruction_count; i++) {
|
|
u8 ch = instructions[i];
|
|
i32 dx = 0, dy = 0;
|
|
if (ch == '<') dx = -1;
|
|
else if (ch == '>') dx = 1;
|
|
else if (ch == '^') dy = -1;
|
|
else if (ch == 'v') dy = 1;
|
|
else NOT_REACHABLE();
|
|
|
|
if (push_block(&wide_grid, robot_x, robot_y, dx, dy)) {
|
|
robot_x += dx;
|
|
robot_y += dy;
|
|
}
|
|
|
|
}
|
|
i32 part_2 = compute_total(&wide_grid);
|
|
printf("%d\n", part_2);
|
|
}
|