AoC code coverage
Current view: top level - puzzles/2023 - Day10.cpp (source / functions) Coverage Total Hit
Test: master Lines: 75.1 % 213 160
Test Date: 2025-12-11 19:43:23 Functions: 85.7 % 14 12

            Line data    Source code
       1              : #include "Grid2d.h"
       2              : #include "LinewiseInput.h"
       3              : #include "PuzzleImpl.h"
       4              : 
       5              : #include <libassert/assert.hpp>
       6              : 
       7              : #include <cstdint>
       8              : #include <iostream>
       9              : #include <string>
      10              : #include <string_view>
      11              : #include <tuple>
      12              : 
      13              : namespace {
      14              : 
      15              : using Grid = Grid2d<char>;
      16              : using IndexType = Grid2d<char>::IndexType;
      17              : using Coord = Grid::Coord;
      18              : 
      19              : enum Dir : uint8_t { N, S, E, W };
      20              : 
      21            4 : constexpr Coord operator+(Coord const &c, Dir d) {
      22            4 :   switch (d) {
      23            3 :   case N:
      24            3 :     return c + Coord{0, 1};
      25            1 :   case S:
      26            1 :     return c + Coord{0, -1};
      27            0 :   case E:
      28            0 :     return c + Coord{1, 0};
      29            0 :   case W:
      30            0 :     return c + Coord{-1, 0};
      31            0 :   default:
      32            0 :     UNREACHABLE();
      33            4 :   }
      34            4 : }
      35              : 
      36        27636 : constexpr Coord &operator+=(Coord &c, Dir const d) {
      37        27636 :   switch (d) {
      38         6922 :   case N:
      39         6922 :     c[1] += 1;
      40         6922 :     return c;
      41         6922 :   case S:
      42         6922 :     c[1] -= 1;
      43         6922 :     return c;
      44         6896 :   case E:
      45         6896 :     c[0] += 1;
      46         6896 :     return c;
      47         6896 :   case W:
      48         6896 :     c[0] -= 1;
      49         6896 :     return c;
      50            0 :   default:
      51            0 :     UNREACHABLE();
      52        27636 :   }
      53        27636 : }
      54              : 
      55        27634 : Dir findNextDir(char const curNode, Dir const d) {
      56        27634 :   switch (d) {
      57         6920 :   case N:
      58         6920 :     switch (curNode) {
      59         2584 :     case '|':
      60         2584 :       return N;
      61         2156 :     case 'F':
      62         2156 :       return E;
      63         2180 :     case '7':
      64         2180 :       return W;
      65            0 :     default:
      66            0 :       UNREACHABLE(curNode, d);
      67         6920 :     }
      68            0 :     break;
      69         6922 :   case S:
      70         6922 :     switch (curNode) {
      71         2630 :     case '|':
      72         2630 :       return S;
      73         2130 :     case 'J':
      74         2130 :       return W;
      75         2162 :     case 'L':
      76         2162 :       return E;
      77            0 :     default:
      78            0 :       UNREACHABLE(curNode, d);
      79         6922 :     }
      80            0 :     break;
      81         6896 :   case E:
      82         6896 :     switch (curNode) {
      83         2578 :     case '-':
      84         2578 :       return E;
      85         2160 :     case '7':
      86         2160 :       return S;
      87         2158 :     case 'J':
      88         2158 :       return N;
      89            0 :     default:
      90            0 :       UNREACHABLE(curNode, d);
      91         6896 :     }
      92            0 :     break;
      93         6896 :   case W:
      94         6896 :     switch (curNode) {
      95         2586 :     case '-':
      96         2586 :       return W;
      97         2132 :     case 'F':
      98         2132 :       return S;
      99         2178 :     case 'L':
     100         2178 :       return N;
     101            0 :     default:
     102            0 :       UNREACHABLE(curNode, d);
     103         6896 :     }
     104        27634 :   }
     105              : 
     106            0 :   return N;
     107        27634 : }
     108              : 
     109            3 : bool hasNorthConnection(Grid const &grid, Coord const &c) {
     110            3 :   char const neighborPiece = grid[c + N];
     111            3 :   return neighborPiece == '|' || neighborPiece == 'F' || neighborPiece == '7';
     112            3 : }
     113              : 
     114            1 : bool hasSouthConnection(Grid const &grid, Coord const &c) {
     115            1 :   char const neighborPiece = grid[c + S];
     116            1 :   return neighborPiece == '|' || neighborPiece == 'L' || neighborPiece == 'J';
     117            1 : }
     118              : 
     119            0 : bool hasEastConnection(Grid const &grid, Coord const &c) {
     120            0 :   char const neighborPiece = grid[c + E];
     121            0 :   return neighborPiece == '-' || neighborPiece == 'J' || neighborPiece == '7';
     122            0 : }
     123              : 
     124            0 : bool hasWestConnection(Grid const &grid, Coord const &c) {
     125            0 :   char const neighborPiece = grid[c + E];
     126            0 :   return neighborPiece == '-' || neighborPiece == 'F' || neighborPiece == 'L';
     127            0 : }
     128              : 
     129            2 : Dir findStartDir(Grid const &grid, Coord const &c) {
     130            2 :   if (hasNorthConnection(grid, c))
     131            2 :     return N;
     132            0 :   else if (hasSouthConnection(grid, c))
     133            0 :     return S;
     134            0 :   else if (hasEastConnection(grid, c))
     135            0 :     return E;
     136            0 :   else if (hasWestConnection(grid, c))
     137            0 :     return W;
     138            0 :   else
     139            0 :     UNREACHABLE();
     140              : 
     141            0 :   return N;
     142            2 : }
     143              : 
     144            1 : char findStartPosPiece(Grid const &grid, Coord const &c) {
     145              : 
     146            1 :   bool const n = hasNorthConnection(grid, c);
     147            1 :   bool const s = hasSouthConnection(grid, c);
     148            1 :   if (n && s)
     149            1 :     return '|';
     150            0 :   bool const e = hasEastConnection(grid, c);
     151            0 :   if (n && e)
     152            0 :     return 'L';
     153            0 :   if (s && e)
     154            0 :     return 'F';
     155            0 :   bool const w = hasWestConnection(grid, c);
     156            0 :   if (e && w)
     157            0 :     return '-';
     158            0 :   if (n && w)
     159            0 :     return 'J';
     160            0 :   if (s && w)
     161            0 :     return '7';
     162              : 
     163            0 :   PANIC(c, n, s, e, w);
     164            0 : }
     165              : 
     166            1 : size_t findFarthestAwayPoint(Grid const &grid) {
     167            1 :   Coord c = grid.find('S');
     168            1 :   ASSUME(c.x() < grid.xSize());
     169            1 :   ASSUME(c.y() < grid.ySize());
     170              : 
     171            1 :   Dir d = findStartDir(grid, c);
     172            1 :   size_t ctr = 0;
     173            1 :   ++ctr;
     174            1 :   c += d;
     175              : 
     176        13818 :   while (grid[c] != 'S') {
     177        13817 :     d = findNextDir(grid[c], d);
     178        13817 :     ++ctr;
     179        13817 :     c += d;
     180        13817 :   }
     181              : 
     182            1 :   return ctr / 2 + ctr % 2;
     183            1 : }
     184              : 
     185            1 : auto markLoopWithX(Grid const &grid) {
     186            1 :   Grid loopGrid(grid.xSize(), grid.ySize(), '.');
     187            1 :   Coord const startPos = grid.find('S');
     188            1 :   ASSUME(startPos.x() < grid.xSize());
     189            1 :   ASSUME(startPos.y() < grid.ySize());
     190              : 
     191            1 :   Coord c = startPos;
     192              : 
     193            1 :   Dir d = findStartDir(grid, c);
     194            1 :   loopGrid[c] = 'X';
     195            1 :   c += d;
     196              : 
     197        13818 :   while (grid[c] != 'S') {
     198        13817 :     d = findNextDir(grid[c], d);
     199        13817 :     loopGrid[c] = 'X';
     200        13817 :     c += d;
     201        13817 :   }
     202              : 
     203            1 :   return std::make_tuple(std::move(loopGrid), startPos);
     204            1 : }
     205              : 
     206            1 : size_t scanline(Grid const &grid, Grid const &loopGrid) {
     207            1 :   size_t ctr = 0;
     208          141 :   for (IndexType y = 0; y < grid.ySize(); ++y) {
     209          140 :     bool isInner = false;
     210          140 :     bool edgeFromBelow = false;
     211        19740 :     for (IndexType x = 0; x < grid.xSize(); ++x) {
     212        19600 :       if (loopGrid[x, y] != 'X') {
     213              :         // loopGrid[x, y] = isInner ? 'I' : 'O';
     214         5782 :         if (isInner)
     215          461 :           ++ctr;
     216        13818 :       } else {
     217        13818 :         switch (grid[x, y]) {
     218         2608 :         case '|':
     219         2608 :           isInner = !isInner;
     220         2608 :           break;
     221         2582 :         case '-':
     222         2582 :           continue;
     223         2144 :         case 'F':
     224         2144 :           edgeFromBelow = true;
     225         2144 :           break;
     226         2170 :         case 'L':
     227         2170 :           edgeFromBelow = false;
     228         2170 :           break;
     229         2144 :         case 'J':
     230         2144 :           if (edgeFromBelow)
     231         1170 :             isInner = !isInner;
     232         2144 :           break;
     233         2170 :         case '7':
     234         2170 :           if (!edgeFromBelow)
     235         1196 :             isInner = !isInner;
     236         2170 :           break;
     237            0 :         default:
     238            0 :           PANIC(x, y, grid[x, y]);
     239        13818 :         }
     240        13818 :       }
     241        19600 :     }
     242          140 :   }
     243            1 :   return ctr;
     244            1 : }
     245              : 
     246              : } // namespace
     247              : 
     248            1 : template <> std::string solvePart1<2023, 10>(std::string_view const input) {
     249            1 :   LinewiseInput const lines(input);
     250              : 
     251            1 :   Grid grid = lines.makeCharGrid2dWithGhostLayers('.', 1);
     252            1 :   return std::to_string(findFarthestAwayPoint(grid));
     253            1 : }
     254              : 
     255            1 : template <> std::string solvePart2<2023, 10>(std::string_view const input) {
     256            1 :   LinewiseInput const lines(input);
     257            1 :   Grid grid = lines.makeCharGrid2dWithGhostLayers('.', 1);
     258            1 :   auto [loopGrid, startPos] = markLoopWithX(grid);
     259            1 :   grid[startPos] = findStartPosPiece(grid, startPos);
     260            1 :   return std::to_string(scanline(grid, loopGrid));
     261            1 : }
        

Generated by: LCOV version 2.0-1