122 lines
3.8 KiB
C++
122 lines
3.8 KiB
C++
#ifndef DYNAMIC_PROGRAM_DOWNLOAD_H
|
|
#define DYNAMIC_PROGRAM_DOWNLOAD_H
|
|
|
|
#include <boost/json.hpp>
|
|
#include <boost/json/src.hpp>
|
|
#include <cpr/cpr.h>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <variant>
|
|
|
|
#include "game_state.h"
|
|
|
|
|
|
// This helper function deduces the type and assigns the value with the matching key
|
|
template<class T>
|
|
void extract( boost::json::object const& obj, T& t, std::string_view key )
|
|
{
|
|
t = value_to<T>( obj.at( key ) );
|
|
}
|
|
|
|
namespace Hanabi {
|
|
Card tag_invoke(boost::json::value_to_tag<Card>,
|
|
boost::json::value const &jv) {
|
|
Hanabi::Card card{};
|
|
boost::json::object const &obj = jv.as_object();
|
|
extract(obj, card.rank, "rank");
|
|
extract(obj, card.suit, "suitIndex");
|
|
card.rank = 5 - card.rank;
|
|
return card;
|
|
}
|
|
|
|
Action tag_invoke(boost::json::value_to_tag<Action>, boost::json::value const &jv) {
|
|
Hanabi::Action 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<ActionType>(type);
|
|
switch (action.type) {
|
|
case ActionType::color_clue:
|
|
case ActionType::rank_clue:
|
|
action.type = ActionType::clue;
|
|
break;
|
|
case ActionType::end_game:
|
|
case ActionType::vote_terminate:
|
|
action.type = ActionType::end_game;
|
|
break;
|
|
case ActionType::play:
|
|
case ActionType::discard:
|
|
break;
|
|
default:
|
|
throw std::runtime_error("Invalid game format, could not parse action type " + std::to_string(type));
|
|
}
|
|
return action;
|
|
}
|
|
|
|
void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, Hanabi::Card const &card) {
|
|
jv = {{"suitIndex", card.suit}, {"rank", card.rank}};
|
|
}
|
|
}
|
|
|
|
|
|
std::vector<Hanabi::Card> parse_deck(const boost::json::value& deck_json) {
|
|
auto deck = boost::json::value_to<std::vector<Hanabi::Card>>(deck_json);
|
|
std::array<std::array<Hanabi::rank_t, 5>, 6> next_copy_indices {};
|
|
for(size_t i = 0; i < deck.size(); i++) {
|
|
auto &card = deck[i];
|
|
assert(card.rank < 5);
|
|
assert(card.rank >= 0);
|
|
assert(card.suit < 6);
|
|
assert(card.suit >= 0);
|
|
card.index = i;
|
|
card.copy = next_copy_indices[card.suit][card.rank];
|
|
next_copy_indices[card.suit][card.rank]++;
|
|
}
|
|
return deck;
|
|
}
|
|
|
|
std::vector<Hanabi::Action> parse_actions(const boost::json::value& action_json) {
|
|
return boost::json::value_to<std::vector<Hanabi::Action>>(action_json);
|
|
}
|
|
|
|
boost::json::object download_game_json(int game_id) {
|
|
std::string request_str = "https://hanab.live/export/" + std::to_string(game_id);
|
|
cpr::Response r = cpr::Get(cpr::Url(request_str));
|
|
if (r.header["content-type"] != "application/json; charset=utf-8") {
|
|
return {};
|
|
}
|
|
return boost::json::parse(r.text).as_object();
|
|
}
|
|
|
|
boost::json::object open_game_json(const char* filename) {
|
|
std::ifstream file(filename);
|
|
if (!file.is_open()) {
|
|
std::cout << "Failed to open " << filename << "." << std::endl;
|
|
return {};
|
|
}
|
|
std::string game_json ((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
|
return boost::json::parse(game_json).as_object();
|
|
}
|
|
|
|
void get_game(std::variant<int, const char*> game_spec) {
|
|
const boost::json::object game = [&game_spec](){
|
|
if (game_spec.index() == 0) {
|
|
return download_game_json(std::get<int>(game_spec));
|
|
} else {
|
|
return open_game_json(std::get<const char*>(game_spec));
|
|
}
|
|
}();
|
|
const std::vector<Hanabi::Card> deck = parse_deck(game.at("deck"));
|
|
const std::vector<Hanabi::Action> actions = parse_actions(game.at("actions"));
|
|
const unsigned num_players = game.at("players").as_array().size();
|
|
std::cout << deck.size() << std::endl;
|
|
std::cout << num_players;
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // DYNAMIC_PROGRAM_DOWNLOAD_H
|