AoC code coverage
Current view: top level - puzzles/2023 - Day08.cpp (source / functions) Coverage Total Hit
Test: master Lines: 87.3 % 79 69
Test Date: 2025-12-11 19:43:23 Functions: 88.9 % 9 8

            Line data    Source code
       1              : #include "LinewiseInput.h"
       2              : #include "PuzzleImpl.h"
       3              : 
       4              : #include <libassert/assert.hpp>
       5              : #include <limits>
       6              : #include <re2/re2.h>
       7              : 
       8              : #include <algorithm>
       9              : #include <charconv>
      10              : #include <cstdint>
      11              : #include <numeric>
      12              : #include <string>
      13              : #include <string_view>
      14              : #include <tuple>
      15              : 
      16              : namespace {
      17              : 
      18              : struct Node {
      19              :   unsigned l = std::numeric_limits<unsigned>::max();
      20              :   unsigned r = std::numeric_limits<unsigned>::max();
      21              : };
      22              : 
      23         4717 : constexpr unsigned parseLocation(std::string_view const location) {
      24         4717 :   DEBUG_ASSERT(location.size() == 3u);
      25         4717 :   return ((location[0] - 'A') << 10u) + ((location[1] - 'A') << 5u) + (location[2] - 'A');
      26         4717 : }
      27              : 
      28         1572 : bool isStartingLocation(unsigned loc) {
      29         1572 :   constexpr unsigned mask = (1u << 5u) - 1u;
      30         1572 :   return (loc & mask) == 0;
      31         1572 : }
      32              : 
      33       218293 : bool isGoalLocation(unsigned loc) {
      34       218293 :   constexpr unsigned mask = (1u << 5u) - 1u;
      35       218293 :   return (loc & mask) == 'Z' - 'A';
      36       218293 : }
      37              : 
      38            0 : [[maybe_unused]] std::string printLocation(unsigned loc) {
      39            0 :   std::string result;
      40            0 :   result.resize(3u);
      41            0 : 
      42            0 :   constexpr unsigned mask = (1u << 5u) - 1u;
      43            0 :   result[0] = static_cast<char>('A' + (loc >> 10u));
      44            0 :   result[1] = static_cast<char>('A' + ((loc >> 5u) & mask));
      45            0 :   result[2] = static_cast<char>('A' + (loc & mask));
      46            0 :   return result;
      47            0 : }
      48              : 
      49            2 : std::tuple<std::vector<Node>, std::vector<unsigned>> parseNodes(auto begin, auto end) {
      50            2 :   static RE2 const nodePattern(R"(([A-Z]{3}) = \(([A-Z]{3}), ([A-Z]{3})\))");
      51              : 
      52            2 :   std::vector<Node> map(1u << 15);
      53            2 :   std::vector<unsigned> startingLocations;
      54            2 :   std::string_view cur, l, r;
      55         1574 :   while (begin != end) {
      56         1572 :     ASSUME(RE2::FullMatch(*begin++, nodePattern, &cur, &l, &r));
      57         1572 :     unsigned loc = parseLocation(cur);
      58         1572 :     DEBUG_ASSERT(loc < map.size());
      59         1572 :     map[loc] = Node{.l = parseLocation(l), .r = parseLocation(r)};
      60         1572 :     if (isStartingLocation(loc))
      61           12 :       startingLocations.push_back(loc);
      62         1572 :   }
      63              : 
      64            2 :   return {map, startingLocations};
      65            2 : }
      66              : 
      67              : } // namespace
      68              : 
      69            1 : template <> std::string solvePart1<2023, 8>(std::string_view const input) {
      70            1 :   LinewiseInput const lines(input);
      71            1 :   std::string_view const path = lines[0];
      72            1 :   auto [nodes, locations] = parseNodes(std::next(lines.begin(), 2), lines.end());
      73              : 
      74            1 :   unsigned constexpr goal = parseLocation("ZZZ");
      75            1 :   unsigned location = parseLocation("AAA");
      76              : 
      77            1 :   uint64_t ctr = 0;
      78           48 :   while (location != goal) {
      79        12408 :     for (auto it = path.begin(); it != path.end() && !isGoalLocation(location); ++it) {
      80        12361 :       location = *it == 'L' ? nodes[location].l : nodes[location].r;
      81        12361 :       ++ctr;
      82        12361 :     }
      83           47 :   }
      84            1 :   return std::to_string(ctr);
      85            1 : }
      86              : 
      87            1 : template <> std::string solvePart2<2023, 8>(std::string_view const input) {
      88            1 :   LinewiseInput const lines(input);
      89              : 
      90            1 :   std::string_view const path = lines[0];
      91            1 :   auto [nodes, locations] = parseNodes(std::next(lines.begin(), 2), lines.end());
      92              : 
      93            1 :   std::vector<uint64_t> counters(locations.size());
      94              : 
      95            7 :   for (size_t i = 0; i < locations.size(); ++i) {
      96            6 :     unsigned location = locations[i];
      97            6 :     uint64_t &ctr = counters[i];
      98          396 :     while (!isGoalLocation(location))
      99       102960 :       for (auto it = path.begin(); it != path.end() && !isGoalLocation(location); ++it) {
     100       102570 :         location = *it == 'L' ? nodes[location].l : nodes[location].r;
     101       102570 :         ++ctr;
     102       102570 :       }
     103            6 :   }
     104              : 
     105            6 :   auto countSteps = [&nodes, &path](unsigned location) {
     106            6 :     uint64_t ctr = 0u;
     107          396 :     while (!isGoalLocation(location))
     108       102960 :       for (auto it = path.begin(); it != path.end() && !isGoalLocation(location); ++it) {
     109       102570 :         location = *it == 'L' ? nodes[location].l : nodes[location].r;
     110       102570 :         ++ctr;
     111       102570 :       }
     112            6 :     return ctr;
     113            6 :   };
     114              : 
     115            1 :   return std::to_string(std::transform_reduce(
     116            1 :       locations.begin(), locations.end(), size_t(1),
     117            6 :       [](uint64_t lhs, uint64_t rhs) { return std::lcm(lhs, rhs); }, countSteps));
     118            1 : }
        

Generated by: LCOV version 2.0-1