diff --git a/download.h b/download.h index 37e0382..cb82d7c 100644 --- a/download.h +++ b/download.h @@ -10,108 +10,149 @@ #include "game_state.h" - // 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 = value_to( obj.at( key ) ); +void extract(boost::json::object const &obj, T &t, std::string_view key) { + t = 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(); - extract(obj, card.rank, "rank"); - extract(obj, card.suit, "suitIndex"); - card.rank = 5 - card.rank; - return card; - } + Card tag_invoke(boost::json::value_to_tag, + 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, boost::json::value const &jv) { - Hanabi::Action action{}; - uint8_t type; - boost::json::object const &obj = jv.as_object(); + void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, Hanabi::Card const &card) { + jv = {{"suitIndex", card.suit}, + {"rank", card.rank}}; + } +} - extract(obj, action.target, "target"); - extract(obj, type, "type"); +namespace Download { - action.type = static_cast(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)); + struct Action { + Hanabi::ActionType type{}; + uint8_t target; + }; + + Action tag_invoke(boost::json::value_to_tag, boost::json::value const &jv) { + 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(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: + 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::vector 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); + } + return deck; + } + + std::vector parse_actions(const boost::json::value &action_json) { + return boost::json::value_to>(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(file)), std::istreambuf_iterator()); + return boost::json::parse(game_json).as_object(); + } + + template + Hanabi::HanabiState produce_state( + const std::vector& deck, + const std::vector& actions, + size_t num_turns_to_replicate + ) { + Hanabi::HanabiState game(deck); + std::uint8_t index; + for (size_t i = 0; i < num_turns_to_replicate; i++) { + switch(actions[i].type) { + case Hanabi::ActionType::color_clue: + case Hanabi::ActionType::rank_clue: + game.clue(); + break; + case Hanabi::ActionType::discard: + index = game.find_card_in_hand(deck[actions[i].target]); + assert(index != -1); + game.discard(index); + break; + case Hanabi::ActionType::play: + index = game.find_card_in_hand(deck[actions[i].target]); + assert(index != -1); + game.play(index); + break; + case Hanabi::ActionType::vote_terminate: + case Hanabi::ActionType::end_game: + return game; + } } - 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 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); + return game; } - return deck; -} -std::vector parse_actions(const boost::json::value& action_json) { - return boost::json::value_to>(action_json); -} + void get_game(std::variant game_spec) { + const boost::json::object game_json = [&game_spec]() { + if (game_spec.index() == 0) { + return download_game_json(std::get(game_spec)); + } else { + return open_game_json(std::get(game_spec)); + } + }(); + const std::vector deck = parse_deck(game_json.at("deck")); + const std::vector actions = parse_actions(game_json.at("actions")); + const unsigned num_players = game_json.at("players").as_array().size(); + std::cout << deck.size() << std::endl; + std::cout << num_players; -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(file)), std::istreambuf_iterator()); - return boost::json::parse(game_json).as_object(); -} - -void get_game(std::variant game_spec) { - const boost::json::object game = [&game_spec](){ - if (game_spec.index() == 0) { - return download_game_json(std::get(game_spec)); - } else { - return open_game_json(std::get(game_spec)); + auto game = produce_state<6, 3, 5, 0>(deck, actions, 40); + std::cout << game << std::endl; } - }(); - const std::vector deck = parse_deck(game.at("deck")); - const std::vector 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; -} - +} // namespacen Download #endif // DYNAMIC_PROGRAM_DOWNLOAD_H diff --git a/game_state.h b/game_state.h index 25105a3..29d9baf 100644 --- a/game_state.h +++ b/game_state.h @@ -43,7 +43,6 @@ struct Card { suit_t suit; rank_t rank; uint8_t copy; - uint8_t index; // index of card in the deck Card &operator++(); Card successor() const; @@ -53,7 +52,7 @@ struct Card { }; std::ostream &operator<<(std::ostream &os, const Card &card) { - os << suit_initials[card.suit] << +card.rank; + os << suit_initials[card.suit] << 5 - card.rank; return os; } @@ -112,14 +111,6 @@ enum class ActionType { vote_terminate = 10, }; -/** - * Action type for replay format of hanab.live - */ -struct Action { - ActionType type{}; - uint8_t target; -}; - struct BacktrackAction { ActionType type{}; Card discarded{}; @@ -142,6 +133,8 @@ public: BacktrackAction discard(std::uint8_t index); + std::uint8_t find_card_in_hand(const Card& card) const; + void revert(const BacktrackAction &action); void draw(std::uint8_t index); @@ -156,7 +149,7 @@ public: clue_t _num_clues{}; std::uint8_t _draw_pile_size{}; Stacks _stacks{}; - std::array, num_players> _hands{}; + std::array, num_players> _hands{}; CardPositions _card_positions{}; std::list _draw_pile{}; diff --git a/game_state.hpp b/game_state.hpp index 72cc186..12ba3e1 100644 --- a/game_state.hpp +++ b/game_state.hpp @@ -127,6 +127,17 @@ namespace Hanabi { return ret; } + template + std::uint8_t HanabiState::find_card_in_hand( + const Hanabi::Card &card) const { + for(std::uint8_t i = 0; i < hand_size; i++) { + if(_hands[_turn][i].rank == card.rank && _hands[_turn][i].suit == card.suit) { + return i; + } + } + return -1; + } + template std::ostream &operator<<(std::ostream &os, const HanabiState hanabi_state) { os << "Stacks: " << hanabi_state._stacks << std::endl; diff --git a/main.cpp b/main.cpp index f9a8c83..77d4f5f 100644 --- a/main.cpp +++ b/main.cpp @@ -38,7 +38,7 @@ void test_game() { assert(state == state2); } -void download() { get_game("314159.json"); } +void download() { Download::get_game("1004116.json"); } void print_sizes() { std::cout << "size of card -> hand map: " << sizeof(HanabiState<5, 3, 4, 5>) @@ -54,7 +54,7 @@ void print_sizes() { } int main() { - Hanabi::test_game(); +// Hanabi::test_game(); Hanabi::download(); return 0;