#include "parse_game.h" #include "myassert.h" namespace Parsing { // This helper function deduces the type and assigns the value with the matching key template void extract(boost::json::object const & obj, T & t, std::string_view key) { t = boost::json::value_to(obj.at(key)); } } namespace Hanabi { Card tag_invoke( boost::json::value_to_tag, boost::json::value const & jv ) { Hanabi::Card card{}; boost::json::object const & obj = jv.as_object(); Parsing::extract(obj, card.rank, "rank"); Parsing::extract(obj, card.suit, "suitIndex"); card.rank = 5 - card.rank; return card; } void tag_invoke(boost::json::value_from_tag, boost::json::value & jv, Hanabi::Card const & card) { jv = {{ "suitIndex", card.suit} , {"rank" , card.rank}}; } } namespace Parsing { HanabLiveAction tag_invoke(boost::json::value_to_tag, boost::json::value const & jv) { HanabLiveAction action{}; uint8_t type; boost::json::object const & obj = jv.as_object(); extract(obj, action.target, "target"); extract(obj, type, "type"); action.type = static_cast(type); switch (action.type) { case Hanabi::ActionType::color_clue: case Hanabi::ActionType::rank_clue: action.type = Hanabi::ActionType::clue; break; case Hanabi::ActionType::end_game: case Hanabi::ActionType::vote_terminate_players: case Hanabi::ActionType::vote_terminate: action.type = Hanabi::ActionType::end_game; break; case Hanabi::ActionType::play: case Hanabi::ActionType::discard: break; default: throw std::runtime_error( "Invalid game format, could not parse action type " + std::to_string(type)); } return action; } std::pair, Hanabi::suit_t> parse_deck(const boost::json::value & deck_json) { auto deck = boost::json::value_to>(deck_json); for (auto & card: deck) { ASSERT(card.rank < 5); ASSERT(card.rank >= 0); ASSERT(card.suit < 6); ASSERT(card.suit >= 0); } Hanabi::suit_t num_suits = 0; for (const auto & card: deck) { num_suits = std::max(num_suits, card.suit); } return {deck, num_suits + 1}; } std::vector parse_actions(const boost::json::value & action_json) { return boost::json::value_to>(action_json); } std::vector convert_actions(std::vector const & hanab_live_actions, std::vector const & deck) { std::vector actions; std::transform( hanab_live_actions.begin() , hanab_live_actions.end() , std::back_inserter(actions) , [&deck](HanabLiveAction const & action) { return Hanabi::Action{action.type, deck[action.target]}; } ); return actions; } Hanabi::GameInfo parse_game(boost::json::object const & game_json) { auto const [deck, num_suits] = parse_deck(game_json.at("deck")); const std::vector hanab_live_actions = parse_actions(game_json.at("actions")); Hanabi::player_t num_players = game_json.at("players").as_array().size(); std::vector actions = convert_actions(hanab_live_actions, deck); boost::json::value const * options = game_json.if_contains("options"); Hanabi::clue_t num_clues_gained_on_discard = Hanabi::clue_t(1); if (options != nullptr) { boost::json::value const * variant = options->as_object().if_contains("variant"); if (variant != nullptr) { if (variant->as_string().find("Clue Starved") != std::string::npos) { num_clues_gained_on_discard = Hanabi::clue_t (1) / Hanabi::clue_t (2); } } } return {deck, actions, num_suits, num_players, num_clues_gained_on_discard }; } }