Implement option to list all actions in replay
This commit is contained in:
parent
cdf8575283
commit
35b78cb4db
6 changed files with 84 additions and 26 deletions
|
@ -63,9 +63,15 @@ namespace Hanabi
|
|||
std::variant<std::monostate, clue_t> clue_spec{static_cast<clue_t>(0)};
|
||||
|
||||
/**
|
||||
* If true, then all states corresponding to smaller draw pile sizes
|
||||
* If true, then all states corresponding to smaller draw pile sizes are evaluated as well.
|
||||
*/
|
||||
bool recursive{false};
|
||||
|
||||
/**
|
||||
* If true, lists all possible actions and their win probabilities in the specified
|
||||
* and future game states, including suboptimal ones.
|
||||
*/
|
||||
bool list_actions{false};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace Hanabi
|
|||
|
||||
[[nodiscard]] virtual std::pair<std::vector<std::uint64_t>, std::vector<Card>> dump_unique_id_parts() const = 0;
|
||||
|
||||
virtual std::vector<std::pair<Action, std::optional<probability_t>>> get_reasonable_actions() = 0;
|
||||
virtual std::vector<std::pair<Action, std::optional<probability_t>>> get_reasonable_actions(bool evaluate_all = false) = 0;
|
||||
|
||||
virtual std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>>
|
||||
possible_next_states(hand_index_t index, bool play) = 0;
|
||||
|
@ -110,6 +110,7 @@ namespace Hanabi
|
|||
Game(std::unique_ptr<HanabiStateIF> state, GameInfo game_info);
|
||||
|
||||
[[nodiscard]] unsigned cur_turn() const;
|
||||
[[nodiscard]] unsigned num_turns() const;
|
||||
|
||||
void make_turn();
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ namespace Hanabi
|
|||
|
||||
[[nodiscard]] std::pair<std::vector<std::uint64_t>, std::vector<Card>> dump_unique_id_parts() const final;
|
||||
|
||||
std::vector<std::pair<Action, std::optional<probability_t>>> get_reasonable_actions() final;
|
||||
std::vector<std::pair<Action, std::optional<probability_t>>> get_reasonable_actions(bool evaluate_all = false) final;
|
||||
|
||||
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>>
|
||||
possible_next_states(hand_index_t index, bool play) final;
|
||||
|
|
|
@ -765,7 +765,7 @@ namespace Hanabi
|
|||
|
||||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||
std::vector<std::pair<Action, std::optional<probability_t>>>
|
||||
HanabiState<num_suits, num_players, hand_size>::get_reasonable_actions()
|
||||
HanabiState<num_suits, num_players, hand_size>::get_reasonable_actions(bool evaluate_all)
|
||||
{
|
||||
std::vector<std::pair<Action, std::optional<probability_t>>> reasonable_actions{};
|
||||
|
||||
|
@ -784,9 +784,14 @@ namespace Hanabi
|
|||
bool known = true;
|
||||
probability_t sum_of_probabilities = 0;
|
||||
|
||||
do_for_each_potential_draw(index, true, [this, &sum_of_probabilities
|
||||
do_for_each_potential_draw(index, true, [this, &sum_of_probabilities, &evaluate_all
|
||||
, &known](const unsigned long multiplicity) {
|
||||
const std::optional<probability_t> prob = lookup();
|
||||
std::optional<probability_t> prob;
|
||||
if (evaluate_all) {
|
||||
prob = evaluate_state();
|
||||
} else {
|
||||
prob = lookup();
|
||||
}
|
||||
if (prob.has_value())
|
||||
{
|
||||
sum_of_probabilities += prob.value() * multiplicity;
|
||||
|
@ -796,7 +801,6 @@ namespace Hanabi
|
|||
known = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (known)
|
||||
{
|
||||
const unsigned long total_weight = std::max(static_cast<unsigned long>(_weighted_draw_pile_size), 1ul);
|
||||
|
@ -821,9 +825,14 @@ namespace Hanabi
|
|||
bool known = true;
|
||||
probability_t sum_of_probabilities = 0;
|
||||
|
||||
do_for_each_potential_draw(index, false, [this, &sum_of_probabilities
|
||||
do_for_each_potential_draw(index, false, [this, &sum_of_probabilities, &evaluate_all
|
||||
, &known](const unsigned long multiplicity) {
|
||||
const std::optional<probability_t> prob = lookup();
|
||||
std::optional<probability_t> prob;
|
||||
if (evaluate_all) {
|
||||
prob = evaluate_state();
|
||||
} else {
|
||||
prob = lookup();
|
||||
}
|
||||
if (prob.has_value())
|
||||
{
|
||||
sum_of_probabilities += prob.value() * multiplicity;
|
||||
|
@ -833,7 +842,6 @@ namespace Hanabi
|
|||
known = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (known)
|
||||
{
|
||||
const unsigned long total_weight = std::max(static_cast<unsigned long>(_weighted_draw_pile_size), 1ul);
|
||||
|
@ -854,7 +862,12 @@ namespace Hanabi
|
|||
if (_num_clues >= clue_t(1))
|
||||
{
|
||||
give_clue();
|
||||
const std::optional<probability_t> prob = lookup();
|
||||
std::optional<probability_t> prob;
|
||||
if (evaluate_all) {
|
||||
prob = evaluate_state();
|
||||
} else {
|
||||
prob = lookup();
|
||||
}
|
||||
const Action action = {ActionType::clue, Cards::unknown};
|
||||
reasonable_actions.emplace_back(action, prob);
|
||||
revert_clue();
|
||||
|
@ -1273,20 +1286,23 @@ namespace Hanabi
|
|||
)
|
||||
{
|
||||
// This macro can be activated if we want to dump details on all game states visited for analysis purposes.
|
||||
#define DUMP_STATES
|
||||
#ifdef DUMP_STATES
|
||||
std::cout << *this << std::endl;
|
||||
const auto [id_parts, cards] = dump_unique_id_parts();
|
||||
std::cout << "id is: " << id << ", id parts are: ";
|
||||
for (auto const & part: id_parts) {
|
||||
std::cout << part << " ";
|
||||
if (id == 87476369689) {
|
||||
std::cout << *this << std::endl;
|
||||
const auto [id_parts, cards] = dump_unique_id_parts();
|
||||
std::cout << "id is: " << id << ", id parts are: ";
|
||||
for (auto const & part: id_parts) {
|
||||
std::cout << part << " ";
|
||||
}
|
||||
std::cout << ", encoded cards are ";
|
||||
for (auto const & part: cards) {
|
||||
std::cout << part << " ";
|
||||
}
|
||||
std::cout << ", probability is ";
|
||||
print_probability(std::cout, probability);
|
||||
std::cout << "\n" << std::endl;
|
||||
}
|
||||
std::cout << ", encoded cards are ";
|
||||
for (auto const & part: cards) {
|
||||
std::cout << part << " ";
|
||||
}
|
||||
std::cout << ", probability is ";
|
||||
print_probability(std::cout, probability);
|
||||
std::cout << "\n" << std::endl;
|
||||
#endif
|
||||
if (_position_tablebase.contains(id))
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "state_explorer.h"
|
||||
|
||||
#include "command_line_interface.h"
|
||||
#include "myassert.h"
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
|
@ -121,6 +122,22 @@ namespace Hanabi
|
|||
|
||||
// We are ready to start backtracking now
|
||||
game.state->init_backtracking_information();
|
||||
quiet_os << "Base state specified:\n" << *game.state << std::endl;
|
||||
|
||||
if (parms.list_actions) {
|
||||
unsigned max_turn = game.cur_turn();
|
||||
// Note this loop is safe even for unsigned max_turn, since turn() is at least 1.
|
||||
for (unsigned turn = game.num_turns(); turn >= max_turn; turn--) {
|
||||
bool reached = game.goto_turn(turn);
|
||||
game.state->evaluate_state();
|
||||
ASSERT(reached);
|
||||
for (auto const & [action, probability] : game.state->get_reasonable_actions(true)) {
|
||||
std::cout << "Turn " << turn << ", " << action << ": ";
|
||||
print_probability(std::cout, probability) << std::endl;
|
||||
}
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (parms.recursive)
|
||||
{
|
||||
|
@ -198,7 +215,7 @@ namespace Hanabi
|
|||
cli(game);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::optional<CLIParms> parse_parms(int argc, char *argv[])
|
||||
|
@ -225,6 +242,7 @@ namespace Hanabi
|
|||
("recursive,r", "If specified, also analyzes all game states with fewer cards in the draw pile than the "
|
||||
"specified one, considering smaller draw pile sizes first. Also useful for infinite analysis "
|
||||
"(specifying this with a complex base state")
|
||||
("list-actions,l","List all actions (including suboptimal ones) of all turns after specified state.")
|
||||
("all-clues", "Whenever evaluating a game state, evaluate it with all clue counts and output their "
|
||||
"probabilities.")
|
||||
("quiet,q", "Deactivate all non-essential prints. Useful if output is parsed by another program.");
|
||||
|
@ -247,6 +265,7 @@ namespace Hanabi
|
|||
std::cout << desc << std::endl;
|
||||
std::cout << "You have to either specify --game or --file as a data source.\n";
|
||||
std::cout << "You may not specify both --turn and --draw at the same time.\n";
|
||||
std::cout << "You may not specifiy both --recursive and --list-actions at the same time.\n";
|
||||
std::cout << "If none of them is specified, it is assumed that --draw 5 was given." << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -300,8 +319,16 @@ namespace Hanabi
|
|||
parms.clue_spec = std::monostate();
|
||||
}
|
||||
|
||||
if (vm.count("recursive") + vm.count("list-actions") >= 2)
|
||||
{
|
||||
std::cout << "Conflicting options --recursive and --list-actions specified." << std::endl;
|
||||
std::cout << "Use '--help' to print a help message." << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Parse opt-in bool options
|
||||
parms.recursive = vm.count("recursive") > 0;
|
||||
parms.list_actions = vm.count("list-actions") > 0;
|
||||
parms.quiet = vm.count("quiet") > 0;
|
||||
|
||||
if (parms.recursive and std::holds_alternative<clue_t>(parms.clue_spec) and std::get<clue_t>(parms.clue_spec) != clue_t(0))
|
||||
|
|
|
@ -34,11 +34,19 @@ namespace Hanabi
|
|||
return next_action + 1;
|
||||
}
|
||||
|
||||
unsigned Game::num_turns() const
|
||||
{
|
||||
return actions.size();
|
||||
}
|
||||
|
||||
void Game::make_turn()
|
||||
{
|
||||
ASSERT(next_action < actions.size());
|
||||
Card const next_draw = deck[deck.size() - state->draw_pile_size()];
|
||||
state->rotate_next_draw(next_draw);
|
||||
size_t draw_pile_size = state->draw_pile_size();
|
||||
if (draw_pile_size > 0) {
|
||||
Card const next_draw = deck.at(deck.size() - draw_pile_size);
|
||||
state->rotate_next_draw(next_draw);
|
||||
}
|
||||
Action const & action = actions[next_action];
|
||||
std::uint8_t index;
|
||||
switch (action.type)
|
||||
|
|
Loading…
Reference in a new issue