expand cli interface

new commands opt and initials
This commit is contained in:
Maximilian Keßler 2023-08-12 10:19:04 +02:00
parent 4718698ddc
commit a95efaea4a
Signed by: max
GPG key ID: BCC5A619923C0BA5
3 changed files with 64 additions and 8 deletions

View file

@ -29,7 +29,7 @@ namespace Hanabi {
return ret;
}
constexpr static std::array<std::string, 10> cli_commands = {
constexpr static std::array<std::string, 12> 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<char, 6> 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 <card>: 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<probability_t> 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<probability_t> 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;
}
}

View file

@ -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<char, 6> suit_initials = {'r', 'y', 'g', 'b', 'p', 't'};
// Note that this is therefore not static so that we have external linking
inline std::array<char, 6> suit_initials = {'r', 'y', 'g', 'b', 'p', 't'};
struct Card {
suit_t suit;

View file

@ -56,7 +56,10 @@ namespace Hanabi {
template<size_t num_suits>
std::ostream &operator<<(std::ostream &os, const Stacks<num_suits> &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;