Add day 22 part 2 solution
parent
bdcc189c89
commit
11249d06ee
|
@ -1,7 +1,7 @@
|
|||
CC=g++
|
||||
BIN_NAME=day22
|
||||
CCFLAGS=-o $(BIN_NAME) -g -std=c++17
|
||||
LDFLAGS=-lfolly
|
||||
CCFLAGS=-o $(BIN_NAME) -O2 -std=c++17
|
||||
LDFLAGS=
|
||||
|
||||
.PHONY: all, clean
|
||||
|
||||
|
|
114
day22/day22.cpp
114
day22/day22.cpp
|
@ -1,18 +1,13 @@
|
|||
#include <folly/String.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <queue>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Pair of ingredients and allergens
|
||||
using IngredientLineItem = std::pair<std::vector<std::string>, std::vector<std::string>>;
|
||||
auto constexpr INGREDIENT_PATTERN = R"((.*) \(contains (.*)\))";
|
||||
enum Player { PLAYER1, PLAYER2 };
|
||||
|
||||
std::vector<std::string> readInput(const std::string &filename) {
|
||||
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));
|
||||
}
|
||||
|
||||
template <typename Iterator, typename IntContainer>
|
||||
void containerStoi(Iterator begin, Iterator end, IntContainer &out) {
|
||||
std::transform(begin, end, std::back_inserter(out), [](const std::string &item) { return std::stoi(item); });
|
||||
/**
|
||||
* Perform std::stoi on every element in the container, pushing the items to the output iterator.
|
||||
* @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<std::string>, std::vector<std::string>> inputComponents = splitInput(input);
|
||||
std::pair<std::vector<int>, std::vector<int>> decks;
|
||||
containerStoi(inputComponents.first.cbegin() + 1, inputComponents.first.cend(), decks.first);
|
||||
containerStoi(inputComponents.second.cbegin() + 1, inputComponents.second.cend(), decks.second);
|
||||
containerStoi(inputComponents.first.cbegin() + 1, inputComponents.first.cend(), std::back_inserter(decks.first));
|
||||
containerStoi(inputComponents.second.cbegin() + 1, inputComponents.second.cend(), std::back_inserter(decks.second));
|
||||
|
||||
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::deque<int>(initialDecks.first.cbegin(), initialDecks.first.cend()),
|
||||
std::deque<int>(initialDecks.second.cbegin(), initialDecks.second.cend())};
|
||||
int i = 0;
|
||||
while (!decks.first.empty() && !decks.second.empty()) {
|
||||
i++;
|
||||
int player1Card = decks.first.front();
|
||||
int player2Card = decks.second.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;
|
||||
|
||||
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) {
|
||||
std::vector<int> winnerDeck = playBasicGame(decks);
|
||||
int i = 1;
|
||||
int total = 0;
|
||||
for (auto it = winnerDeck.crbegin(); it != winnerDeck.crend(); (++it, i++)) {
|
||||
int value = *it;
|
||||
total += i * value;
|
||||
}
|
||||
int part2(const std::pair<std::vector<int>, std::vector<int>> &initialDecks) {
|
||||
using DeckPair = std::pair<std::deque<int>, std::deque<int>>;
|
||||
std::function<std::pair<Player, DeckPair>(DeckPair &)> playGame =
|
||||
[&playGame](DeckPair &decks) -> std::pair<Player, std::pair<std::deque<int>, std::deque<int>>> {
|
||||
// Holds game states that have already been used, preventing their resuse within a single sub-game
|
||||
std::set<DeckPair> usedDecks;
|
||||
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[]) {
|
||||
|
@ -102,4 +167,5 @@ int main(int argc, char *argv[]) {
|
|||
auto decks = parseDecks(input);
|
||||
|
||||
std::cout << part1(decks) << std::endl;
|
||||
std::cout << part2(decks) << std::endl;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue