AoC code coverage
Current view: top level - puzzles/2024 - Day18.cpp (source / functions) Coverage Total Hit
Test: master Lines: 100.0 % 136 136
Test Date: 2025-12-11 19:43:23 Functions: 100.0 % 9 9

            Line data    Source code
       1              : #include "PuzzleImpl.h"
       2              : 
       3              : #include "Grid2d.h"
       4              : #include "LinewiseInput.h"
       5              : #include "Parsing.h"
       6              : #include "Views.h"
       7              : 
       8              : #include <format>
       9              : #include <queue>
      10              : #include <ranges>
      11              : #include <string_view>
      12              : #include <vector>
      13              : 
      14              : using namespace std::literals;
      15              : 
      16              : namespace {
      17              : int constexpr mapSize = 71;
      18              : int constexpr numBytes = 1024;
      19              : 
      20              : using VertexId = Vec2<int>;
      21              : int constexpr numDirections = 4;
      22              : std::array<Vec2<int>, numDirections> constexpr dirs{{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}};
      23              : 
      24              : struct VertexScore {
      25        48816 :   constexpr bool operator<(VertexScore const &other) const { return score > other.score; }
      26              : 
      27              :   VertexId id;
      28              :   int64_t score;
      29              : };
      30              : 
      31              : VertexId constexpr InvalidVertexId{-1, -1};
      32              : 
      33            1 : Grid2d<char> parseMap(std::string_view const input) {
      34            1 :   Grid2d<char> map(mapSize, mapSize, '#', 1);
      35            1 :   map.fill('.');
      36            1 :   LinewiseInput linewise(input);
      37         1024 :   for (auto l : linewise | views::take(numBytes)) {
      38         1024 :     auto v = parseIntegerRange<int>(l);
      39         1024 :     map[v[0], v[1]] = '#';
      40         1024 :   }
      41              : 
      42            1 :   return map;
      43            1 : }
      44              : 
      45            1 : auto parseMap2(std::string_view const input) {
      46            1 :   Grid2d<char> map(mapSize, mapSize, '#', 1);
      47            1 :   map.fill('.');
      48            1 :   LinewiseInput linewise(input);
      49         1024 :   for (auto l : linewise | views::take(numBytes)) {
      50         1024 :     auto v = parseIntegerRange<int>(l);
      51         1024 :     map[v[0], v[1]] = '#';
      52         1024 :   }
      53              : 
      54            1 :   std::vector<Vec2<int>> remainingBytes;
      55         2426 :   for (auto l : linewise | views::drop(numBytes)) {
      56         2426 :     auto v = parseIntegerRange<int>(l);
      57         2426 :     remainingBytes.emplace_back(v[0], v[1]);
      58         2426 :   }
      59              : 
      60            1 :   return std::make_tuple(std::move(map), std::move(remainingBytes));
      61            1 : }
      62              : 
      63              : } // namespace
      64              : 
      65            1 : template <> std::string solvePart1<2024, 18>(std::string_view const input) {
      66            1 :   Grid2d<char> const map = parseMap(input);
      67              : 
      68            1 :   VertexId const start{0, 0};
      69            1 :   VertexId const goal{mapSize - 1, mapSize - 1};
      70              : 
      71            1 :   Grid2d<VertexId> pred(map.xSize(), map.ySize(), InvalidVertexId);
      72            1 :   Grid2d<int64_t> gScore(map.xSize(), map.ySize(), std::numeric_limits<int64_t>::max());
      73            1 :   Grid2d<int64_t> fScore(map.xSize(), map.ySize(), std::numeric_limits<int64_t>::max());
      74            1 :   std::priority_queue<VertexScore> queue;
      75            1 :   std::vector<VertexId> shortestPath;
      76              : 
      77         3960 :   auto h = [&](VertexId const &v) {
      78         3960 :     return std::abs(v.x() - goal.x()) + std::abs(v.y() - goal.y());
      79         3960 :   };
      80              : 
      81            1 :   auto reconstructPath = [&](VertexId v) {
      82            1 :     shortestPath.clear();
      83          277 :     while (v != start) {
      84          276 :       shortestPath.push_back(v);
      85          276 :       ASSUME(pred[v] != InvalidVertexId);
      86          276 :       v = pred[v];
      87          276 :     }
      88            1 :   };
      89              : 
      90            1 :   gScore[start] = 0;
      91            1 :   fScore[start] = h(start);
      92            1 :   queue.emplace(start, fScore[start]);
      93              : 
      94         3944 :   while (!queue.empty()) {
      95         3944 :     auto [cur, oldFscore] = queue.top();
      96         3944 :     queue.pop();
      97         3944 :     if (oldFscore != fScore[cur])
      98          298 :       continue;
      99              : 
     100         3646 :     if (cur == goal) {
     101            1 :       reconstructPath(cur);
     102            1 :       break;
     103            1 :     }
     104              : 
     105        14580 :     for (auto d : dirs) {
     106        14580 :       VertexId neighbor = cur + d;
     107        14580 :       if (map[neighbor] == '#')
     108         2413 :         continue;
     109        12167 :       int64_t tentiative_gScore = gScore[cur] + 1;
     110        12167 :       if (tentiative_gScore < gScore[neighbor]) {
     111         3959 :         pred[neighbor] = cur;
     112         3959 :         gScore[neighbor] = tentiative_gScore;
     113         3959 :         fScore[neighbor] = tentiative_gScore + h(neighbor);
     114         3959 :         queue.emplace(neighbor, fScore[neighbor]);
     115         3959 :       }
     116        12167 :     }
     117         3645 :   }
     118              : 
     119            1 :   return std::to_string(shortestPath.size());
     120            1 : }
     121              : 
     122            1 : template <> std::string solvePart2<2024, 18>(std::string_view const input) {
     123            1 :   auto [map, remainingBytes] = parseMap2(input);
     124              : 
     125            1 :   VertexId const start{0, 0};
     126            1 :   VertexId const goal{mapSize - 1, mapSize - 1};
     127              : 
     128            1 :   Grid2d<VertexId> pred(map.xSize(), map.ySize(), InvalidVertexId);
     129            1 :   Grid2d<int64_t> gScore(map.xSize(), map.ySize(), std::numeric_limits<int64_t>::max());
     130            1 :   Grid2d<int64_t> fScore(map.xSize(), map.ySize(), std::numeric_limits<int64_t>::max());
     131            1 :   std::priority_queue<VertexScore> queue;
     132            1 :   std::vector<VertexId> shortestPath;
     133              : 
     134         6659 :   auto h = [&](VertexId const &v) {
     135         6659 :     return std::abs(v.x() - goal.x()) + std::abs(v.y() - goal.y());
     136         6659 :   };
     137              : 
     138            7 :   auto reconstructPath = [&](VertexId v) {
     139            7 :     shortestPath.clear();
     140         3691 :     while (v != start) {
     141         3684 :       shortestPath.push_back(v);
     142         3684 :       ASSUME(pred[v] != InvalidVertexId);
     143         3684 :       v = pred[v];
     144         3684 :     }
     145            7 :   };
     146              : 
     147            1 :   auto left = remainingBytes.begin();
     148            1 :   auto right = remainingBytes.end();
     149           12 :   while (left != right) {
     150           11 :     auto it = std::next(left, std::distance(left, right) / 2);
     151              : 
     152           11 :     Grid2d<char> updatedMap = map;
     153        20727 :     for (auto it2 = remainingBytes.begin(); it2 <= it; ++it2)
     154        20716 :       updatedMap[*it2] = '#';
     155              : 
     156           11 :     pred.fill(InvalidVertexId);
     157           11 :     gScore.fill(std::numeric_limits<int64_t>::max());
     158           11 :     fScore.fill(std::numeric_limits<int64_t>::max());
     159           11 :     shortestPath.clear();
     160              : 
     161           11 :     gScore[start] = 0;
     162           11 :     fScore[start] = h(start);
     163           11 :     queue.emplace(start, fScore[start]);
     164              : 
     165         6663 :     while (!queue.empty()) {
     166         6659 :       auto [cur, oldFscore] = queue.top();
     167         6659 :       queue.pop();
     168         6659 :       if (oldFscore != fScore[cur])
     169           67 :         continue;
     170              : 
     171         6592 :       if (cur == goal) {
     172            7 :         reconstructPath(cur);
     173            7 :         break;
     174            7 :       }
     175              : 
     176        26340 :       for (auto d : dirs) {
     177        26340 :         VertexId neighbor = cur + d;
     178        26340 :         if (updatedMap[neighbor] == '#')
     179        12763 :           continue;
     180        13577 :         int64_t tentiative_gScore = gScore[cur] + 1;
     181        13577 :         if (tentiative_gScore < gScore[neighbor]) {
     182         6648 :           pred[neighbor] = cur;
     183         6648 :           gScore[neighbor] = tentiative_gScore;
     184         6648 :           fScore[neighbor] = tentiative_gScore + h(neighbor);
     185         6648 :           queue.emplace(neighbor, fScore[neighbor]);
     186         6648 :         }
     187        13577 :       }
     188         6585 :     }
     189           11 :     if (shortestPath.empty())
     190            4 :       right = it;
     191            7 :     else
     192            7 :       left = std::next(it);
     193           11 :   }
     194              : 
     195            1 :   return std::format("{},{}", left->x(), left->y());
     196            1 : }
        

Generated by: LCOV version 2.0-1