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

Generated by: LCOV version 2.0-1