aoc2024: Implement day15 solution

This commit is contained in:
Bananymous 2024-12-15 20:45:57 +02:00
parent a89fd95864
commit a7e06715ba
3 changed files with 336 additions and 0 deletions

View File

@ -13,6 +13,7 @@ set(AOC2024_PROJECTS
day12
day13
day14
day15
full
)

View File

@ -0,0 +1,9 @@
set(SOURCES
main.cpp
)
add_executable(aoc2024_day15 ${SOURCES})
banan_include_headers(aoc2024_day15 ban)
banan_link_library(aoc2024_day15 libc)
install(TARGETS aoc2024_day15 OPTIONAL)

View File

@ -0,0 +1,326 @@
#include <BAN/Vector.h>
#include <inttypes.h>
#include <stdio.h>
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using isize = ssize_t;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using usize = size_t;
struct Grid2D
{
usize width { 0 };
usize height { 0 };
BAN::Vector<char> data;
inline char get(usize x, usize y) const
{
ASSERT(x < width && y < height);
return data[y * width + x];
}
inline char& get(usize x, usize y)
{
ASSERT(x < width && y < height);
return data[y * width + x];
}
};
struct Position
{
i32 x, y;
};
enum class Dir
{
Up, Down,
Left, Right,
};
struct ParseInputResult
{
Grid2D grid;
Position robot;
BAN::Vector<Dir> moves;
};
static ParseInputResult parse_input(FILE* fp, bool wide)
{
Grid2D grid;
char buffer[1024] {};
while (fgets(buffer, sizeof(buffer), fp))
{
const usize len = strlen(buffer);
if (len == 0 || buffer[0] == '\n')
break;
if (grid.data.empty())
grid.width = len - 1;
grid.height++;
ASSERT(buffer[grid.width] == '\n');
if (grid.data.capacity() < grid.height * grid.width)
MUST(grid.data.reserve(2 * grid.height * grid.width));
MUST(grid.data.resize(grid.height * grid.width));
memcpy(&grid.data[(grid.height - 1) * grid.width], buffer, grid.width);
}
if (wide)
{
BAN::Vector<char> wide_data;
MUST(wide_data.resize(grid.data.size() * 2));
for (usize i = 0; i < grid.data.size(); i++)
{
char l = 0, r = 0;
switch (grid.data[i])
{
case '#': l = '#'; r = '#'; break;
case 'O': l = '['; r = ']'; break;
case '.': l = '.'; r = '.'; break;
case '@': l = '@'; r = '.'; break;
}
wide_data[i * 2 + 0] = l;
wide_data[i * 2 + 1] = r;
}
grid.data = BAN::move(wide_data);
grid.width *= 2;
}
constexpr auto char_to_dir =
[](char ch) -> Dir
{
switch (ch)
{
case '^': return Dir::Up;
case 'v': return Dir::Down;
case '<': return Dir::Left;
case '>': return Dir::Right;
}
ASSERT_NOT_REACHED();
};
BAN::Vector<Dir> moves;
for (;;)
{
usize nread = fread(buffer, 1, sizeof(buffer), fp);
if (nread == 0)
break;
MUST(moves.reserve(moves.size() + nread));
for (usize i = 0; i < nread; i++)
if (buffer[i] != '\n')
MUST(moves.push_back(char_to_dir(buffer[i])));
}
Position robot;
for (u32 y = 0; y < grid.height; y++)
{
for (u32 x = 0; x < grid.width; x++)
{
if (grid.data[y * grid.width + x] != '@')
continue;
grid.data[y * grid.width + x] = '.';
robot = { (i32)x, (i32)y };
}
}
return ParseInputResult {
.grid = BAN::move(grid),
.robot = robot,
.moves = BAN::move(moves),
};
}
i64 part1(FILE* fp)
{
auto [grid, robot, moves] = parse_input(fp, false);
for (usize i = 0; i < moves.size(); i++)
{
#if 0
printf("\e[H");
for (usize y = 0; y < grid.height; y++) {
for (usize x = 0; x < grid.width; x++)
printf("%c ", grid.get(x, y));
printf("\n");
}
printf("\e[%u;%uH\e[31m@\e[m", robot.y + 1, robot.x * 2 + 1);
printf("\e[%zuH%zu/%zu\n", grid.height + 1, i, moves.size());
getchar();
#endif
i32 vx = 0, vy = 0;
switch (moves[i])
{
case Dir::Up: vx = 0; vy = -1; break;
case Dir::Down: vx = 0; vy = 1; break;
case Dir::Left: vx = -1; vy = 0; break;
case Dir::Right: vx = 1; vy = 0; break;
}
Position empty { -1, -1 };
for (usize j = 1;; j++)
{
const auto pos = Position {
(i32)(robot.x + j * vx),
(i32)(robot.y + j * vy),
};
const char ch = grid.get(pos.x, pos.y);
if (ch == '#')
break;
if (ch == 'O')
continue;
ASSERT(ch == '.');
empty = pos;
break;
}
if (empty.x == -1 || empty.y == -1)
continue;
while (empty.x != robot.x || empty.y != robot.y)
{
const auto pos = Position {
(i32)(empty.x - vx),
(i32)(empty.y - vy),
};
grid.get(empty.x, empty.y) = grid.get(pos.x, pos.y);
empty = pos;
}
grid.get(robot.x, robot.y) = '.';
robot.x += vx;
robot.y += vy;
}
i64 result = 0;
for (usize y = 0; y < grid.height; y++)
for (usize x = 0; x < grid.width; x++)
if (grid.get(x, y) == 'O')
result += 100 * y + x;
return result;
}
static bool can_move(const Grid2D& grid, Position pos, Position dir)
{
const char ch = grid.get(pos.x, pos.y);
if (ch == '.')
return true;
if (ch == '#')
return false;
if (dir.x)
return can_move(grid, { pos.x + dir.x, pos.y }, dir);
ASSERT(ch == '[' || ch == ']');
const i32 dir_x = (ch == '[') ? 1 : -1;
return can_move(grid, { pos.x, pos.y + dir.y }, dir)
&& can_move(grid, { pos.x + dir_x, pos.y + dir.y }, dir);
}
static void do_move(Grid2D& grid, Position pos, Position dir)
{
const char ch = grid.get(pos.x, pos.y);
ASSERT(ch != '#');
if (ch == '.')
;
else if (dir.x)
do_move(grid, { pos.x + dir.x, pos.y }, dir);
else
{
ASSERT(ch == '[' || ch == ']');
const i32 dir_x = (ch == '[') ? 1 : -1;
do_move(grid, { pos.x, pos.y + dir.y }, dir);
do_move(grid, { pos.x + dir_x, pos.y + dir.y }, dir);
}
grid.get(pos.x, pos.y) = grid.get(pos.x - dir.x, pos.y - dir.y);
grid.get(pos.x - dir.x, pos.y - dir.y) = '.';
//grid.get(pos.x + dir.x, pos.y + dir.y) = grid.get(pos.x, pos.y);
//grid.get(pos.x, pos.y) = '.';
}
i64 part2(FILE* fp)
{
auto [grid, robot, moves] = parse_input(fp, true);
for (usize i = 0; i < moves.size(); i++)
{
#if 0
printf("\e[H");
for (usize y = 0; y < grid.height; y++) {
for (usize x = 0; x < grid.width; x++)
printf("%c", grid.get(x, y));
printf("\n");
}
printf("\e[%u;%uH\e[31m@\e[m", robot.y + 1, robot.x + 1);
printf("\e[%zuH%zu/%zu\n", grid.height + 1, i, moves.size());
getchar();
#endif
Position dir {};
switch (moves[i])
{
case Dir::Up: dir = { 0, -1 }; break;
case Dir::Down: dir = { 0, 1 }; break;
case Dir::Left: dir = { -1, 0 }; break;
case Dir::Right: dir = { 1, 0 }; break;
}
const auto next = Position {
robot.x + dir.x,
robot.y + dir.y,
};
if (!can_move(grid, next, dir))
continue;
do_move(grid, next, dir);
robot = next;
}
i64 result = 0;
for (usize y = 0; y < grid.height; y++)
for (usize x = 0; x < grid.width; x++)
if (grid.get(x, y) == '[')
result += 100 * y + x;
return result;
}
int main(int argc, char** argv)
{
const char* file_path = "/usr/share/aoc2024/day15_input.txt";
if (argc >= 2)
file_path = argv[1];
FILE* fp = fopen(file_path, "r");
if (fp == nullptr)
{
perror("fopen");
return 1;
}
printf("part1: %" PRId64 "\n", part1(fp));
fseek(fp, 0, SEEK_SET);
printf("part2: %" PRId64 "\n", part2(fp));
fclose(fp);
}