119 lines
3.2 KiB
C++
119 lines
3.2 KiB
C++
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <boost/fusion/adapted/std_tuple.hpp>
|
|
#include <boost/phoenix.hpp>
|
|
#include <boost/spirit/include/qi.hpp>
|
|
|
|
#include <doctest.h>
|
|
|
|
#include <aocpp/Parsing.hpp>
|
|
#include <aocpp/Startup.hpp>
|
|
|
|
namespace {
|
|
|
|
namespace qi = boost::spirit::qi;
|
|
namespace phx = boost::phoenix;
|
|
namespace ascii = boost::spirit::ascii;
|
|
|
|
struct Game
|
|
{
|
|
unsigned long long game_id;
|
|
std::vector<std::vector<std::tuple<unsigned long long, std::string>>> rounds;
|
|
};
|
|
|
|
struct Grammar : public qi::grammar<std::string::const_iterator, std::vector<Game>(), ascii::space_type> {
|
|
qi::rule<iterator_type, Game(), ascii::space_type> game;
|
|
qi::rule<iterator_type, std::vector<Game>(), ascii::space_type> games;
|
|
qi::rule<iterator_type, std::tuple<unsigned long long, std::string>(), ascii::space_type> entry;
|
|
|
|
Grammar() : base_type{games} {
|
|
using namespace qi::labels;
|
|
games = *game;
|
|
game =
|
|
"Game" >> qi::ulong_long [ phx::bind(&Game::game_id, _val) = _1] >> ":" >>
|
|
(entry % "," % ";") [ phx::bind(&Game::rounds, _val) = _1];
|
|
entry = qi::ulong_long >> qi::lexeme [ qi::as_string[+qi::alpha] ];
|
|
}
|
|
};
|
|
|
|
auto Part1(std::vector<Game> const& input) -> unsigned long long
|
|
{
|
|
static auto const limits = std::unordered_map<std::string, unsigned long long>{
|
|
{"red", 12},
|
|
{"green", 13},
|
|
{"blue", 14},
|
|
};
|
|
|
|
unsigned long long result = 0;
|
|
for (auto const& game : input)
|
|
{
|
|
auto const good = std::all_of(game.rounds.begin(), game.rounds.end(), [](auto const& round)
|
|
{
|
|
return std::all_of(round.begin(), round.end(), [](auto const& entry)
|
|
{
|
|
auto const limit = limits.find(std::get<1>(entry));
|
|
return limit != limits.end() && limit->second >= std::get<0>(entry);
|
|
});
|
|
});
|
|
if (good)
|
|
{
|
|
result += game.game_id;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto Part2(std::vector<Game> const& input) -> unsigned long long
|
|
{
|
|
unsigned long long result = 0;
|
|
for (auto const& game : input)
|
|
{
|
|
std::unordered_map<std::string, unsigned long long> maxes;
|
|
for (auto const& round : game.rounds) {
|
|
for (auto const& [count, color] : round)
|
|
{
|
|
auto& entry = maxes[color];
|
|
entry = std::max(entry, count);
|
|
}
|
|
}
|
|
|
|
result += std::transform_reduce(
|
|
maxes.begin(), maxes.end(),
|
|
1ULL, std::multiplies{}, [](auto& kv) { return kv.second; });
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_SUITE("2023-02 examples") {
|
|
TEST_CASE("example") {
|
|
std::istringstream in {
|
|
R"(Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
|
|
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
|
|
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
|
|
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
|
|
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
|
|
)"
|
|
};
|
|
auto const input = aocpp::ParseGrammar_(Grammar{}, in);
|
|
CHECK(Part1(input) == 8);
|
|
CHECK(Part2(input) == 2'286);
|
|
}
|
|
}
|
|
|
|
auto Main(std::istream & in, std::ostream & out) -> void
|
|
{
|
|
auto const input = aocpp::ParseGrammar_(Grammar{}, in);
|
|
out << "Part 1: " << Part1(input) << std::endl;
|
|
out << "Part 2: " << Part2(input) << std::endl;
|
|
}
|