better interface for cli
This commit is contained in:
parent
7dc9d41aa7
commit
9887d104b8
3 changed files with 36 additions and 37 deletions
|
@ -5,7 +5,7 @@
|
||||||
#include "game_interface.h"
|
#include "game_interface.h"
|
||||||
|
|
||||||
namespace Hanabi {
|
namespace Hanabi {
|
||||||
void cli(const std::shared_ptr<HanabiStateIF>& game);
|
void cli(Game const & game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -173,8 +173,7 @@ namespace Hanabi {
|
||||||
if (parms.interactive.value_or(!parms.quiet))
|
if (parms.interactive.value_or(!parms.quiet))
|
||||||
{
|
{
|
||||||
quiet_os << "\nDropping into interactive command line to explore result (type 'help'):" << std::endl;
|
quiet_os << "\nDropping into interactive command line to explore result (type 'help'):" << std::endl;
|
||||||
auto game_shared = std::shared_ptr<HanabiStateIF>(game.state.release());
|
cli(game);
|
||||||
cli(game_shared);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -89,8 +89,8 @@ namespace Hanabi {
|
||||||
return static_cast<int>(std::ceil(std::log10(probability)));
|
return static_cast<int>(std::ceil(std::log10(probability)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ask_for_card_and_rotate_draw(const std::shared_ptr<HanabiStateIF>& game, hand_index_t index, bool play) {
|
bool ask_for_card_and_rotate_draw(HanabiStateIF & state, hand_index_t index, bool play) {
|
||||||
const auto next_states = game->possible_next_states(index, play);
|
const auto next_states = state.possible_next_states(index, play);
|
||||||
|
|
||||||
if (next_states.size() <= 1) {
|
if (next_states.size() <= 1) {
|
||||||
// No need to ask for anything if draw contains only one possible type of card
|
// No need to ask for anything if draw contains only one possible type of card
|
||||||
|
@ -104,7 +104,7 @@ namespace Hanabi {
|
||||||
int max_rational_digit_len = 0;
|
int max_rational_digit_len = 0;
|
||||||
for(const auto &[card_multiplicity, probability]: next_states) {
|
for(const auto &[card_multiplicity, probability]: next_states) {
|
||||||
// If the card is played, we can treat it as a trash draw as well
|
// If the card is played, we can treat it as a trash draw as well
|
||||||
if (game->is_trash(card_multiplicity.card) or (play and game->cur_hand()[index] == card_multiplicity.card)) {
|
if (state.is_trash(card_multiplicity.card) or (play and state.cur_hand()[index] == card_multiplicity.card)) {
|
||||||
states_to_show.front().first.multiplicity += card_multiplicity.multiplicity;
|
states_to_show.front().first.multiplicity += card_multiplicity.multiplicity;
|
||||||
states_to_show.front().second = probability;
|
states_to_show.front().second = probability;
|
||||||
} else {
|
} else {
|
||||||
|
@ -147,14 +147,14 @@ namespace Hanabi {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto selected_draw_it = std::find_if(next_states.begin(), next_states.end(), [&drawn_card, &game](const std::pair<CardMultiplicity, std::optional<probability_t>>& pair) {
|
auto selected_draw_it = std::find_if(next_states.begin(), next_states.end(), [&drawn_card, &state](const std::pair<CardMultiplicity, std::optional<probability_t>>& pair) {
|
||||||
return (game->is_trash(pair.first.card) and drawn_card == Cards::trash) or pair.first.card == drawn_card;
|
return (state.is_trash(pair.first.card) and drawn_card == Cards::trash) or pair.first.card == drawn_card;
|
||||||
});
|
});
|
||||||
if (selected_draw_it == next_states.end()){
|
if (selected_draw_it == next_states.end()){
|
||||||
std::cout << "That card is not in the draw pile, aborting." << std::endl;
|
std::cout << "That card is not in the draw pile, aborting." << std::endl;
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
game->rotate_next_draw(selected_draw_it->first.card);
|
state.rotate_next_draw(selected_draw_it->first.card);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ namespace Hanabi {
|
||||||
std::cout << "Use 'quit' to exit the interactive shell." << std::endl << "> ";
|
std::cout << "Use 'quit' to exit the interactive shell." << std::endl << "> ";
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli(const std::shared_ptr<HanabiStateIF>& game) {
|
void cli(Game const & game) {
|
||||||
std::signal(SIGINT, signal_handler);
|
std::signal(SIGINT, signal_handler);
|
||||||
// Set up GNU readline
|
// Set up GNU readline
|
||||||
rl_attempted_completion_function = cli_command_completion;
|
rl_attempted_completion_function = cli_command_completion;
|
||||||
|
@ -193,11 +193,11 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("dump-id-parts")) {
|
if (prompt.starts_with("dump-id-parts")) {
|
||||||
for (const auto val: game->dump_unique_id_parts().first) {
|
for (const auto val: game.state->dump_unique_id_parts().first) {
|
||||||
std::cout << val << ", ";
|
std::cout << val << ", ";
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
for (const auto card: game->dump_unique_id_parts().second) {
|
for (const auto card: game.state->dump_unique_id_parts().second) {
|
||||||
std::cout << card << " ";
|
std::cout << card << " ";
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
@ -229,8 +229,8 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("state")) {
|
if (prompt.starts_with("state")) {
|
||||||
std::cout << *game << std::endl;
|
std::cout << *game.state << std::endl;
|
||||||
const std::optional<probability_t> prob = game->lookup();
|
const std::optional<probability_t> prob = game.state->lookup();
|
||||||
std::cout << "Winning chance: ";
|
std::cout << "Winning chance: ";
|
||||||
print_probability(std::cout, prob) << std::endl;
|
print_probability(std::cout, prob) << std::endl;
|
||||||
continue;
|
continue;
|
||||||
|
@ -238,7 +238,7 @@ namespace Hanabi {
|
||||||
|
|
||||||
if (prompt.starts_with("evaluate")) {
|
if (prompt.starts_with("evaluate")) {
|
||||||
std::cout << "Evaluating current game state, this might take a while." << std::endl;
|
std::cout << "Evaluating current game state, this might take a while." << std::endl;
|
||||||
game->evaluate_state();
|
game.state->evaluate_state();
|
||||||
std::cout << "Evaluated state." << std::endl;
|
std::cout << "Evaluated state." << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -263,14 +263,14 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
std::cout << "Reverting " << turns_to_revert << " turn(s)." << std::endl;
|
std::cout << "Reverting " << turns_to_revert << " turn(s)." << std::endl;
|
||||||
while(turns_to_revert--) {
|
while(turns_to_revert--) {
|
||||||
game->revert();
|
game.state->revert();
|
||||||
depth--;
|
depth--;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("id")) {
|
if (prompt.starts_with("id")) {
|
||||||
std::cout << game->unique_id() << std::endl;
|
std::cout << game.state->unique_id() << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,24 +284,24 @@ namespace Hanabi {
|
||||||
std::cout << "Could not parse card " << prompt.substr(5,2) << std::endl;
|
std::cout << "Could not parse card " << prompt.substr(5,2) << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const hand_index_t index = game->find_card_in_hand(card);
|
const hand_index_t index = game.state->find_card_in_hand(card);
|
||||||
if (index == hand_index_t(-1)) {
|
if (index == hand_index_t(-1)) {
|
||||||
std::cout << "This card is not in the current players hand, aborting." << std::endl;
|
std::cout << "This card is not in the current players hand, aborting." << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!ask_for_card_and_rotate_draw(game, index, true)) {
|
if (!ask_for_card_and_rotate_draw(*game.state, index, true)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
game->play(index);
|
game.state->play(index);
|
||||||
depth++;
|
depth++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("discard")) {
|
if (prompt.starts_with("discard")) {
|
||||||
const auto hand = game->cur_hand();
|
const auto hand = game.state->cur_hand();
|
||||||
hand_index_t trash_index = invalid_hand_idx;
|
hand_index_t trash_index = invalid_hand_idx;
|
||||||
for(hand_index_t index = 0; index < hand.size(); index++) {
|
for(hand_index_t index = 0; index < hand.size(); index++) {
|
||||||
if (game->is_trash(hand[index])) {
|
if (game.state->is_trash(hand[index])) {
|
||||||
trash_index = index;
|
trash_index = index;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -310,30 +310,30 @@ namespace Hanabi {
|
||||||
std::cout << "No trash in hand found, discarding not supported." << std::endl;
|
std::cout << "No trash in hand found, discarding not supported." << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (game->num_clues() == max_num_clues) {
|
if (game.state->num_clues() == max_num_clues) {
|
||||||
std::cout << "You cannot discard at " << max_num_clues << " clues." << std::endl;
|
std::cout << "You cannot discard at " << max_num_clues << " clues." << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!ask_for_card_and_rotate_draw(game, trash_index, false)) {
|
if (!ask_for_card_and_rotate_draw(*game.state, trash_index, false)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
game->discard(trash_index);
|
game.state->discard(trash_index);
|
||||||
depth++;
|
depth++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("clue")) {
|
if (prompt.starts_with("clue")) {
|
||||||
if (game->num_clues() == 0) {
|
if (game.state->num_clues() == 0) {
|
||||||
std::cout << "You cannot give a clue at 0 clues." << std::endl;
|
std::cout << "You cannot give a clue at 0 clues." << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
game->give_clue();
|
game.state->give_clue();
|
||||||
depth++;
|
depth++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("actions")) {
|
if (prompt.starts_with("actions")) {
|
||||||
auto reasonable_actions = game->get_reasonable_actions();
|
auto reasonable_actions = game.state->get_reasonable_actions();
|
||||||
int max_rational_digit_len = std::accumulate(
|
int max_rational_digit_len = std::accumulate(
|
||||||
reasonable_actions.begin(),
|
reasonable_actions.begin(),
|
||||||
reasonable_actions.end(),
|
reasonable_actions.end(),
|
||||||
|
@ -356,14 +356,14 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("opt")) {
|
if (prompt.starts_with("opt")) {
|
||||||
const auto reasonable_actions = game->get_reasonable_actions();
|
const auto reasonable_actions = game.state->get_reasonable_actions();
|
||||||
if(reasonable_actions.empty()) {
|
if(reasonable_actions.empty()) {
|
||||||
std::cout << "Game is over, no actions to take." << std::endl;
|
std::cout << "Game is over, no actions to take." << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Action best_action;
|
Action best_action;
|
||||||
std::optional<probability_t> best_probability;
|
std::optional<probability_t> best_probability;
|
||||||
for (const auto &[action, probability] : game->get_reasonable_actions()) {
|
for (const auto &[action, probability] : game.state->get_reasonable_actions()) {
|
||||||
if (!best_probability.has_value() or (probability.has_value() and probability.value() > best_probability.value())) {
|
if (!best_probability.has_value() or (probability.has_value() and probability.value() > best_probability.value())) {
|
||||||
best_action = action;
|
best_action = action;
|
||||||
best_probability = probability;
|
best_probability = probability;
|
||||||
|
@ -374,23 +374,23 @@ namespace Hanabi {
|
||||||
switch(best_action.type) {
|
switch(best_action.type) {
|
||||||
case ActionType::play:
|
case ActionType::play:
|
||||||
std::cout << "Playing " << best_action.card << std::endl;
|
std::cout << "Playing " << best_action.card << std::endl;
|
||||||
index = game->find_card_in_hand(best_action.card);
|
index = game.state->find_card_in_hand(best_action.card);
|
||||||
if(!ask_for_card_and_rotate_draw(game,index,true)) {
|
if(!ask_for_card_and_rotate_draw(*game.state,index,true)) {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
game->play(game->find_card_in_hand(best_action.card));
|
game.state->play(game.state->find_card_in_hand(best_action.card));
|
||||||
break;
|
break;
|
||||||
case ActionType::discard:
|
case ActionType::discard:
|
||||||
std::cout << "Discarding" << std::endl;
|
std::cout << "Discarding" << std::endl;
|
||||||
index = game->find_card_in_hand(best_action.card);
|
index = game.state->find_card_in_hand(best_action.card);
|
||||||
if(!ask_for_card_and_rotate_draw(game, index, false)) {
|
if(!ask_for_card_and_rotate_draw(*game.state, index, false)) {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
game->discard(game->find_card_in_hand(best_action.card));
|
game.state->discard(game.state->find_card_in_hand(best_action.card));
|
||||||
break;
|
break;
|
||||||
case ActionType::clue:
|
case ActionType::clue:
|
||||||
std::cout << "Giving a clue" << std::endl;
|
std::cout << "Giving a clue" << std::endl;
|
||||||
game->give_clue();
|
game.state->give_clue();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue