From 30bf0251bc7ac832a1b0e542def68d855563c576 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Sun, 27 Dec 2020 21:46:24 -0500 Subject: [PATCH] Add day 24 part 2 solution --- day24/day24.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 6 deletions(-) diff --git a/day24/day24.cpp b/day24/day24.cpp index 932d110..9a9fc1f 100644 --- a/day24/day24.cpp +++ b/day24/day24.cpp @@ -9,6 +9,7 @@ enum Direction { EAST, WEST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST, NORTH_EAST }; constexpr auto DIRECTION_PATTERN = R"(e|w|se|sw|nw|ne)"; +constexpr int NUM_DAYS = 100; const std::map INPUT_TO_DIRECTION{ std::make_pair("e", EAST), std::make_pair("w", WEST), @@ -18,6 +19,8 @@ const std::map INPUT_TO_DIRECTION{ std::make_pair("ne", NORTH_EAST), }; +// https://www.redblobgames.com/grids/hexagons/ +// (He uses down to mean +y but I flipped that for my own sake). const std::map> DIRECTION_TO_DELTA{ std::make_pair(EAST, std::make_pair(-2, 0)), std::make_pair(WEST, std::make_pair(2, 0)), @@ -38,6 +41,11 @@ std::vector readInput(const std::string &filename) { return input; } +/** + * Parse a single line of input + * @param inputLine The line to parse + * @return std::vector A vector of the directions on each line. + */ std::vector parseInputLine(const std::string &inputLine) { std::regex directionPattern(DIRECTION_PATTERN); std::vector res; @@ -53,6 +61,11 @@ std::vector parseInputLine(const std::string &inputLine) { return res; } +/** + * Parse the entire input + * @param input The full puzzle input + * @return std::vector> The parsed puzzle input, which is each line as a vector of directions. + */ std::vector> parseInput(const std::vector &input) { std::vector> res; std::transform(input.cbegin(), input.cend(), std::back_inserter(res), parseInputLine); @@ -60,22 +73,109 @@ std::vector> parseInput(const std::vector &i return res; } -template -std::pair getPosition(Iter start, Iter end) { +/** + * Get the final position from an iterator of directions + * @tparam InputIterator An iterator of directions + * @param start The start iterator + * @param end The end iterator + * @return std::pair The final position from the sequence of directions + */ +template +std::pair getPositionFromDirections(InputIterator start, InputIterator end) { return std::accumulate(start, end, std::make_pair(0, 0), [](std::pair position, const Direction &dir) { - std::pair delta = DIRECTION_TO_DELTA.at(dir); + const std::pair &delta = DIRECTION_TO_DELTA.at(dir); return std::make_pair(position.first + delta.first, position.second + delta.second); }); } -int part1(const std::vector> &input) { +/** + * Given the puzzle input, get the tiles that are flipped. + * @param input The parsed puzzle input + * @return std::map, bool> A map of positions to whether or not the tiles are flipped (i.e. black) + */ +std::map, bool> getFlippedTiles(const std::vector> &input) { std::map, bool> flipped; for (const std::vector &sequence : input) { - std::pair pos = getPosition(sequence.cbegin(), sequence.cend()); + std::pair pos = getPositionFromDirections(sequence.cbegin(), sequence.cend()); flipped[pos] = !flipped[pos]; } - return std::count_if(flipped.cbegin(), flipped.cend(), [](auto entry) { return entry.second; }); + return flipped; +} + +/** + * Count the number of flipped (i.e. black) tiles + * @param tiles The tiles on the board + * @return int The number of flipped tiles + */ +int countFlippedTiles(std::map, bool> &tiles) { + return std::count_if(tiles.cbegin(), tiles.cend(), [](auto entry) { return entry.second; }); +} + +/** + * Get the neighbors of a given position + * @param position The position to get the neighbor of + * @return std::vector> The neighbors + */ +std::vector> getNeighbors(const std::pair &position) { + std::vector> res; + + std::transform( + DIRECTION_TO_DELTA.cbegin(), + DIRECTION_TO_DELTA.cend(), + std::back_inserter(res), + [&position](const auto &entry) { + const std::pair &delta = entry.second; + return std::make_pair(position.first + delta.first, position.second + delta.second); + }); + + return res; +} + +int part1(const std::vector> &input) { + auto flipped = getFlippedTiles(input); + + return countFlippedTiles(flipped); +} + +int part2(const std::vector> &input) { + std::map, bool> flipped = getFlippedTiles(input); + std::map, bool> next = getFlippedTiles(input); + for (int i = 0; i < NUM_DAYS; i++) { + // Ensure all entries have their neighbors in the map + // If this is not true, the number of neighbors will not be run correctly. + for (const auto &entry : next) { + for (const auto &neighbor : getNeighbors(entry.first)) { + // Emplace will not overwrite existing elements + flipped.emplace(neighbor, false); + } + } + + next.clear(); + + for (auto &entry : flipped) { + auto neighbors = getNeighbors(entry.first); + int numFlippedNeighbors = + std::count_if(neighbors.cbegin(), neighbors.cend(), [&flipped](const std::pair &pos) { + auto posIt = flipped.find(pos); + return posIt == flipped.end() ? false : posIt->second; + }); + + bool nextState = entry.second; + if (entry.second && (numFlippedNeighbors == 0 || numFlippedNeighbors > 2)) { + nextState = false; + next.emplace(entry.first, false); + } else if (!entry.second && numFlippedNeighbors == 2) { + nextState = true; + } + + next.emplace(entry.first, nextState); + } + + std::swap(flipped, next); + } + + return countFlippedTiles(flipped); } int main(int argc, char *argv[]) { @@ -88,4 +188,5 @@ int main(int argc, char *argv[]) { auto parsedInput = parseInput(input); std::cout << part1(parsedInput) << std::endl; + std::cout << part2(parsedInput) << std::endl; }