From a95efaea4ac3a4ab0f8b9875eb10c6d1d8b84927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Sat, 12 Aug 2023 10:19:04 +0200 Subject: [PATCH] expand cli interface new commands opt and initials --- cli_interface.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++----- game_state.h | 3 ++- game_state.hpp | 5 +++- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/cli_interface.cpp b/cli_interface.cpp index 7040408..1e768e5 100644 --- a/cli_interface.cpp +++ b/cli_interface.cpp @@ -29,7 +29,7 @@ namespace Hanabi { return ret; } - constexpr static std::array cli_commands = { + constexpr static std::array cli_commands = { "play", "clue", "discard", @@ -40,6 +40,8 @@ namespace Hanabi { "evaluate", "help", "quit", + "initials", + "opt", }; char * cli_commands_generator(const char *text, int state) { @@ -61,18 +63,17 @@ namespace Hanabi { Card parse_card(std::string card_str) { - if (card_str == "trash") { + if (card_str == "trash" or card_str == "kt") { return Cards::trash; } if(card_str.size() != 2) { return Cards::unknown; } - constexpr std::array color_initials = {'r', 'y', 'g', 'b', 'p', 't'}; - auto it = std::find(color_initials.begin(), color_initials.end(), card_str[0]); - if (it == color_initials.end()) { + auto it = std::find(suit_initials.begin(), suit_initials.end(), card_str[0]); + if (it == suit_initials.end()) { return Cards::unknown; } - const suit_t suit = std::distance(color_initials.begin(), it); + const suit_t suit = std::distance(suit_initials.begin(), it); try { const rank_t rank = 5 - std::stoi(card_str.substr(1, 1)); return Card {suit, rank}; @@ -98,6 +99,7 @@ namespace Hanabi { std::cout << " copie(s) in draw) " << probability << std::endl; } } + if (num_trash > 0) { std::cout << Cards::trash << " (" << num_trash << " copie(s) in draw) " << trash_discard_prob << std::endl; } @@ -150,6 +152,7 @@ namespace Hanabi { 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 last turn of game." << 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; @@ -164,6 +167,23 @@ namespace Hanabi { break; } + if (prompt.starts_with("initials")) { + if (prompt.length() < 12) { + std::cout << "At least 3 initials need to be specified" << std::endl; + continue; + } + const std::string new_initials = prompt.substr(9); + for(size_t i = 0; i < std::min(size_t(6), new_initials.length()); i++) { + suit_initials[i] = new_initials[i]; + } + std::cout << "Updated initials to "; + for(const char c: suit_initials) { + std::cout << c; + } + std::cout << std::endl; + continue; + } + if (prompt.starts_with("state")) { std::cout << *game << std::endl; const std::optional prob = game->lookup(); @@ -259,6 +279,38 @@ namespace Hanabi { continue; } + if (prompt.starts_with("opt")) { + const auto reasonable_actions = game->get_reasonable_actions(); + if(reasonable_actions.empty()) { + std::cout << "Game is over, no actions to take." << std::endl; + continue; + } + Action best_action; + std::optional best_probability; + for (const auto &[action, probability] : game->get_reasonable_actions()) { + if (!best_probability.has_value() or (probability.has_value() and probability.value() > best_probability.value())) { + best_action = action; + best_probability = probability; + } + } + switch(best_action.type) { + case ActionType::play: + std::cout << "Playing " << best_action.card << std::endl; + game->play(game->find_card_in_hand(best_action.card)); + break; + case ActionType::discard: + std::cout << "Discarding" << std::endl; + game->discard(game->find_card_in_hand(best_action.card)); + break; + case ActionType::clue: + std::cout << "Giving a clue" << std::endl; + game->give_clue(); + break; + default: + break; + } + } + std::cout << "Unrecognized command. Type 'help' for a list of available commands." << std::endl; } } diff --git a/game_state.h b/game_state.h index 522471b..9270709 100644 --- a/game_state.h +++ b/game_state.h @@ -43,7 +43,8 @@ namespace Hanabi { // We might want to change these at runtime to adapt to other variants. // However, a global variable is used so that we can have an output operator for cards reading from here - static std::array suit_initials = {'r', 'y', 'g', 'b', 'p', 't'}; + // Note that this is therefore not static so that we have external linking + inline std::array suit_initials = {'r', 'y', 'g', 'b', 'p', 't'}; struct Card { suit_t suit; diff --git a/game_state.hpp b/game_state.hpp index f780304..e7548d4 100644 --- a/game_state.hpp +++ b/game_state.hpp @@ -56,7 +56,10 @@ namespace Hanabi { template std::ostream &operator<<(std::ostream &os, const Stacks &stacks) { for (size_t i = 0; i < stacks.size() - 1; i++) { - os << starting_card_rank - stacks[i] << ", "; + os << starting_card_rank - stacks[i]; + if(i < stacks.size() - 1) { + os << ", "; + } } os << starting_card_rank - stacks.back(); return os;