From b1c1f41c6e91f88a0748810216aa17b554f7c0dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Sat, 12 Aug 2023 18:48:01 +0200 Subject: [PATCH] add cli command to dump id parts --- include/game_state.h | 7 ++++-- include/game_state.hpp | 49 ++++++++++++++++++++++++++++++++++++++++++ src/cli_interface.cpp | 46 ++++++++++++++++++++++++++------------- 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/include/game_state.h b/include/game_state.h index 9270709..e379f15 100644 --- a/include/game_state.h +++ b/include/game_state.h @@ -220,6 +220,8 @@ public: [[nodiscard]] virtual std::optional lookup() const = 0; [[nodiscard]] virtual std::uint64_t unique_id() const = 0; + [[nodiscard]] virtual std::pair, std::vector> dump_unique_id_parts() const = 0; + virtual std::vector>> get_reasonable_actions() = 0; virtual std::vector>> possible_next_states(hand_index_t index, bool play) = 0; @@ -264,8 +266,9 @@ public: void init_backtracking_information() final; probability_t evaluate_state() final; - std::optional lookup() const; - std::uint64_t unique_id() const final; + [[nodiscard]] std::optional lookup() const final; + [[nodiscard]] std::uint64_t unique_id() const final; + [[nodiscard]] std::pair, std::vector> dump_unique_id_parts() const final; std::vector>> get_reasonable_actions() final; std::vector>> possible_next_states(hand_index_t index, bool play) final; diff --git a/include/game_state.hpp b/include/game_state.hpp index 9083158..00bceb7 100644 --- a/include/game_state.hpp +++ b/include/game_state.hpp @@ -816,6 +816,55 @@ namespace Hanabi { return id; } + template + std::pair, std::vector> HanabiState::dump_unique_id_parts() const { + std::vector ret; + std::vector cards; + + // encode all positions of cards that started in draw pile + ASSERT(_relative_representation.card_positions_draw.size() == _relative_representation.good_cards_draw.size()); + for(size_t i = 0; i < _relative_representation.card_positions_draw.size(); i++) { + for(player_t player : _relative_representation.card_positions_draw[i]) { + // We normalize here: If a card is already played, then the positions of its other copies + // do not matter, so we can just pretend that they are all in the trash already. + // The resulting states will be equivalent. + if (!is_trash(_relative_representation.good_cards_draw[i])) { + ret.push_back(player); + } else { + ret.push_back(trash_or_play_stack); + } + cards.push_back(_relative_representation.good_cards_draw[i]); + } + } + + // encode number of clues + ret.push_back(_num_clues); + + // we can encode draw pile size and extra turn in one metric, since we only have extra turns if draw pile is empty + const std::uint8_t draw_pile_size_and_extra_turns = [this]() -> uint8_t { + if(_endgame_turns_left == no_endgame) { + return _weighted_draw_pile_size + num_players; + } + else { + return _endgame_turns_left; + } + }(); + + ret.push_back(draw_pile_size_and_extra_turns); + + // encode positions of cards that started in hands + ret.push_back(_relative_representation.card_positions_hands.to_ulong()); + + ret.push_back(_turn); + + // The id is unique now, since for all relevant cards, we know their position (including if they are played), + // the number of clues, the draw pile size and whose turn it is. + // This already uniquely determines the current players position, assuming that we never discard good cards + // (and only play them) + + return {ret, cards}; + } + template const std::unordered_map& HanabiState::position_tablebase() const { return _position_tablebase; diff --git a/src/cli_interface.cpp b/src/cli_interface.cpp index d130376..a16def9 100644 --- a/src/cli_interface.cpp +++ b/src/cli_interface.cpp @@ -29,19 +29,20 @@ namespace Hanabi { return ret; } - constexpr static std::array cli_commands = { + constexpr static std::array cli_commands = { "play", "clue", "discard", - "id", + "opt", "state", + "id", "revert", "actions", "evaluate", "help", "quit", - "initials", - "opt", + "set-initials", + "dump-id-parts", }; char * cli_commands_generator(const char *text, int state) { @@ -152,16 +153,31 @@ namespace Hanabi { add_history(prompt.c_str()); if (prompt.starts_with("help")) { - std::cout << "state: print information on current game state." << std::endl; - std::cout << "clue: give a clue." << std::endl; - std::cout << "play : play specified card." << std::endl; - std::cout << "discard: discard trash from hand." << std::endl; - std::cout << "opt: take optimal action. In case of ties, prefers plays and discards in that order." << std::endl; - std::cout << "revert : revert specified number of turns (default 1)." << std::endl; - std::cout << "actions: display list of reasonable actions to take and their winning chances." << std::endl; - std::cout << "evaluate: evaluate current game state recursively. Potentially runtime-expensive." << std::endl; - std::cout << "id: display id of state. Has no inherent meaning, useful for debugging." << std::endl; - std::cout << "quit: Quit this interactive shell." << std::endl; + std::cout << "state: print information on current game state." << std::endl; + std::cout << "clue: give a clue." << std::endl; + std::cout << "play : play specified card." << std::endl; + std::cout << "discard: discard trash from hand." << std::endl; + std::cout << "opt: take optimal action. In case of ties, prefers plays and discards in that order." << std::endl; + std::cout << "revert : revert specified number of turns (default 1)." << std::endl; + std::cout << "actions: display list of reasonable actions to take and their winning chances." << std::endl; + std::cout << "evaluate: evaluate current game state recursively. Potentially runtime-expensive." << std::endl; + std::cout << "set-initials : Set initials for the suits." << std::endl; + std::cout << "quit: Quit this interactive shell." << std::endl; + std::cout << "id: display id of state. Has no inherent meaning, useful for debugging." << std::endl; + std::cout << "dump-id-parts: Dump parts used to calculate the id of the state as well as the cards associated to them." << std::endl; + std::cout << "help: Display this help message." << std::endl; + continue; + } + + if (prompt.starts_with("dump-id-parts")) { + for (const auto val: game->dump_unique_id_parts().first) { + std::cout << val << ", "; + } + std::cout << std::endl; + for (const auto card: game->dump_unique_id_parts().second) { + std::cout << card << " "; + } + std::cout << std::endl; continue; } @@ -171,7 +187,7 @@ namespace Hanabi { break; } - if (prompt.starts_with("initials")) { + if (prompt.starts_with("set-initials")) { if (prompt.length() < 12) { std::cout << "At least 3 initials need to be specified" << std::endl; continue;