diff --git a/userspace/aoc2023/CMakeLists.txt b/userspace/aoc2023/CMakeLists.txt index c77f1e1b..cfadf996 100644 --- a/userspace/aoc2023/CMakeLists.txt +++ b/userspace/aoc2023/CMakeLists.txt @@ -18,6 +18,7 @@ set(AOC2023_PROJECTS day13 day14 day15 + day16 ) set(BANAN_AOC2023_BIN ${BANAN_BIN}/aoc2023) diff --git a/userspace/aoc2023/day16/CMakeLists.txt b/userspace/aoc2023/day16/CMakeLists.txt new file mode 100644 index 00000000..d19f078a --- /dev/null +++ b/userspace/aoc2023/day16/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.26) + +project(aoc2023_day16 CXX) + +set(SOURCES + main.cpp +) + +add_executable(aoc2023_day16 ${SOURCES}) +target_compile_options(aoc2023_day16 PUBLIC -O2 -g) +target_link_libraries(aoc2023_day16 PUBLIC libc ban) + +add_dependencies(aoc2023_day16 libc-install ban-install) + +add_custom_target(aoc2023_day16-install + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/aoc2023_day16 ${BANAN_AOC2023_BIN}/day16 + DEPENDS aoc2023_day16 + DEPENDS aoc2023_always +) + +add_dependencies(aoc2023 aoc2023_day16) +add_dependencies(aoc2023-install aoc2023_day16-install) diff --git a/userspace/aoc2023/day16/main.cpp b/userspace/aoc2023/day16/main.cpp new file mode 100644 index 00000000..a2a01ecc --- /dev/null +++ b/userspace/aoc2023/day16/main.cpp @@ -0,0 +1,232 @@ +#include +#include +#include + +#include +#include +#include + +using i8 = int8_t; +using i16 = int16_t; +using i32 = int32_t; +using i64 = int64_t; + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; +using u64 = uint64_t; + +enum class Tile { Empty, PositiveMirror, NegativeMirror, HorizontalSplitter, VerticalSplitter }; +enum class Direction { North, South, East, West }; + +using Grid = BAN::Vector>; + +struct Position +{ + i64 y; + i64 x; + Direction dir; + + bool operator==(const Position& other) const + { + return x == other.x && y == other.y && dir == other.dir; + } +}; + +struct PositionHash +{ + BAN::hash_t operator()(Position position) const + { + return BAN::hash()((position.y << 32) | position.x) ^ BAN::hash()(static_cast(position.dir)); + } +}; + +Grid parse_grid(FILE* fp) +{ + Grid grid; + + auto char_to_tile = + [](char c) + { + if (c == '.') + return Tile::Empty; + if (c == '/') + return Tile::PositiveMirror; + if (c == '\\') + return Tile::NegativeMirror; + if (c == '-') + return Tile::HorizontalSplitter; + if (c == '|') + return Tile::VerticalSplitter; + ASSERT_NOT_REACHED(); + }; + + char buffer[128]; + while (fgets(buffer, sizeof(buffer), fp)) + { + BAN::StringView line(buffer); + ASSERT(line.back() == '\n'); + line = line.substring(0, line.size() - 1); + if (line.empty()) + break; + + MUST(grid.emplace_back(line.size())); + for (size_t i = 0; i < line.size(); i++) + grid.back()[i] = char_to_tile(line[i]); + } + + return grid; +} + +BAN::Vector get_next_positions(Position position, const Grid& grid) +{ + auto tile = grid[position.y][position.x]; + + BAN::Vector next_positions; + + switch (tile) + { + case Tile::Empty: + switch (position.dir) + { + case Direction::North: MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); break; + case Direction::South: MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); break; + case Direction::West: MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); break; + case Direction::East: MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); break; + } + break; + case Tile::PositiveMirror: + switch (position.dir) + { + case Direction::North: MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); break; + case Direction::South: MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); break; + case Direction::West: MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); break; + case Direction::East: MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); break; + } + break; + case Tile::NegativeMirror: + switch (position.dir) + { + case Direction::North: MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); break; + case Direction::South: MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); break; + case Direction::West: MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); break; + case Direction::East: MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); break; + } + break; + case Tile::HorizontalSplitter: + switch (position.dir) + { + case Direction::North: + case Direction::South: + MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); + MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); + break; + case Direction::West: MUST(next_positions.emplace_back(position.y, position.x - 1, Direction::West)); break; + case Direction::East: MUST(next_positions.emplace_back(position.y, position.x + 1, Direction::East)); break; + } + break; + case Tile::VerticalSplitter: + switch (position.dir) + { + case Direction::North: MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); break; + case Direction::South: MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); break; + case Direction::West: + case Direction::East: + MUST(next_positions.emplace_back(position.y - 1, position.x, Direction::North)); + MUST(next_positions.emplace_back(position.y + 1, position.x, Direction::South)); + break; + } + break; + } + + for (size_t i = 0; i < next_positions.size();) + { + if (next_positions[i].y < 0 || next_positions[i].y >= (i64)grid.size()) + next_positions.remove(i); + else if (next_positions[i].x < 0 || next_positions[i].x >= (i64)grid.front().size()) + next_positions.remove(i); + else + i++; + } + + return next_positions; +} + +i64 count_energized_tiles(const Grid& grid, Position start) +{ + BAN::HashSet visited; + BAN::HashSet energized; + + BAN::Vector current_positions; + MUST(current_positions.push_back(start)); + MUST(visited.insert(current_positions.front())); + + while (!current_positions.empty()) + { + auto position = current_positions.back(); + current_positions.pop_back(); + + MUST(energized.insert({ position.y, position.x, Direction::North })); + + auto next_positions = get_next_positions(position, grid); + for (auto next : next_positions) + { + if (visited.contains(next)) + continue; + MUST(visited.insert(next)); + MUST(current_positions.push_back(next)); + } + } + + return energized.size(); +} + +i64 puzzle1(FILE* fp) +{ + auto grid = parse_grid(fp); + return count_energized_tiles(grid, { 0, 0, Direction::East }); +} + +i64 puzzle2(FILE* fp) +{ + auto grid = parse_grid(fp); + + i64 max_energized = 0; + + for (i64 y = 0; y < (i64)grid.size(); y++) + { + max_energized = BAN::Math::max(max_energized, count_energized_tiles(grid, { y, 0, Direction::East })); + max_energized = BAN::Math::max(max_energized, count_energized_tiles(grid, { y, (i64)grid.front().size() - 1, Direction::West })); + } + + for (i64 x = 0; x < (i64)grid.front().size(); x++) + { + max_energized = BAN::Math::max(max_energized, count_energized_tiles(grid, { 0, x, Direction::South })); + max_energized = BAN::Math::max(max_energized, count_energized_tiles(grid, { (i64)grid.size() - 1, x, Direction::North })); + } + + return max_energized; +} + +int main(int argc, char** argv) +{ + const char* file_path = "/usr/share/aoc2023/day16_input.txt"; + + if (argc >= 2) + file_path = argv[1]; + + FILE* fp = fopen(file_path, "r"); + if (fp == nullptr) + { + perror("fopen"); + return 1; + } + + printf("puzzle1: %" PRId64 "\n", puzzle1(fp)); + + fseek(fp, 0, SEEK_SET); + + printf("puzzle2: %" PRId64 "\n", puzzle2(fp)); + + fclose(fp); +}