2023-11-15 21:47:50 +01:00
|
|
|
#include "parse_game.h"
|
|
|
|
|
2023-11-15 23:07:39 +01:00
|
|
|
#include "myassert.h"
|
|
|
|
|
2023-11-16 16:20:04 +01:00
|
|
|
namespace Parsing
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
// This helper function deduces the type and assigns the value with the matching key
|
|
|
|
template<class T>
|
2023-11-16 16:20:04 +01:00
|
|
|
void extract(boost::json::object const & obj, T & t, std::string_view key)
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
t = value_to<T>(obj.at(key));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-16 16:20:04 +01:00
|
|
|
namespace Hanabi
|
|
|
|
{
|
|
|
|
Card tag_invoke(
|
|
|
|
boost::json::value_to_tag<Card>, boost::json::value const & jv
|
|
|
|
)
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
Hanabi::Card card{};
|
2023-11-16 16:20:04 +01:00
|
|
|
boost::json::object const & obj = jv.as_object();
|
2023-11-15 21:47:50 +01:00
|
|
|
Parsing::extract(obj, card.rank, "rank");
|
|
|
|
Parsing::extract(obj, card.suit, "suitIndex");
|
|
|
|
card.rank = 5 - card.rank;
|
|
|
|
return card;
|
|
|
|
}
|
|
|
|
|
2023-11-16 16:20:04 +01:00
|
|
|
void tag_invoke(boost::json::value_from_tag, boost::json::value & jv, Hanabi::Card const & card)
|
|
|
|
{
|
|
|
|
jv = {{ "suitIndex", card.suit}
|
|
|
|
, {"rank" , card.rank}};
|
2023-11-15 21:47:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-16 16:20:04 +01:00
|
|
|
namespace Parsing
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
|
2023-11-16 16:20:04 +01:00
|
|
|
HanabLiveAction tag_invoke(boost::json::value_to_tag<HanabLiveAction>, boost::json::value const & jv)
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
HanabLiveAction action{};
|
|
|
|
uint8_t type;
|
2023-11-16 16:20:04 +01:00
|
|
|
boost::json::object const & obj = jv.as_object();
|
2023-11-15 21:47:50 +01:00
|
|
|
|
|
|
|
extract(obj, action.target, "target");
|
|
|
|
extract(obj, type, "type");
|
|
|
|
|
|
|
|
action.type = static_cast<Hanabi::ActionType>(type);
|
2023-11-16 16:20:04 +01:00
|
|
|
switch (action.type)
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-11-16 16:20:04 +01:00
|
|
|
std::pair<std::vector<Hanabi::Card>, Hanabi::suit_t> parse_deck(const boost::json::value & deck_json)
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
auto deck = boost::json::value_to<std::vector<Hanabi::Card>>(deck_json);
|
2023-11-16 16:20:04 +01:00
|
|
|
for (auto & card: deck)
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
ASSERT(card.rank < 5);
|
|
|
|
ASSERT(card.rank >= 0);
|
|
|
|
ASSERT(card.suit < 6);
|
|
|
|
ASSERT(card.suit >= 0);
|
|
|
|
}
|
|
|
|
Hanabi::suit_t num_suits = 0;
|
2023-11-16 16:20:04 +01:00
|
|
|
for (const auto & card: deck)
|
|
|
|
{
|
2023-11-15 21:47:50 +01:00
|
|
|
num_suits = std::max(num_suits, card.suit);
|
|
|
|
}
|
|
|
|
return {deck, num_suits + 1};
|
|
|
|
}
|
|
|
|
|
2023-11-16 16:20:04 +01:00
|
|
|
std::vector<HanabLiveAction> parse_actions(const boost::json::value & action_json)
|
2023-11-15 21:47:50 +01:00
|
|
|
{
|
|
|
|
return boost::json::value_to<std::vector<HanabLiveAction>>(action_json);
|
|
|
|
}
|
|
|
|
|
2023-11-16 16:20:04 +01:00
|
|
|
std::vector<Hanabi::Action>
|
|
|
|
convert_actions(std::vector<HanabLiveAction> const & hanab_live_actions, std::vector<Hanabi::Card> const & deck)
|
2023-11-15 21:47:50 +01:00
|
|
|
{
|
|
|
|
std::vector<Hanabi::Action> actions;
|
|
|
|
std::transform(
|
2023-11-16 16:20:04 +01:00
|
|
|
hanab_live_actions.begin()
|
|
|
|
, hanab_live_actions.end()
|
|
|
|
, std::back_inserter(actions)
|
|
|
|
, [&deck](HanabLiveAction const & action) {
|
|
|
|
return Hanabi::Action{action.type, deck[action.target]};
|
2023-11-15 21:47:50 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
return actions;
|
|
|
|
}
|
|
|
|
|
2023-11-15 23:23:21 +01:00
|
|
|
Hanabi::GameInfo parse_game(boost::json::object const & game_json)
|
2023-11-15 21:47:50 +01:00
|
|
|
{
|
|
|
|
auto const [deck, num_suits] = parse_deck(game_json.at("deck"));
|
|
|
|
const std::vector<Parsing::HanabLiveAction> hanab_live_actions = parse_actions(game_json.at("actions"));
|
|
|
|
Hanabi::player_t num_players = game_json.at("players").as_array().size();
|
|
|
|
std::vector<Hanabi::Action> actions = convert_actions(hanab_live_actions, deck);
|
|
|
|
|
2024-01-09 02:21:01 +01:00
|
|
|
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 };
|
2023-11-15 21:47:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|