add history and command completion to readline
This commit is contained in:
parent
74fe5513da
commit
4718698ddc
2 changed files with 70 additions and 10 deletions
|
@ -1,5 +1,6 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <readline/readline.h>
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -28,6 +29,37 @@ namespace Hanabi {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr static std::array<std::string, 10> cli_commands = {
|
||||||
|
"play",
|
||||||
|
"clue",
|
||||||
|
"discard",
|
||||||
|
"id",
|
||||||
|
"state",
|
||||||
|
"revert",
|
||||||
|
"actions",
|
||||||
|
"evaluate",
|
||||||
|
"help",
|
||||||
|
"quit",
|
||||||
|
};
|
||||||
|
|
||||||
|
char * cli_commands_generator(const char *text, int state) {
|
||||||
|
std::string text_str (text);
|
||||||
|
for(auto& command : cli_commands) {
|
||||||
|
if (command.starts_with(text_str) && state-- <= 0) {
|
||||||
|
return strdup(command.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **
|
||||||
|
cli_command_completion(const char *text, int start, int end)
|
||||||
|
{
|
||||||
|
rl_attempted_completion_over = 1;
|
||||||
|
return rl_completion_matches(text, cli_commands_generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Card parse_card(std::string card_str) {
|
Card parse_card(std::string card_str) {
|
||||||
if (card_str == "trash") {
|
if (card_str == "trash") {
|
||||||
return Cards::trash;
|
return Cards::trash;
|
||||||
|
@ -56,16 +88,18 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
std::cout << "Choose drawn card: " << std::endl;
|
std::cout << "Choose drawn card: " << std::endl;
|
||||||
unsigned num_trash = 0;
|
unsigned num_trash = 0;
|
||||||
|
std::optional<probability_t> trash_discard_prob = 0;
|
||||||
for(const auto &[card_multiplicity, probability]: next_states) {
|
for(const auto &[card_multiplicity, probability]: next_states) {
|
||||||
if (game->is_trash(card_multiplicity.card)) {
|
if (game->is_trash(card_multiplicity.card)) {
|
||||||
num_trash += card_multiplicity.multiplicity;
|
num_trash += card_multiplicity.multiplicity;
|
||||||
|
trash_discard_prob = probability;
|
||||||
} else {
|
} else {
|
||||||
std::cout << card_multiplicity.card << " (" << card_multiplicity.multiplicity;
|
std::cout << card_multiplicity.card << " (" << card_multiplicity.multiplicity;
|
||||||
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" << std::endl;
|
std::cout << Cards::trash << " (" << num_trash << " copie(s) in draw) " << trash_discard_prob << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream prompt;
|
std::stringstream prompt;
|
||||||
|
@ -103,22 +137,33 @@ namespace Hanabi {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void cli(const std::shared_ptr<HanabiStateIF>& game) {
|
void cli(const std::shared_ptr<HanabiStateIF>& game) {
|
||||||
|
rl_attempted_completion_function = cli_command_completion;
|
||||||
|
using_history();
|
||||||
|
unsigned depth = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
const std::string prompt = read_line_memory_safe("> ");
|
const std::string prompt = read_line_memory_safe("> ");
|
||||||
|
add_history(prompt.c_str());
|
||||||
|
|
||||||
if (prompt.starts_with("help")) {
|
if (prompt.starts_with("help")) {
|
||||||
std::cout << "state: print information on current game state." << std::endl;
|
std::cout << "state: print information on current game state." << std::endl;
|
||||||
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 << "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 << "id: display id of state. Has no inherent meaning, useful for debugging." << 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;
|
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;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prompt.starts_with("quit")) {
|
||||||
|
std::cout << "Quitting." << std::endl;
|
||||||
|
clear_history();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
@ -130,11 +175,17 @@ namespace Hanabi {
|
||||||
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->evaluate_state();
|
||||||
std::cout << "Evaluated state." << std::endl;
|
std::cout << "Evaluated state." << std::endl;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("revert")) {
|
if (prompt.starts_with("revert")) {
|
||||||
|
if (depth == 0) {
|
||||||
|
std::cout << "Cannot revert more than base state." << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
std::cout << "Reverting one turn" << std::endl;
|
std::cout << "Reverting one turn" << std::endl;
|
||||||
game->revert();
|
game->revert();
|
||||||
|
depth--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +198,7 @@ namespace Hanabi {
|
||||||
const Card card = parse_card(prompt.substr(5,2));
|
const Card card = parse_card(prompt.substr(5,2));
|
||||||
if (prompt.length() < 7) {
|
if (prompt.length() < 7) {
|
||||||
std::cout << "No card specified." << std::endl;
|
std::cout << "No card specified." << std::endl;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (card == Cards::unknown) {
|
if (card == Cards::unknown) {
|
||||||
std::cout << "Could not parse card " << prompt.substr(5,2) << std::endl;
|
std::cout << "Could not parse card " << prompt.substr(5,2) << std::endl;
|
||||||
|
@ -155,11 +207,13 @@ namespace Hanabi {
|
||||||
const hand_index_t index = game->find_card_in_hand(card);
|
const hand_index_t index = game->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;
|
||||||
}
|
}
|
||||||
if (!ask_for_card_and_rotate_draw(game, index, true)) {
|
if (!ask_for_card_and_rotate_draw(game, index, true)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
game->play(index);
|
game->play(index);
|
||||||
|
depth++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,11 +238,17 @@ namespace Hanabi {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
game->discard(trash_index);
|
game->discard(trash_index);
|
||||||
|
depth++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("clue")) {
|
if (prompt.starts_with("clue")) {
|
||||||
|
if (game->num_clues() == 0) {
|
||||||
|
std::cout << "You cannot give a clue at 0 clues." << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
game->give_clue();
|
game->give_clue();
|
||||||
|
depth++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
|
|
||||||
namespace Hanabi {
|
namespace Hanabi {
|
||||||
[[noreturn]] [[noreturn]] void cli(const std::shared_ptr<HanabiStateIF>& game);
|
void cli(const std::shared_ptr<HanabiStateIF>& game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue