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

            Line data    Source code
       1              : #include "PuzzleImpl.h"
       2              : 
       3              : #include "Grid2d.h"
       4              : #include "LinewiseInput.h"
       5              : 
       6              : #include <algorithm>
       7              : #include <string>
       8              : #include <string_view>
       9              : #include <vector>
      10              : 
      11              : using namespace std::literals;
      12              : 
      13              : namespace {
      14              : 
      15            2 : std::vector<Vec2<int>> findPath(Grid2d<char> const &map) {
      16            2 :   static std::array<Vec2<int>, 4u> constexpr dirs{{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}};
      17              : 
      18            2 :   std::vector<Vec2<int>> path;
      19              : 
      20            2 :   Vec2<int> pos = map.find('S');
      21            2 :   Vec2<int> predPos{-1, -1};
      22            2 :   path.push_back(pos);
      23              : 
      24        18826 :   while (map[pos] != 'E') {
      25        47348 :     for (auto d : dirs) {
      26        47348 :       auto n = pos + d;
      27        47348 :       if (n != predPos && map[n] != '#') {
      28        18824 :         predPos = pos;
      29        18824 :         pos = n;
      30        18824 :         path.push_back(pos);
      31        18824 :         break;
      32        18824 :       }
      33        47348 :     }
      34        18824 :   }
      35              : 
      36            2 :   return path;
      37            2 : }
      38              : 
      39              : struct PosIdx {
      40              :   Vec2<int> pos;
      41              :   int idx;
      42              : };
      43              : 
      44            2 : size_t countCheats(Grid2d<char> const &map, int const radius, int const minSaving) {
      45            2 :   std::vector<Vec2<int>> path = findPath(map);
      46              : 
      47            2 :   using ParcelGrid = Grid2d<std::vector<PosIdx>>;
      48            2 :   Vec2<int> constexpr parcelSize{5, 5};
      49            2 :   Vec2<int> parcelRadius = (radius + (parcelSize - 1)) / parcelSize;
      50            2 :   ParcelGrid parcels((map.size() + (parcelSize - 1)) / parcelSize, {},
      51            2 :                      std::max(parcelRadius.x(), parcelRadius.y()));
      52              : 
      53        18828 :   for (int i = 0; i < std::ssize(path); ++i)
      54        18826 :     parcels[path[i] / parcelSize].emplace_back(path[i], i);
      55              : 
      56            2 :   size_t ctr = 0;
      57              : 
      58           60 :   for (ParcelGrid::Coord p1{0, 0}; p1.y() < parcels.ySize(); ++p1.y())
      59         1740 :     for (p1.x() = 0; p1.x() < parcels.xSize(); ++p1.x()) {
      60              :       // for each parcel p1
      61              : 
      62              :       // handle local neighbors
      63         1682 :       auto const &parcel = parcels[p1];
      64        20508 :       for (auto itStart = parcel.begin(); itStart != parcel.end(); ++itStart)
      65       126464 :         for (auto itEnd = std::next(itStart); itEnd != parcel.end(); ++itEnd) {
      66       107638 :           int const cheatLength = (itEnd->pos - itStart->pos).l1norm();
      67       107638 :           if (cheatLength <= radius) {
      68        71487 :             auto saving = (itEnd->idx - itStart->idx) - cheatLength;
      69        71487 :             if (saving >= minSaving)
      70         7984 :               ++ctr;
      71        71487 :           }
      72       107638 :         }
      73              : 
      74              :       // Handle neighbors in neighboring parcels
      75         7569 :       for (ParcelGrid::Coord p2 = p1; p2.y() <= p1.y() + parcelRadius.y(); ++p2.y())
      76         5887 :         for (p2.x() = (p2.y() == p1.y() ? p1.x() + 1 : p1.x() - parcelRadius.x());
      77        42891 :              p2.x() <= p1.x() + parcelRadius.x(); ++p2.x()) {
      78              :           // for each parcel p2 in the neighborhood of p1 (located after p1 in grid order)
      79              : 
      80        37004 :           for (auto const &pp1 : parcels[p1])
      81      4255277 :             for (auto const &pp2 : parcels[p2]) {
      82      4255277 :               auto pathDist = std::abs(pp2.idx - pp1.idx);
      83      4255277 :               auto cheatLength = (pp2.pos - pp1.pos).l1norm();
      84      4255277 :               if (cheatLength <= radius && pathDist - cheatLength >= minSaving)
      85       998852 :                 ++ctr;
      86      4255277 :             }
      87        37004 :         }
      88         1682 :     }
      89              : 
      90            2 :   return ctr;
      91            2 : }
      92              : 
      93              : } // namespace
      94              : 
      95            1 : template <> std::string solvePart1<2024, 20>(std::string_view const input) {
      96            1 :   Grid2d<char> const map = LinewiseInput(input).makeCharGrid2d();
      97            1 :   return std::to_string(countCheats(map, 2, 100));
      98            1 : }
      99              : 
     100            1 : template <> std::string solvePart2<2024, 20>(std::string_view const input) {
     101            1 :   Grid2d<char> const map = LinewiseInput(input).makeCharGrid2d();
     102            1 :   return std::to_string(countCheats(map, 20, 100));
     103            1 : }
        

Generated by: LCOV version 2.0-1