Add day 22 part 2 solution

master
Nick Krichevsky 2020-12-25 19:47:57 -05:00
parent bdcc189c89
commit 11249d06ee
2 changed files with 92 additions and 26 deletions

View File

@ -1,7 +1,7 @@
CC=g++ CC=g++
BIN_NAME=day22 BIN_NAME=day22
CCFLAGS=-o $(BIN_NAME) -g -std=c++17 CCFLAGS=-o $(BIN_NAME) -O2 -std=c++17
LDFLAGS=-lfolly LDFLAGS=
.PHONY: all, clean .PHONY: all, clean

View File

@ -1,18 +1,13 @@
#include <folly/String.h>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <numeric> #include <numeric>
#include <queue> #include <queue>
#include <regex>
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
// Pair of ingredients and allergens enum Player { PLAYER1, PLAYER2 };
using IngredientLineItem = std::pair<std::vector<std::string>, std::vector<std::string>>;
auto constexpr INGREDIENT_PATTERN = R"((.*) \(contains (.*)\))";
std::vector<std::string> readInput(const std::string &filename) { std::vector<std::string> readInput(const std::string &filename) {
std::vector<std::string> input; std::vector<std::string> input;
@ -41,27 +36,58 @@ std::pair<std::vector<std::string>, std::vector<std::string>> splitInput(const s
std::move(beforeEmptyLine), std::move(afterEmptyLine)); std::move(beforeEmptyLine), std::move(afterEmptyLine));
} }
template <typename Iterator, typename IntContainer> /**
void containerStoi(Iterator begin, Iterator end, IntContainer &out) { * Perform std::stoi on every element in the container, pushing the items to the output iterator.
std::transform(begin, end, std::back_inserter(out), [](const std::string &item) { return std::stoi(item); }); * @tparam Iterator The iterator for the input container
* @tparam IntContainer The container to output to
* @param begin The start of the range
* @param end The end of the range
* @param out The output iterator
*/
template <typename InputIterator, typename OutputIterator>
void containerStoi(InputIterator begin, InputIterator end, OutputIterator out) {
std::transform(begin, end, out, [](const std::string &item) { return std::stoi(item); });
} }
/**
* Parse the decks from the puzzle input
* @param input The puzzle input
* @return std::pair<std::vector<int>, std::vector<int>> Both decks to play the game (player 1, player 2)
*/
std::pair<std::vector<int>, std::vector<int>> parseDecks(const std::vector<std::string> &input) { std::pair<std::vector<int>, std::vector<int>> parseDecks(const std::vector<std::string> &input) {
std::pair<std::vector<std::string>, std::vector<std::string>> inputComponents = splitInput(input); std::pair<std::vector<std::string>, std::vector<std::string>> inputComponents = splitInput(input);
std::pair<std::vector<int>, std::vector<int>> decks; std::pair<std::vector<int>, std::vector<int>> decks;
containerStoi(inputComponents.first.cbegin() + 1, inputComponents.first.cend(), decks.first); containerStoi(inputComponents.first.cbegin() + 1, inputComponents.first.cend(), std::back_inserter(decks.first));
containerStoi(inputComponents.second.cbegin() + 1, inputComponents.second.cend(), decks.second); containerStoi(inputComponents.second.cbegin() + 1, inputComponents.second.cend(), std::back_inserter(decks.second));
return decks; return decks;
} }
std::vector<int> playBasicGame(const std::pair<std::vector<int>, std::vector<int>> &initialDecks) { /**
* Calculate the score for a game
* @tparam ReverseIterator A reverse iterator for the winning deck. Could technically be any input iterator but the
* score calculation requires going backwards
* @param begin The start of the deck
* @param end The end of the deck
* @return int The game score
*/
template <typename ReverseIterator>
int calculateScore(ReverseIterator begin, ReverseIterator end) {
int i = 1;
int total = 0;
for (auto it = begin; it != end; (++it, i++)) {
int value = *it;
total += i * value;
}
return total;
}
int part1(const std::pair<std::vector<int>, std::vector<int>> &initialDecks) {
std::pair<std::deque<int>, std::deque<int>> decks{ std::pair<std::deque<int>, std::deque<int>> decks{
std::deque<int>(initialDecks.first.cbegin(), initialDecks.first.cend()), std::deque<int>(initialDecks.first.cbegin(), initialDecks.first.cend()),
std::deque<int>(initialDecks.second.cbegin(), initialDecks.second.cend())}; std::deque<int>(initialDecks.second.cbegin(), initialDecks.second.cend())};
int i = 0;
while (!decks.first.empty() && !decks.second.empty()) { while (!decks.first.empty() && !decks.second.empty()) {
i++;
int player1Card = decks.first.front(); int player1Card = decks.first.front();
int player2Card = decks.second.front(); int player2Card = decks.second.front();
decks.first.pop_front(); decks.first.pop_front();
@ -77,19 +103,58 @@ std::vector<int> playBasicGame(const std::pair<std::vector<int>, std::vector<int
std::deque<int> &winnerDeck = decks.first.empty() ? decks.second : decks.first; std::deque<int> &winnerDeck = decks.first.empty() ? decks.second : decks.first;
return std::vector<int>(winnerDeck.cbegin(), winnerDeck.cend()); return calculateScore(winnerDeck.crbegin(), winnerDeck.crend());
} }
int part1(const std::pair<std::vector<int>, std::vector<int>> &decks) { int part2(const std::pair<std::vector<int>, std::vector<int>> &initialDecks) {
std::vector<int> winnerDeck = playBasicGame(decks); using DeckPair = std::pair<std::deque<int>, std::deque<int>>;
int i = 1; std::function<std::pair<Player, DeckPair>(DeckPair &)> playGame =
int total = 0; [&playGame](DeckPair &decks) -> std::pair<Player, std::pair<std::deque<int>, std::deque<int>>> {
for (auto it = winnerDeck.crbegin(); it != winnerDeck.crend(); (++it, i++)) { // Holds game states that have already been used, preventing their resuse within a single sub-game
int value = *it; std::set<DeckPair> usedDecks;
total += i * value; while (!decks.first.empty() && !decks.second.empty()) {
if (usedDecks.find(decks) != usedDecks.end()) {
return std::make_pair(PLAYER1, decks);
} }
return total; usedDecks.insert(decks);
int player1Card = decks.first.front();
int player2Card = decks.second.front();
Player roundWinner;
if (player1Card < decks.first.size() && player2Card < decks.second.size()) {
// Must be done after the size check...
decks.first.pop_front();
decks.second.pop_front();
DeckPair truncatedDecks;
std::copy_n(decks.first.cbegin(), player1Card, std::back_inserter(truncatedDecks.first));
std::copy_n(decks.second.cbegin(), player2Card, std::back_inserter(truncatedDecks.second));
roundWinner = playGame(truncatedDecks).first;
} else {
roundWinner = player1Card > player2Card ? PLAYER1 : PLAYER2;
decks.first.pop_front();
decks.second.pop_front();
}
if (roundWinner == PLAYER1) {
decks.first.push_back(player1Card);
decks.first.push_back(player2Card);
} else {
decks.second.push_back(player2Card);
decks.second.push_back(player1Card);
}
}
return std::make_pair(decks.first.empty() ? PLAYER2 : PLAYER1, decks);
};
std::pair<std::deque<int>, std::deque<int>> decks{
std::deque<int>(initialDecks.first.cbegin(), initialDecks.first.cend()),
std::deque<int>(initialDecks.second.cbegin(), initialDecks.second.cend())};
auto gameResult = playGame(decks);
auto winningDeck = gameResult.first == PLAYER1 ? gameResult.second.first : gameResult.second.second;
return calculateScore(winningDeck.crbegin(), winningDeck.crend());
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -102,4 +167,5 @@ int main(int argc, char *argv[]) {
auto decks = parseDecks(input); auto decks = parseDecks(input);
std::cout << part1(decks) << std::endl; std::cout << part1(decks) << std::endl;
std::cout << part2(decks) << std::endl;
} }