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

Generated by: LCOV version 2.0-1