From e6d36a9092e5ddc04b662068441a4cf74aed0698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20M=C3=A5rdbrink?= Date: Mon, 16 Dec 2024 17:40:38 +0100 Subject: [PATCH] Day 15 --- d15/main.cxx | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 327 insertions(+), 6 deletions(-) diff --git a/d15/main.cxx b/d15/main.cxx index 74bf743..06e37ba 100644 --- a/d15/main.cxx +++ b/d15/main.cxx @@ -1,15 +1,336 @@ +#include +#include +#include #include -#include +#include +#include +#include +#include +#include -using std::cout, std::endl; +using std::cout, std::endl, std::pair; -int main() { - std::ifstream file{"../d15/input.txt"}; +namespace views = std::ranges::views; + +constexpr const char content[] = { + #embed "input.txt" +}; + +enum class Direction : char { + UP = '^', + DOWN = 'v', + LEFT = '<', + RIGHT = '>', +}; + +enum class TileType : char { + ROBOT = '@', + WALL = '#', + BOX = 'O', + EMPTY = '.', +}; + +namespace std { + template<> + struct hash> { + std::size_t operator()(const std::pair& k) const { + return std::hash()(k.first) ^ std::hash()(k.second); + } + }; +} + +bool move_tile(std::pair pos, Direction dir, std::unordered_map, TileType>& map, TileType type) { + int h = Direction::LEFT == dir ? -1 : Direction::RIGHT == dir ? 1 : 0; + int v = Direction::UP == dir ? -1 : Direction::DOWN == dir ? 1 : 0; + + if(map[{pos.first + h, pos.second + v}] == TileType::EMPTY) { + map[{pos.first + h, pos.second + v}] = type; + map[{pos.first, pos.second}] = TileType::EMPTY; + return true; + } else if(map[{pos.first + h, pos.second + v}] == TileType::BOX) { + if(move_tile({pos.first + h, pos.second + v}, dir, map, TileType::BOX)) { + map[{pos.first + h, pos.second + v}] = type; + map[{pos.first, pos.second}] = TileType::EMPTY; + return true; + } else { + return false; + } + } else if(map[{pos.first + h , pos.second + v}] == TileType::WALL) { + return false; + } + return false; +} + +int64_t part_1() { + std::istringstream input_stream(content); std::string line; - while (std::getline(file, line)) { - + std::unordered_map, TileType> map = {}; + + int y = 0, x = 0; + std::pair robot_pos; + while(getline(input_stream, line)) { + if(line == "") break; + + x = 0; + for(auto c : line) { + switch (c) { + case '#': + map[{x, y}] = TileType::WALL; + break; + case '@': + robot_pos = {x, y}; + map[{x, y}] = TileType::ROBOT; + break; + case 'O': + map[{x, y}] = TileType::BOX; + break; + default: + map[{x, y}] = TileType::EMPTY; + break; + } + x++; + } + y++; } + + std::vector movements = {}; + while(getline(input_stream, line)) { + if(line.empty()) break; + for(auto c : line) { + movements.push_back(static_cast(c)); + } + } + + for(auto m : movements) { + if(move_tile(robot_pos, m, map, TileType::ROBOT)) { + int h = Direction::LEFT == m ? -1 : Direction::RIGHT == m ? 1 : 0; + int v = Direction::UP == m ? -1 : Direction::DOWN == m ? 1 : 0; + robot_pos.first += h; + robot_pos.second += v; + } + } + + auto total = 0; + for(auto [k, v] : map) { + if(v == TileType::BOX) { + total += 100 * k.second + k.first; + } + } + + return total; +} + +enum class ScaledTileType { + ROBOT, + WALL, + LBOX, + RBOX, + EMPTY, +}; + +bool is_vertical_box_free(std::pair pos, ScaledTileType type, std::unordered_map, ScaledTileType>& map, Direction dir) { + int v = Direction::UP == dir ? -1 : Direction::DOWN == dir ? 1 : 0; + + std::pair lft_pos, right_pos; + if(type == ScaledTileType::LBOX) { + lft_pos = {pos.first , pos.second}; + right_pos = {pos.first + 1, pos.second}; + } else { + lft_pos = {pos.first - 1, pos.second}; + right_pos = {pos.first, pos.second}; + } + + auto next_right = map[{right_pos.first, right_pos.second + v}]; + auto next_left = map[{lft_pos.first, lft_pos.second + v}]; + + if(next_left == ScaledTileType::EMPTY && next_right == ScaledTileType::EMPTY) { + return true; + } else if(next_left == ScaledTileType::WALL || next_right == ScaledTileType::WALL) { + return false; + } + bool box_free = true; + if(next_left == ScaledTileType::LBOX || next_left == ScaledTileType::RBOX) { + auto box_type = next_left; + box_free &= is_vertical_box_free({lft_pos.first, lft_pos.second + v}, box_type, map, dir); + } + if(next_right == ScaledTileType::LBOX || next_right == ScaledTileType::RBOX) { + auto box_type = next_right; + box_free &= is_vertical_box_free({right_pos.first, right_pos.second + v}, box_type, map, dir); + } + return box_free; +} + +bool is_horizontal_box_free(std::pair pos, ScaledTileType type, std::unordered_map, ScaledTileType>& map, Direction dir) { + int h = Direction::LEFT == dir ? -1 : Direction::RIGHT == dir ? 1 : 0; + + auto next = map[{pos.first + h, pos.second}]; + if(next == ScaledTileType::EMPTY) { + return true; + } else if (next == ScaledTileType::RBOX || next == ScaledTileType::LBOX) { + return is_horizontal_box_free({pos.first + h, pos.second}, next, map, dir); + } else { + return false; + } +} + +bool is_box_free(std::pair pos, ScaledTileType type, std::unordered_map, ScaledTileType>& map, Direction dir) { + switch (dir) { + case Direction::UP: + case Direction::DOWN: + return is_vertical_box_free(pos, type, map, dir); + case Direction::LEFT: + case Direction::RIGHT: + return is_horizontal_box_free(pos, type, map, dir); + } +} + +bool move_vertical_tile(std::pair pos, ScaledTileType type, std::unordered_map, ScaledTileType>& map, Direction dir) { + int v = Direction::UP == dir ? -1 : Direction::DOWN == dir ? 1 : 0; + + auto next_pos = std::pair{pos.first, pos.second + v}; + auto next = map[{pos.first, pos.second + v}]; + if(next == ScaledTileType::EMPTY) { + map[next_pos] = type; + map[pos] = ScaledTileType::EMPTY; + return true; + } else if(next == ScaledTileType::LBOX) { + if(is_vertical_box_free(next_pos, ScaledTileType::LBOX, map, dir)) { + move_vertical_tile(next_pos, ScaledTileType::LBOX, map, dir); + move_vertical_tile({pos.first + 1, pos.second + v}, ScaledTileType::RBOX, map, dir); + map[next_pos] = type; + map[pos] = ScaledTileType::EMPTY; + return true; + } else { + return false; + } + } else if(next == ScaledTileType::RBOX) { + if(is_vertical_box_free(next_pos, ScaledTileType::RBOX, map, dir)) { + move_vertical_tile(next_pos, ScaledTileType::RBOX, map, dir); + move_vertical_tile({pos.first - 1, pos.second + v}, ScaledTileType::LBOX, map, dir); + map[next_pos] = type; + map[pos] = ScaledTileType::EMPTY; + return true; + } else { + return false; + } + } else if(next == ScaledTileType::WALL) { + return false; + } + + return false; +} + +bool move_horizontal_tile(std::pair pos, ScaledTileType type, std::unordered_map, ScaledTileType>& map, Direction dir) { + int h = Direction::LEFT == dir ? -1 : Direction::RIGHT == dir ? 1 : 0; + + auto next_pos = std::pair{pos.first + h, pos.second}; + auto next = map[{pos.first + h, pos.second}]; + if(next == ScaledTileType::EMPTY) { + map[next_pos] = type; + map[pos] = ScaledTileType::EMPTY; + return true; + } else if(next == ScaledTileType::LBOX || next == ScaledTileType::RBOX) { + if(is_horizontal_box_free(next_pos, next, map, dir)) { + move_horizontal_tile(next_pos, next, map, dir); + map[next_pos] = type; + map[pos] = ScaledTileType::EMPTY; + return true; + } else { + return false; + } + } else if(next == ScaledTileType::WALL) { + return false; + } + + return false; +} + +bool move_scaled_tile(std::pair pos, ScaledTileType type, std::unordered_map, ScaledTileType>& map, Direction dir) { + switch (dir) { + case Direction::UP: + case Direction::DOWN: + return move_vertical_tile(pos, type, map, dir); + case Direction::LEFT: + case Direction::RIGHT: + return move_horizontal_tile(pos, type, map, dir); + } + + return false; +} + +int64_t part_2() { + std::istringstream input_stream(content); + std::string line; + + std::unordered_map, ScaledTileType> map = {}; + + int y = 0, x = 0; + std::pair robot_pos; + while(getline(input_stream, line)) { + if(line == "") break; + + x = 0; + for(auto c : line) { + switch(c) { + case '#': + map[{x, y}] = ScaledTileType::WALL; + map[{x+1, y}] = ScaledTileType::WALL; + break; + case '@': + robot_pos = {x, y}; + map[{x, y}] = ScaledTileType::ROBOT; + map[{x+1, y}] = ScaledTileType::EMPTY; + break; + case 'O': + map[{x, y}] = ScaledTileType::LBOX; + map[{x+1, y}] = ScaledTileType::RBOX; + break; + default: + map[{x, y}] = ScaledTileType::EMPTY; + map[{x+1, y}] = ScaledTileType::EMPTY; + break; + } + x += 2; + } + y++; + } + + std::vector movements = {}; + while(getline(input_stream, line)) { + if(line.empty()) break; + for(auto c : line) { + movements.push_back(static_cast(c)); + } + } + + for(auto m : movements) { + if(move_scaled_tile(robot_pos, ScaledTileType::ROBOT, map, m)) { + int v = Direction::UP == m ? -1 : Direction::DOWN == m ? 1 : 0; + int h = Direction::LEFT == m ? -1 : Direction::RIGHT == m ? 1 : 0; + robot_pos.first += h; + robot_pos.second += v; + } + } + + auto total = 0; + for(auto [k, v] : map) { + if(v == ScaledTileType::LBOX) { + total += 100 * k.second + k.first; + } + } + + return total; +} + +int main() { + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + auto p1_val = part_1(); + std::chrono::steady_clock::time_point middle = std::chrono::steady_clock::now(); + auto p2_val = part_2(); + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + cout << "Part 1: " << p1_val << " (" << std::chrono::duration_cast(middle - begin).count() << "µs)" << endl; + cout << "Part 2: " << p2_val << " (" << std::chrono::duration_cast(end - middle).count() << "µs)" << endl; return 0; }