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

            Line data    Source code
       1              : #include "Grid2d.h"
       2              : #include "LinewiseInput.h"
       3              : #include "PuzzleImpl.h"
       4              : #include "Vector.h"
       5              : 
       6              : #include <libassert/assert.hpp>
       7              : #include <ranges>
       8              : #include <re2/re2.h>
       9              : 
      10              : #include <algorithm>
      11              : #include <string_view>
      12              : #include <vector>
      13              : 
      14              : namespace rng = std::ranges;
      15              : 
      16              : namespace {
      17              : 
      18              : class Brick {
      19              : public:
      20         2910 :   Brick(std::string_view const str) {
      21         2910 :     static RE2 const pattern = R"((\d+),(\d+),(\d+)~(\d+),(\d+),(\d+))";
      22         2910 :     ASSUME(RE2::FullMatch(str, pattern, &_start.x(), &_start.y(), &_start.z(), &_end.x(), &_end.y(),
      23         2910 :                           &_end.z()));
      24              : 
      25         2910 :     ASSUME(_start.x() <= _end.x());
      26         2910 :     ASSUME(_start.y() <= _end.y());
      27         2910 :     ASSUME(_start.z() <= _end.z());
      28         2910 :   }
      29              : 
      30      4386594 :   [[nodiscard]] Vec3<int> const &start() const { return _start; }
      31      4354926 :   [[nodiscard]] Vec3<int> const &end() const { return _end; }
      32              : 
      33         2910 :   void dropToLvl(int const lvl) {
      34         2910 :     int d = _start.z() - lvl;
      35         2910 :     _start.z() -= d;
      36         2910 :     _end.z() -= d;
      37         2910 :   }
      38              : 
      39              : private:
      40              :   Vec3<int> _start;
      41              :   Vec3<int> _end;
      42              : };
      43              : 
      44            2 : void settleBricks(std::vector<Brick> &bricks) {
      45            2 :   Vec3<int> min = bricks.front().start();
      46            2 :   Vec3<int> max = bricks.front().end();
      47         2910 :   for (Brick const &b : bricks) {
      48         2910 :     min.x() = std::min(min.x(), b.start().x());
      49         2910 :     min.y() = std::min(min.y(), b.start().y());
      50         2910 :     min.z() = std::min(min.z(), b.start().z());
      51         2910 :     max.x() = std::max(max.x(), b.end().x());
      52         2910 :     max.y() = std::max(max.y(), b.end().y());
      53         2910 :     max.z() = std::max(max.z(), b.end().z());
      54         2910 :   }
      55              : 
      56            2 :   ASSUME(min.x() >= 0);
      57            2 :   ASSUME(min.y() >= 0);
      58              : 
      59            2 :   Grid2d<int> heightMap(max.x() + 1, max.y() + 1, 1);
      60              : 
      61        67132 :   std::ranges::sort(bricks, std::ranges::less{}, [](Brick const &b) { return b.start().z(); });
      62              : 
      63         2910 :   for (Brick &b : bricks) {
      64         2910 :     int maxHeight = 0;
      65         8480 :     for (int y = b.start().y(); y <= b.end().y(); ++y)
      66        13678 :       for (int x = b.start().x(); x <= b.end().x(); ++x)
      67         8108 :         maxHeight = std::max(heightMap[x, y], maxHeight);
      68         2910 :     b.dropToLvl(maxHeight + 1);
      69         8480 :     for (int y = b.start().y(); y <= b.end().y(); ++y)
      70        13678 :       for (int x = b.start().x(); x <= b.end().x(); ++x)
      71         8108 :         heightMap[x, y] = b.end().z();
      72         2910 :   }
      73            2 : }
      74              : 
      75              : class SupportInfo {
      76              : public:
      77              :   SupportInfo(std::vector<Brick> const &bricks)
      78            2 :       : _supports(bricks.size()), _isSupportedBy(bricks.size()) {
      79         2912 :     for (size_t i = 0; i < bricks.size(); ++i)
      80      4236960 :       for (size_t j = 0; j < bricks.size(); ++j) {
      81      4234050 :         if (bricks[i].end().z() + 1 != bricks[j].start().z())
      82      4206546 :           continue;
      83        27504 :         if (bricks[i].end().x() < bricks[j].start().x() ||
      84        27504 :             bricks[i].start().x() > bricks[j].end().x())
      85        19086 :           continue;
      86         8418 :         if (bricks[i].end().y() < bricks[j].start().y() ||
      87         8418 :             bricks[i].start().y() > bricks[j].end().y())
      88         5274 :           continue;
      89         3144 :         _supports[i].insert(j);
      90         3144 :         _isSupportedBy[j].insert(i);
      91         3144 :       }
      92            2 :   }
      93              : 
      94         1455 :   bool safeToDisentegrate(size_t const idx) {
      95         1455 :     return rng::all_of(_supports[idx],
      96         1455 :                        [this](size_t const i) { return _isSupportedBy[i].size() > 1u; });
      97         1455 :   }
      98              : 
      99         1455 :   size_t numFallingBricks(size_t const brickIdx) {
     100         1455 :     std::set<size_t> fallingBricks;
     101         1455 :     fallingBricks.insert(brickIdx);
     102         1455 :     for (size_t const i : _supports[brickIdx])
     103         1572 :       addFallingBricks(i, fallingBricks);
     104              : 
     105         1455 :     return fallingBricks.size() - 1u;
     106         1455 :   }
     107              : 
     108              : private:
     109        78255 :   void addFallingBricks(size_t brickIdx, std::set<size_t> &fallingBricks) {
     110        78255 :     if (fallingBricks.contains(brickIdx) || !rng::includes(fallingBricks, _isSupportedBy[brickIdx]))
     111         7253 :       return;
     112        71002 :     fallingBricks.insert(brickIdx);
     113        71002 :     for (size_t const i : _supports[brickIdx])
     114        76683 :       addFallingBricks(i, fallingBricks);
     115        71002 :   }
     116              : 
     117              :   std::vector<std::set<size_t>> _supports;
     118              :   std::vector<std::set<size_t>> _isSupportedBy;
     119              : };
     120              : 
     121              : } // namespace
     122              : 
     123            1 : template <> size_t part1<2023, 22>(std::string_view const input) {
     124            1 :   LinewiseInput lines(input);
     125            1 :   std::vector<Brick> bricks(lines.begin(), lines.end());
     126            1 :   settleBricks(bricks);
     127            1 :   SupportInfo si(bricks);
     128              : 
     129            1 :   return rng::count_if(rng::views::iota(0u, bricks.size()),
     130         1455 :                        [&](size_t const i) { return si.safeToDisentegrate(i); });
     131            1 : }
     132              : 
     133            1 : template <> size_t part2<2023, 22>(std::string_view const input) {
     134            1 :   LinewiseInput lines(input);
     135            1 :   std::vector<Brick> bricks(lines.begin(), lines.end());
     136            1 :   settleBricks(bricks);
     137            1 :   SupportInfo si(bricks);
     138              : 
     139            1 :   return rng::fold_left(
     140            1 :       rng::views::iota(0u, bricks.size()) |
     141         1455 :           rng::views::transform([&](size_t const i) { return si.numFallingBricks(i); }),
     142            1 :       0u, std::plus<>());
     143            1 : }
        

Generated by: LCOV version 2.0-1