Add day 24 part 2 solution

master
Nick Krichevsky 2020-12-27 21:46:24 -05:00
parent 2d4168440f
commit 30bf0251bc
1 changed files with 107 additions and 6 deletions

View File

@ -9,6 +9,7 @@
enum Direction { EAST, WEST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST, NORTH_EAST }; enum Direction { EAST, WEST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST, NORTH_EAST };
constexpr auto DIRECTION_PATTERN = R"(e|w|se|sw|nw|ne)"; constexpr auto DIRECTION_PATTERN = R"(e|w|se|sw|nw|ne)";
constexpr int NUM_DAYS = 100;
const std::map<std::string, Direction> INPUT_TO_DIRECTION{ const std::map<std::string, Direction> INPUT_TO_DIRECTION{
std::make_pair("e", EAST), std::make_pair("e", EAST),
std::make_pair("w", WEST), std::make_pair("w", WEST),
@ -18,6 +19,8 @@ const std::map<std::string, Direction> INPUT_TO_DIRECTION{
std::make_pair("ne", NORTH_EAST), 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, std::pair<int, int>> DIRECTION_TO_DELTA{ const std::map<Direction, std::pair<int, int>> DIRECTION_TO_DELTA{
std::make_pair(EAST, std::make_pair(-2, 0)), std::make_pair(EAST, std::make_pair(-2, 0)),
std::make_pair(WEST, std::make_pair(2, 0)), std::make_pair(WEST, std::make_pair(2, 0)),
@ -38,6 +41,11 @@ std::vector<std::string> readInput(const std::string &filename) {
return input; return input;
} }
/**
* Parse a single line of input
* @param inputLine The line to parse
* @return std::vector<Direction> A vector of the directions on each line.
*/
std::vector<Direction> parseInputLine(const std::string &inputLine) { std::vector<Direction> parseInputLine(const std::string &inputLine) {
std::regex directionPattern(DIRECTION_PATTERN); std::regex directionPattern(DIRECTION_PATTERN);
std::vector<Direction> res; std::vector<Direction> res;
@ -53,6 +61,11 @@ std::vector<Direction> parseInputLine(const std::string &inputLine) {
return res; return res;
} }
/**
* Parse the entire input
* @param input The full puzzle input
* @return std::vector<std::vector<Direction>> The parsed puzzle input, which is each line as a vector of directions.
*/
std::vector<std::vector<Direction>> parseInput(const std::vector<std::string> &input) { std::vector<std::vector<Direction>> parseInput(const std::vector<std::string> &input) {
std::vector<std::vector<Direction>> res; std::vector<std::vector<Direction>> res;
std::transform(input.cbegin(), input.cend(), std::back_inserter(res), parseInputLine); std::transform(input.cbegin(), input.cend(), std::back_inserter(res), parseInputLine);
@ -60,22 +73,109 @@ std::vector<std::vector<Direction>> parseInput(const std::vector<std::string> &i
return res; return res;
} }
template <typename Iter> /**
std::pair<int, int> 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<int, int> The final position from the sequence of directions
*/
template <typename InputIterator>
std::pair<int, int> getPositionFromDirections(InputIterator start, InputIterator end) {
return std::accumulate(start, end, std::make_pair(0, 0), [](std::pair<int, int> position, const Direction &dir) { return std::accumulate(start, end, std::make_pair(0, 0), [](std::pair<int, int> position, const Direction &dir) {
std::pair<int, int> delta = DIRECTION_TO_DELTA.at(dir); const std::pair<int, int> &delta = DIRECTION_TO_DELTA.at(dir);
return std::make_pair(position.first + delta.first, position.second + delta.second); return std::make_pair(position.first + delta.first, position.second + delta.second);
}); });
} }
int part1(const std::vector<std::vector<Direction>> &input) { /**
* Given the puzzle input, get the tiles that are flipped.
* @param input The parsed puzzle input
* @return std::map<std::pair<int, int>, bool> A map of positions to whether or not the tiles are flipped (i.e. black)
*/
std::map<std::pair<int, int>, bool> getFlippedTiles(const std::vector<std::vector<Direction>> &input) {
std::map<std::pair<int, int>, bool> flipped; std::map<std::pair<int, int>, bool> flipped;
for (const std::vector<Direction> &sequence : input) { for (const std::vector<Direction> &sequence : input) {
std::pair<int, int> pos = getPosition(sequence.cbegin(), sequence.cend()); std::pair<int, int> pos = getPositionFromDirections(sequence.cbegin(), sequence.cend());
flipped[pos] = !flipped[pos]; 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<std::pair<int, int>, 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<std::pair<int, int>> The neighbors
*/
std::vector<std::pair<int, int>> getNeighbors(const std::pair<int, int> &position) {
std::vector<std::pair<int, int>> res;
std::transform(
DIRECTION_TO_DELTA.cbegin(),
DIRECTION_TO_DELTA.cend(),
std::back_inserter(res),
[&position](const auto &entry) {
const std::pair<int, int> &delta = entry.second;
return std::make_pair(position.first + delta.first, position.second + delta.second);
});
return res;
}
int part1(const std::vector<std::vector<Direction>> &input) {
auto flipped = getFlippedTiles(input);
return countFlippedTiles(flipped);
}
int part2(const std::vector<std::vector<Direction>> &input) {
std::map<std::pair<int, int>, bool> flipped = getFlippedTiles(input);
std::map<std::pair<int, int>, 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<int, int> &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[]) { int main(int argc, char *argv[]) {
@ -88,4 +188,5 @@ int main(int argc, char *argv[]) {
auto parsedInput = parseInput(input); auto parsedInput = parseInput(input);
std::cout << part1(parsedInput) << std::endl; std::cout << part1(parsedInput) << std::endl;
std::cout << part2(parsedInput) << std::endl;
} }