forked from Bananymous/banan-os
aoc2024: Implement day15 solution
This commit is contained in:
parent
a89fd95864
commit
a7e06715ba
|
@ -13,6 +13,7 @@ set(AOC2024_PROJECTS
|
||||||
day12
|
day12
|
||||||
day13
|
day13
|
||||||
day14
|
day14
|
||||||
|
day15
|
||||||
full
|
full
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue