diff --git a/userspace/aoc2024/CMakeLists.txt b/userspace/aoc2024/CMakeLists.txt index c01b6cbe..5e608ad2 100644 --- a/userspace/aoc2024/CMakeLists.txt +++ b/userspace/aoc2024/CMakeLists.txt @@ -8,6 +8,7 @@ set(AOC2024_PROJECTS day7 day8 day9 + day10 full ) diff --git a/userspace/aoc2024/day10/CMakeLists.txt b/userspace/aoc2024/day10/CMakeLists.txt new file mode 100644 index 00000000..0d29ecab --- /dev/null +++ b/userspace/aoc2024/day10/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCES + main.cpp +) + +add_executable(aoc2024_day10 ${SOURCES}) +banan_include_headers(aoc2024_day10 ban) +banan_link_library(aoc2024_day10 libc) + +install(TARGETS aoc2024_day10 OPTIONAL) diff --git a/userspace/aoc2024/day10/main.cpp b/userspace/aoc2024/day10/main.cpp new file mode 100644 index 00000000..8e70d75c --- /dev/null +++ b/userspace/aoc2024/day10/main.cpp @@ -0,0 +1,171 @@ +#include +#include + +#include +#include + +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 Position +{ + u32 x, y; + + constexpr bool operator==(const Position& other) const + { + return x == other.x && y == other.y; + } +}; + +struct PositionHash +{ + constexpr BAN::hash_t operator()(Position state) const + { + return BAN::hash{}((u64)state.x << 32 | (u64)state.y); + } +}; + +struct Grid2D +{ + usize width { 0 }; + usize height { 0 }; + BAN::Vector data; + + inline char get(usize x, usize y) const + { + ASSERT(x < width && y < height); + return data[y * width + x]; + } +}; + +static Grid2D read_grid2d(FILE* fp) +{ + usize width { 0 }; + usize height { 0 }; + BAN::Vector data; + + char buffer[1024]; + while (fgets(buffer, sizeof(buffer), fp)) + { + const usize len = strlen(buffer); + if (len == 0 || buffer[0] == '\n') + break; + if (data.empty()) + width = len - 1; + height++; + + ASSERT(buffer[width] == '\n'); + + if (data.capacity() < height * width) + MUST(data.reserve(2 * height * width)); + + MUST(data.resize(height * width)); + memcpy(&data[(height - 1) * width], buffer, width); + } + + (void)data.shrink_to_fit(); + + return Grid2D { + .width = width, + .height = height, + .data = BAN::move(data), + }; +} + +i64 get_trailhead_score(const Grid2D& map, u32 x, u32 y, char curr, BAN::HashSet& trail_ends, bool allow_duplicate) +{ + if (x >= map.width) + return 0; + if (y >= map.height) + return 0; + + if (map.get(x, y) != curr) + return 0; + if (curr == '9') + { + const auto pos = Position { .x = x, .y = y }; + if (trail_ends.contains(pos)) + return allow_duplicate; + MUST(trail_ends.insert(Position { .x = x, .y = y })); + return 1; + } + + i64 result = 0; + result += get_trailhead_score(map, x - 1, y, curr + 1, trail_ends, allow_duplicate); + result += get_trailhead_score(map, x, y - 1, curr + 1, trail_ends, allow_duplicate); + result += get_trailhead_score(map, x + 1, y, curr + 1, trail_ends, allow_duplicate); + result += get_trailhead_score(map, x, y + 1, curr + 1, trail_ends, allow_duplicate); + return result; +} + +i64 part1(FILE* fp) +{ + auto map = read_grid2d(fp); + + i64 result = 0; + + for (u32 y = 0; y < map.height; y++) + { + for (u32 x = 0; x < map.width; x++) + { + if (map.get(x, y) != '0') + continue; + BAN::HashSet trail_ends; + result += get_trailhead_score(map, x, y, '0', trail_ends, false); + } + } + + return result; +} + +i64 part2(FILE* fp) +{ + auto map = read_grid2d(fp); + + i64 result = 0; + + for (u32 y = 0; y < map.height; y++) + { + for (u32 x = 0; x < map.width; x++) + { + if (map.get(x, y) != '0') + continue; + BAN::HashSet trail_ends; + result += get_trailhead_score(map, x, y, '0', trail_ends, true); + } + } + + return result; +} + +int main(int argc, char** argv) +{ + const char* file_path = "/usr/share/aoc2024/day10_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); +}