advent-of-code-2020/day24/day24.cpp

190 lines
5.9 KiB
C++
Raw Permalink Normal View History

2020-12-27 23:07:34 +00:00
#include <fstream>
#include <functional>
#include <iostream>
#include <numeric>
#include <regex>
#include <string>
#include <tuple>
enum Direction { EAST, WEST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST, NORTH_EAST };
constexpr auto DIRECTION_PATTERN = R"(e|w|se|sw|nw|ne)";
2020-12-28 02:46:24 +00:00
constexpr int NUM_DAYS = 100;
2020-12-27 23:07:34 +00:00
const std::map<std::string, Direction> INPUT_TO_DIRECTION{
std::make_pair("e", EAST),
std::make_pair("w", WEST),
std::make_pair("se", SOUTH_EAST),
std::make_pair("sw", SOUTH_WEST),
std::make_pair("nw", NORTH_WEST),
std::make_pair("ne", NORTH_EAST),
};
2020-12-28 02:46:24 +00:00
// https://www.redblobgames.com/grids/hexagons/
// (He uses down to mean +y but I flipped that for my own sake).
2020-12-27 23:07:34 +00:00
const std::map<Direction, std::pair<int, int>> DIRECTION_TO_DELTA{
std::make_pair(EAST, std::make_pair(-2, 0)),
std::make_pair(WEST, std::make_pair(2, 0)),
std::make_pair(SOUTH_EAST, std::make_pair(-1, -1)),
std::make_pair(SOUTH_WEST, std::make_pair(1, -1)),
std::make_pair(NORTH_WEST, std::make_pair(1, 1)),
std::make_pair(NORTH_EAST, std::make_pair(-1, 1)),
};
std::vector<std::string> readInput(const std::string &filename) {
std::vector<std::string> input;
std::string line;
std::ifstream file(filename);
while (std::getline(file, line)) {
input.push_back(line);
}
return input;
}
2020-12-28 02:46:24 +00:00
/**
* Parse a single line of input
* @param inputLine The line to parse
* @return std::vector<Direction> A vector of the directions on each line.
*/
2020-12-27 23:07:34 +00:00
std::vector<Direction> parseInputLine(const std::string &inputLine) {
std::regex directionPattern(DIRECTION_PATTERN);
std::vector<Direction> res;
std::transform(
std::sregex_iterator(inputLine.cbegin(), inputLine.cend(), directionPattern),
std::sregex_iterator(),
std::back_inserter(res),
[](const std::smatch &match) {
std::string directionStr = match.str();
return INPUT_TO_DIRECTION.at(directionStr);
});
return res;
}
2020-12-28 02:46:24 +00:00
/**
* 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.
*/
2020-12-27 23:07:34 +00:00
std::vector<std::vector<Direction>> parseInput(const std::vector<std::string> &input) {
std::vector<std::vector<Direction>> res;
std::transform(input.cbegin(), input.cend(), std::back_inserter(res), parseInputLine);
return res;
}
2020-12-28 02:46:24 +00:00
/**
* 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) {
2020-12-27 23:07:34 +00:00
return std::accumulate(start, end, std::make_pair(0, 0), [](std::pair<int, int> position, const Direction &dir) {
2020-12-28 02:46:24 +00:00
const std::pair<int, int> &delta = DIRECTION_TO_DELTA.at(dir);
2020-12-27 23:07:34 +00:00
return std::make_pair(position.first + delta.first, position.second + delta.second);
});
}
2020-12-28 02:46:24 +00:00
/**
* 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) {
2020-12-27 23:07:34 +00:00
std::map<std::pair<int, int>, bool> flipped;
for (const std::vector<Direction> &sequence : input) {
2020-12-28 02:46:24 +00:00
std::pair<int, int> pos = getPositionFromDirections(sequence.cbegin(), sequence.cend());
2020-12-27 23:07:34 +00:00
flipped[pos] = !flipped[pos];
}
2020-12-28 02:46:24 +00:00
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);
}
}
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;
} else if (!entry.second && numFlippedNeighbors == 2) {
nextState = true;
}
2020-12-28 02:57:27 +00:00
next[entry.first] = nextState;
2020-12-28 02:46:24 +00:00
}
std::swap(flipped, next);
}
return countFlippedTiles(flipped);
2020-12-27 23:07:34 +00:00
}
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << argv[0] << " <input_file>" << std::endl;
return 1;
}
auto input = readInput(argv[1]);
auto parsedInput = parseInput(input);
std::cout << part1(parsedInput) << std::endl;
2020-12-28 02:46:24 +00:00
std::cout << part2(parsedInput) << std::endl;
2020-12-27 23:07:34 +00:00
}