expand cli interface
new commands opt and initials
This commit is contained in:
parent
4718698ddc
commit
a95efaea4a
3 changed files with 64 additions and 8 deletions
|
@ -29,7 +29,7 @@ namespace Hanabi {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr static std::array<std::string, 10> cli_commands = {
|
constexpr static std::array<std::string, 12> cli_commands = {
|
||||||
"play",
|
"play",
|
||||||
"clue",
|
"clue",
|
||||||
"discard",
|
"discard",
|
||||||
|
@ -40,6 +40,8 @@ namespace Hanabi {
|
||||||
"evaluate",
|
"evaluate",
|
||||||
"help",
|
"help",
|
||||||
"quit",
|
"quit",
|
||||||
|
"initials",
|
||||||
|
"opt",
|
||||||
};
|
};
|
||||||
|
|
||||||
char * cli_commands_generator(const char *text, int state) {
|
char * cli_commands_generator(const char *text, int state) {
|
||||||
|
@ -61,18 +63,17 @@ namespace Hanabi {
|
||||||
|
|
||||||
|
|
||||||
Card parse_card(std::string card_str) {
|
Card parse_card(std::string card_str) {
|
||||||
if (card_str == "trash") {
|
if (card_str == "trash" or card_str == "kt") {
|
||||||
return Cards::trash;
|
return Cards::trash;
|
||||||
}
|
}
|
||||||
if(card_str.size() != 2) {
|
if(card_str.size() != 2) {
|
||||||
return Cards::unknown;
|
return Cards::unknown;
|
||||||
}
|
}
|
||||||
constexpr std::array<char, 6> color_initials = {'r', 'y', 'g', 'b', 'p', 't'};
|
auto it = std::find(suit_initials.begin(), suit_initials.end(), card_str[0]);
|
||||||
auto it = std::find(color_initials.begin(), color_initials.end(), card_str[0]);
|
if (it == suit_initials.end()) {
|
||||||
if (it == color_initials.end()) {
|
|
||||||
return Cards::unknown;
|
return Cards::unknown;
|
||||||
}
|
}
|
||||||
const suit_t suit = std::distance(color_initials.begin(), it);
|
const suit_t suit = std::distance(suit_initials.begin(), it);
|
||||||
try {
|
try {
|
||||||
const rank_t rank = 5 - std::stoi(card_str.substr(1, 1));
|
const rank_t rank = 5 - std::stoi(card_str.substr(1, 1));
|
||||||
return Card {suit, rank};
|
return Card {suit, rank};
|
||||||
|
@ -98,6 +99,7 @@ namespace Hanabi {
|
||||||
std::cout << " copie(s) in draw) " << probability << std::endl;
|
std::cout << " copie(s) in draw) " << probability << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_trash > 0) {
|
if (num_trash > 0) {
|
||||||
std::cout << Cards::trash << " (" << num_trash << " copie(s) in draw) " << trash_discard_prob << std::endl;
|
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 << "clue: give a clue." << std::endl;
|
||||||
std::cout << "play <card>: play specified card." << std::endl;
|
std::cout << "play <card>: play specified card." << std::endl;
|
||||||
std::cout << "discard: discard trash from hand." << 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 << "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 << "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 << "evaluate: evaluate current game state recursively. Potentially runtime-expensive." << std::endl;
|
||||||
|
@ -164,6 +167,23 @@ namespace Hanabi {
|
||||||
break;
|
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")) {
|
if (prompt.starts_with("state")) {
|
||||||
std::cout << *game << std::endl;
|
std::cout << *game << std::endl;
|
||||||
const std::optional<probability_t> prob = game->lookup();
|
const std::optional<probability_t> prob = game->lookup();
|
||||||
|
@ -259,6 +279,38 @@ namespace Hanabi {
|
||||||
continue;
|
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;
|
std::cout << "Unrecognized command. Type 'help' for a list of available commands." << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,8 @@ namespace Hanabi {
|
||||||
|
|
||||||
// We might want to change these at runtime to adapt to other variants.
|
// 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
|
// 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 {
|
struct Card {
|
||||||
suit_t suit;
|
suit_t suit;
|
||||||
|
|
|
@ -56,7 +56,10 @@ namespace Hanabi {
|
||||||
template<size_t num_suits>
|
template<size_t num_suits>
|
||||||
std::ostream &operator<<(std::ostream &os, const Stacks<num_suits> &stacks) {
|
std::ostream &operator<<(std::ostream &os, const Stacks<num_suits> &stacks) {
|
||||||
for (size_t i = 0; i < stacks.size() - 1; i++) {
|
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();
|
os << starting_card_rank - stacks.back();
|
||||||
return os;
|
return os;
|
||||||
|
|
Loading…
Reference in a new issue