diff options
Diffstat (limited to '2024/src/day15.cpp')
| -rw-r--r-- | 2024/src/day15.cpp | 216 |
1 files changed, 205 insertions, 11 deletions
diff --git a/2024/src/day15.cpp b/2024/src/day15.cpp index 5c2449b..94b8a2b 100644 --- a/2024/src/day15.cpp +++ b/2024/src/day15.cpp | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #include <cassert> | ||
| 1 | #include <fstream> | 2 | #include <fstream> |
| 2 | #include <iostream> | 3 | #include <iostream> |
| 3 | #include <map> | 4 | #include <map> |
| @@ -8,10 +9,12 @@ | |||
| 8 | #include <tuple> | 9 | #include <tuple> |
| 9 | using namespace std; | 10 | using namespace std; |
| 10 | 11 | ||
| 11 | using pos_type = tuple<size_t, size_t>; | 12 | using pos_type = tuple<size_t, size_t>; |
| 12 | using room_type = set<pos_type>; | 13 | using room_type = set<pos_type>; |
| 13 | using boxes_type = set<pos_type>; | 14 | using boxes_type = set<pos_type>; |
| 14 | using puzzle_type = tuple<room_type, boxes_type, string, pos_type>; | 15 | using boxes2_type = map<pos_type, bool>; |
| 16 | using puzzle_type = tuple<room_type, boxes_type, string, pos_type>; | ||
| 17 | using puzzle2_type = tuple<room_type, boxes2_type, string, pos_type>; | ||
| 15 | 18 | ||
| 16 | puzzle_type | 19 | puzzle_type |
| 17 | read_file(string_view filename) | 20 | read_file(string_view filename) |
| @@ -51,12 +54,59 @@ read_file(string_view filename) | |||
| 51 | return { room, boxes, movements, start }; | 54 | return { room, boxes, movements, start }; |
| 52 | } | 55 | } |
| 53 | 56 | ||
| 54 | /* | 57 | puzzle2_type |
| 58 | read_file2(string_view filename) | ||
| 59 | { | ||
| 60 | fstream input{ filename }; | ||
| 61 | room_type room; | ||
| 62 | boxes2_type boxes; | ||
| 63 | string movements; | ||
| 64 | pos_type start; | ||
| 65 | |||
| 66 | size_t yPos = 0; | ||
| 67 | for ( string line; getline(input, line); ) { | ||
| 68 | if ( line.empty() ) { | ||
| 69 | continue; | ||
| 70 | } | ||
| 71 | |||
| 72 | if ( line[0] == '#' ) { | ||
| 73 | for ( size_t xPos = 0; xPos != line.size(); ++xPos ) { | ||
| 74 | auto chr = line.at(xPos); | ||
| 75 | if ( chr == '#' ) { | ||
| 76 | room.emplace(xPos * 2, yPos); | ||
| 77 | room.emplace(xPos * 2 + 1, yPos); | ||
| 78 | } | ||
| 79 | else if ( chr == 'O' ) { | ||
| 80 | boxes.insert({ pos_type(xPos * 2, yPos), true }); | ||
| 81 | boxes.insert({ pos_type(xPos * 2 + 1, yPos), false }); | ||
| 82 | } | ||
| 83 | else if ( chr == '@' ) { | ||
| 84 | start = { xPos * 2, yPos }; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | ++yPos; | ||
| 88 | } | ||
| 89 | else { | ||
| 90 | movements += line; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | return { room, boxes, movements, start }; | ||
| 95 | } | ||
| 96 | |||
| 55 | void | 97 | void |
| 56 | print(const room_type& room, const boxes_type& boxes, const pos_type& pos) | 98 | print(const room_type& room, const boxes_type& boxes, const pos_type& pos) |
| 57 | { | 99 | { |
| 58 | for ( size_t yPos = 0; yPos != 10; ++yPos ) { | 100 | size_t width = 0; |
| 59 | for ( size_t xPos = 0; xPos != 10; ++xPos ) { | 101 | size_t height = 0; |
| 102 | |||
| 103 | for ( const auto& [x, y]: room ) { | ||
| 104 | width = max(width, x + 1); | ||
| 105 | height = max(height, y + 1); | ||
| 106 | } | ||
| 107 | |||
| 108 | for ( size_t yPos = 0; yPos != height; ++yPos ) { | ||
| 109 | for ( size_t xPos = 0; xPos != width; ++xPos ) { | ||
| 60 | if ( pos == pos_type{ xPos, yPos } ) { | 110 | if ( pos == pos_type{ xPos, yPos } ) { |
| 61 | cout << '@'; | 111 | cout << '@'; |
| 62 | } | 112 | } |
| @@ -67,14 +117,45 @@ print(const room_type& room, const boxes_type& boxes, const pos_type& pos) | |||
| 67 | cout << 'O'; | 117 | cout << 'O'; |
| 68 | } | 118 | } |
| 69 | else { | 119 | else { |
| 70 | cout << ' '; | 120 | cout << '.'; |
| 121 | } | ||
| 122 | } | ||
| 123 | cout << '\n'; | ||
| 124 | } | ||
| 125 | cout << endl; | ||
| 126 | } | ||
| 127 | |||
| 128 | void | ||
| 129 | print(const room_type& room, const boxes2_type& boxes, const pos_type& pos) | ||
| 130 | { | ||
| 131 | size_t width = 0; | ||
| 132 | size_t height = 0; | ||
| 133 | |||
| 134 | for ( const auto& [x, y]: room ) { | ||
| 135 | width = max(width, x + 1); | ||
| 136 | height = max(height, y + 1); | ||
| 137 | } | ||
| 138 | |||
| 139 | for ( size_t yPos = 0; yPos != height; ++yPos ) { | ||
| 140 | for ( size_t xPos = 0; xPos != width; ++xPos ) { | ||
| 141 | if ( pos == pos_type{ xPos, yPos } ) { | ||
| 142 | cout << '@'; | ||
| 143 | } | ||
| 144 | else if ( room.contains({ xPos, yPos }) ) { | ||
| 145 | cout << '#'; | ||
| 146 | } | ||
| 147 | else if ( boxes.contains({ xPos, yPos }) ) { | ||
| 148 | auto value = boxes.at({ xPos, yPos }); | ||
| 149 | cout << (value ? '[' : ']'); | ||
| 150 | } | ||
| 151 | else { | ||
| 152 | cout << '.'; | ||
| 71 | } | 153 | } |
| 72 | } | 154 | } |
| 73 | cout << '\n'; | 155 | cout << '\n'; |
| 74 | } | 156 | } |
| 75 | cout << endl; | 157 | cout << endl; |
| 76 | } | 158 | } |
| 77 | */ | ||
| 78 | 159 | ||
| 79 | optional<boxes_type> | 160 | optional<boxes_type> |
| 80 | can_move(const room_type& room, const boxes_type& boxes, pos_type start, pos_type direction) // NOLINT | 161 | can_move(const room_type& room, const boxes_type& boxes, pos_type start, pos_type direction) // NOLINT |
| @@ -132,9 +213,122 @@ part1(const puzzle_type& puzzle) | |||
| 132 | }) << endl; | 213 | }) << endl; |
| 133 | } | 214 | } |
| 134 | 215 | ||
| 216 | tuple<pos_type, pos_type> | ||
| 217 | get_both_parts(const boxes2_type& boxes, pos_type pos) | ||
| 218 | { | ||
| 219 | const auto [x, y] = pos; | ||
| 220 | if ( boxes.at(pos) ) { | ||
| 221 | return { pos, { x + 1, y } }; | ||
| 222 | } | ||
| 223 | else { | ||
| 224 | return { { x - 1, y }, pos }; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | optional<boxes2_type> | ||
| 229 | can_move(const room_type& room, const boxes2_type& all_boxes, pos_type start, pos_type direction) // NOLINT | ||
| 230 | { | ||
| 231 | function<optional<boxes2_type>(const boxes2_type&, size_t)> can_move_up_down = [&room, &all_boxes, &can_move_up_down](const boxes2_type& boxes, size_t dy) -> optional<boxes2_type> { | ||
| 232 | boxes2_type candidates; | ||
| 233 | |||
| 234 | for ( const auto& [pos, b]: boxes ) { | ||
| 235 | const auto [x, y] = pos; | ||
| 236 | pos_type try_pos(x, y + dy); | ||
| 237 | |||
| 238 | if ( room.contains(try_pos) ) { | ||
| 239 | return nullopt; | ||
| 240 | } | ||
| 241 | |||
| 242 | if ( all_boxes.contains(try_pos) ) { | ||
| 243 | const auto [lhs, rhs] = get_both_parts(all_boxes, try_pos); | ||
| 244 | |||
| 245 | assert(all_boxes.contains(lhs) && all_boxes.at(lhs) == true); // NOLINT | ||
| 246 | assert(all_boxes.contains(rhs) && all_boxes.at(rhs) == false); // NOLINT | ||
| 247 | |||
| 248 | candidates.emplace(lhs, true); | ||
| 249 | candidates.emplace(rhs, false); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | if ( candidates.empty() ) { | ||
| 254 | return boxes2_type{}; | ||
| 255 | } | ||
| 256 | |||
| 257 | if ( auto results = can_move_up_down(candidates, dy) ) { | ||
| 258 | for ( const auto& result: results.value() ) { | ||
| 259 | candidates.insert(result); | ||
| 260 | } | ||
| 261 | return candidates; | ||
| 262 | } | ||
| 263 | |||
| 264 | return nullopt; | ||
| 265 | }; | ||
| 266 | |||
| 267 | const auto [x, y] = start; | ||
| 268 | const auto [dx, dy] = direction; | ||
| 269 | |||
| 270 | if ( dy != 0 ) { | ||
| 271 | return can_move_up_down({ { start, false } }, dy); | ||
| 272 | } | ||
| 273 | |||
| 274 | boxes2_type candidates; | ||
| 275 | |||
| 276 | size_t steps = 1; | ||
| 277 | while ( all_boxes.contains({ x + dx * steps, y + dy * steps }) ) { | ||
| 278 | auto foo = all_boxes.at({ x + dx * steps, y + dy * steps }); | ||
| 279 | candidates.insert({ { x + dx * steps, y + dy * steps }, foo }); | ||
| 280 | ++steps; | ||
| 281 | } | ||
| 282 | |||
| 283 | if ( room.contains({ x + dx * steps, y + dy * steps }) ) { | ||
| 284 | return nullopt; | ||
| 285 | } | ||
| 286 | |||
| 287 | return candidates; | ||
| 288 | } | ||
| 289 | |||
| 290 | void | ||
| 291 | part2(const puzzle2_type& puzzle) | ||
| 292 | { | ||
| 293 | map<char, pos_type> deltas = { | ||
| 294 | { '<', { -1, 0 } }, // Left | ||
| 295 | { '^', { 0, -1 } }, // Up | ||
| 296 | { '>', { 1, 0 } }, // Right | ||
| 297 | { 'v', { 0, 1 } } // Down | ||
| 298 | }; | ||
| 299 | |||
| 300 | auto [room, boxes, movements, start] = puzzle; | ||
| 301 | |||
| 302 | for ( auto chr: movements ) { | ||
| 303 | const auto delta = deltas[chr]; | ||
| 304 | if ( auto bitw = can_move(room, boxes, start, delta) ) { | ||
| 305 | const auto [dx, dy] = delta; | ||
| 306 | |||
| 307 | get<0>(start) += dx; | ||
| 308 | get<1>(start) += dy; | ||
| 309 | |||
| 310 | for ( const auto& [box, b]: bitw.value() ) { | ||
| 311 | boxes.erase(box); | ||
| 312 | } | ||
| 313 | |||
| 314 | for ( auto [pos, b]: bitw.value() ) { | ||
| 315 | const auto [x, y] = pos; | ||
| 316 | boxes.emplace(pos_type{ x + dx, y + dy }, b); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | cout << accumulate(boxes.begin(), boxes.end(), 0UL, [](auto init, const auto& box) { | ||
| 322 | const auto& [pos, b] = box; | ||
| 323 | if ( !b ) { | ||
| 324 | return init; | ||
| 325 | } | ||
| 326 | return init + get<0>(pos) + 100 * get<1>(pos); | ||
| 327 | }) << endl; | ||
| 328 | } | ||
| 135 | int | 329 | int |
| 136 | main() | 330 | main() |
| 137 | { | 331 | { |
| 138 | const auto data = read_file("data/day15.txt"); | 332 | part1(read_file("data/day15.txt")); |
| 139 | part1(data); | 333 | part2(read_file2("data/day15.txt")); |
| 140 | } | 334 | } |
