AoC code coverage
Current view: top level - puzzles/2023 - Day13.cpp (source / functions) Coverage Total Hit
Test: master Lines: 100.0 % 132 132
Test Date: 2025-07-28 10:53:57 Functions: 100.0 % 20 20

            Line data    Source code
       1              : #include "Grid2d.h"
       2              : #include "IntegerCast.h"
       3              : #include "LinewiseInput.h"
       4              : #include "PuzzleImpl.h"
       5              : 
       6              : #include <absl/container/flat_hash_map.h>
       7              : #include <absl/hash/hash.h>
       8              : #include <libassert/assert.hpp>
       9              : 
      10              : #include <execution>
      11              : #include <numeric>
      12              : #include <optional>
      13              : #include <string_view>
      14              : 
      15              : namespace {
      16              : using IndexType = Grid2dSpan<char>::IndexType;
      17              : 
      18              : class LineComparator {
      19              : public:
      20          200 :   LineComparator(Grid2dSpan<char const> const &grid) : _grid(grid) {
      21          200 :     _lineHashes.reserve(_grid.ySize());
      22          200 :     absl::Hash<Grid2dSpan<char const>> hasher;
      23         2700 :     for (int y = 0; y < _grid.ySize(); ++y)
      24         2500 :       _lineHashes.push_back(hasher(grid.line(y)));
      25          200 :   }
      26              : 
      27          690 :   bool operator()(IndexType const lhs, IndexType const rhs) const {
      28          690 :     if (_lineHashes[lhs] != _lineHashes[rhs])
      29          369 :       return false;
      30              : 
      31          321 :     return _grid.line(lhs) == _grid.line(rhs);
      32          690 :   }
      33              : 
      34              : private:
      35              :   Grid2dSpan<char const> _grid;
      36              :   std::vector<size_t> _lineHashes;
      37              : };
      38              : 
      39              : class ColumnComparator {
      40              : public:
      41          200 :   ColumnComparator(Grid2dSpan<char const> const &grid) : _grid(grid) {
      42          200 :     _columnHashes.reserve(_grid.xSize());
      43          200 :     absl::Hash<Grid2dSpan<char const>> hasher;
      44         2700 :     for (int x = 0; x < _grid.xSize(); ++x)
      45         2500 :       _columnHashes.push_back(hasher(grid.column(x)));
      46          200 :   }
      47              : 
      48         1418 :   bool operator()(IndexType const lhs, IndexType const rhs) const {
      49         1418 :     if (_columnHashes[lhs] != _columnHashes[rhs])
      50          951 :       return false;
      51              : 
      52          467 :     return _grid.column(lhs) == _grid.column(rhs);
      53         1418 :   }
      54              : 
      55              : private:
      56              :   Grid2dSpan<char const> _grid;
      57              :   std::vector<size_t> _columnHashes;
      58              : };
      59              : 
      60              : class FuzzyLineComparator {
      61              : public:
      62          100 :   FuzzyLineComparator(Grid2dSpan<char const> const &grid) : _grid(grid) {}
      63              : 
      64          328 :   bool operator()(IndexType const lhs, IndexType const rhs) const {
      65          328 :     bool missmatch = false;
      66         2273 :     for (IndexType x = 0; x < _grid.xSize(); ++x) {
      67         2166 :       if (_grid[x, lhs] != _grid[x, rhs]) {
      68          513 :         if (missmatch)
      69          221 :           return false;
      70          292 :         missmatch = true;
      71          292 :       }
      72         2166 :     }
      73          107 :     return true;
      74          328 :   }
      75              : 
      76              : private:
      77              :   Grid2dSpan<char const> _grid;
      78              : };
      79              : 
      80              : class FuzzyColumnComparator {
      81              : public:
      82          100 :   FuzzyColumnComparator(Grid2dSpan<char const> const &grid) : _grid(grid) {}
      83              : 
      84          933 :   bool operator()(IndexType const lhs, IndexType const rhs) const {
      85          933 :     bool missmatch = false;
      86         5227 :     for (IndexType y = 0; y < _grid.ySize(); ++y) {
      87         5047 :       if (_grid[lhs, y] != _grid[rhs, y]) {
      88         1613 :         if (missmatch)
      89          753 :           return false;
      90          860 :         missmatch = true;
      91          860 :       }
      92         5047 :     }
      93          180 :     return true;
      94          933 :   }
      95              : 
      96              : private:
      97              :   Grid2dSpan<char const> _grid;
      98              : };
      99              : 
     100          149 : std::optional<IndexType> findReflectionPoint(IndexType const size, auto const &comp) {
     101              : 
     102          149 :   std::optional<IndexType> reflectionLocation;
     103          149 :   IndexType const last = size - 1;
     104              : 
     105         1285 :   auto checkReflection = [&](IndexType x1, IndexType x2) {
     106         1285 :     if (comp(x1, x2)) {
     107          130 :       bool isReflection = true;
     108          463 :       while (isReflection && x1 < x2) {
     109          333 :         isReflection = comp(x1++, x2--);
     110          333 :       }
     111          130 :       if (isReflection) {
     112          100 :         reflectionLocation = x2;
     113          100 :       }
     114          130 :     }
     115         1285 :   };
     116              : 
     117          939 :   for (IndexType x = last % 2 == 1 ? 0 : 1; x < size && !reflectionLocation; x += 2)
     118          790 :     checkReflection(x, last);
     119              : 
     120          644 :   for (IndexType x = last % 2 == 1 ? last : last - 1; x >= 0 && !reflectionLocation; x -= 2)
     121          495 :     checkReflection(0, x);
     122              : 
     123          149 :   return reflectionLocation;
     124          149 : }
     125              : 
     126              : std::optional<IndexType> findReflectionPointFuzzy(IndexType const size, auto const &comp,
     127          144 :                                                   auto const &fuzzyComp) {
     128              : 
     129          144 :   std::optional<IndexType> reflectionLocation;
     130          144 :   IndexType const last = size - 1;
     131              : 
     132         1141 :   auto checkReflection = [&](IndexType x1, IndexType x2) {
     133         1141 :     if (fuzzyComp(x1, x2)) {
     134          172 :       bool fuzzy = false;
     135          172 :       bool isReflection = true;
     136          662 :       while (isReflection && x1 < x2) {
     137          490 :         isReflection = comp(x1, x2);
     138          490 :         if (!isReflection && !fuzzy) {
     139          120 :           fuzzy = true;
     140          120 :           isReflection = fuzzyComp(x1, x2);
     141          120 :         }
     142          490 :         ++x1;
     143          490 :         --x2;
     144          490 :       }
     145          172 :       if (isReflection && fuzzy) {
     146          100 :         reflectionLocation = x2;
     147          100 :       }
     148          172 :     }
     149         1141 :   };
     150              : 
     151          869 :   for (IndexType x = last % 2 == 1 ? 0 : 1; x < size && !reflectionLocation; x += 2)
     152          725 :     checkReflection(x, last);
     153              : 
     154          560 :   for (IndexType x = last % 2 == 1 ? last : last - 1; x >= 0 && !reflectionLocation; x -= 2)
     155          416 :     checkReflection(0, x);
     156              : 
     157          144 :   return reflectionLocation;
     158          144 : }
     159              : 
     160              : } // namespace
     161              : 
     162            1 : template <> size_t part1<2023, 13>(std::string_view const input) {
     163            1 :   LinewiseInput lines(input);
     164            1 :   auto linesPerGrid = lines.splitOnEmptyLines();
     165              : 
     166            1 :   return std::transform_reduce(
     167            1 :       std::execution::par, linesPerGrid.begin(), linesPerGrid.end(), std::size_t(0), std::plus<>(),
     168          100 :       [](auto const &gridLines) {
     169          100 :         Grid2d<char> const grid = makeCharGrid2d(gridLines);
     170          100 :         LineComparator lineComp(grid);
     171          100 :         ColumnComparator columnComp(grid);
     172              : 
     173          100 :         std::optional<IndexType> columnReflection = findReflectionPoint(grid.xSize(), columnComp);
     174          100 :         if (columnReflection) {
     175           51 :           return integerCast<size_t>(*columnReflection + 1);
     176           51 :         }
     177              : 
     178           49 :         std::optional<IndexType> lineReflection = findReflectionPoint(grid.ySize(), lineComp);
     179           49 :         ASSERT(lineReflection);
     180           49 :         return integerCast<size_t>((grid.ySize() - (*lineReflection + 1)) * 100); // NOLINT
     181          100 :       });
     182            1 : }
     183              : 
     184            1 : template <> size_t part2<2023, 13>(std::string_view const input) {
     185            1 :   LinewiseInput lines(input);
     186            1 :   auto linesPerGrid = lines.splitOnEmptyLines();
     187              : 
     188            1 :   return std::transform_reduce(
     189            1 :       std::execution::par, linesPerGrid.begin(), linesPerGrid.end(), std::size_t(0), std::plus<>(),
     190          100 :       [](auto const &gridLines) {
     191          100 :         Grid2d<char> const grid = makeCharGrid2d(gridLines);
     192          100 :         LineComparator lineComp(grid);
     193          100 :         FuzzyLineComparator fuzzyLineComp(grid);
     194          100 :         ColumnComparator columnComp(grid);
     195          100 :         FuzzyColumnComparator fuzzyColumnComp(grid);
     196              : 
     197          100 :         std::optional<IndexType> columnReflection =
     198          100 :             findReflectionPointFuzzy(grid.xSize(), columnComp, fuzzyColumnComp);
     199          100 :         if (columnReflection) {
     200           56 :           return integerCast<size_t>(*columnReflection + 1);
     201           56 :         }
     202              : 
     203           44 :         std::optional<IndexType> lineReflection =
     204           44 :             findReflectionPointFuzzy(grid.ySize(), lineComp, fuzzyLineComp);
     205              :         ASSERT(lineReflection, grid);
     206           44 :         return integerCast<size_t>((grid.ySize() - (*lineReflection + 1)) * 100); // NOLINT
     207          100 :       });
     208            1 : }
        

Generated by: LCOV version 2.0-1