CLI: sort by probability when asking for draw

Also better alignment of output in list of drawable cards
This commit is contained in:
Maximilian Keßler 2023-09-26 22:26:36 +02:00
parent 129f8ceaec
commit 3fd64bc70f
Signed by: max
GPG key ID: BCC5A619923C0BA5

View file

@ -1,6 +1,9 @@
#include <cstdio> #include <cstdio>
#include <cmath>
#include <csignal> #include <csignal>
#include <iomanip>
#include <readline/readline.h> #include <readline/readline.h>
#include <ranges>
#include <readline/history.h> #include <readline/history.h>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
@ -84,44 +87,59 @@ namespace Hanabi {
} }
} }
int representation_length(probability_t probability) {
return 1 + static_cast<int>(std::ceil(std::log10(probability.denominator()))) + \
static_cast<int>(std::ceil(std::log10(probability.numerator())));
}
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(const std::shared_ptr<HanabiStateIF>& game, hand_index_t index, bool play) {
const auto next_states = game->possible_next_states(index, play); const auto next_states = game->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
return true; return true;
} }
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> states_to_show;
states_to_show.push_back({{Hanabi::Cards::trash, 0}, 0});
std::cout << "Choose drawn card: " << std::endl; std::cout << "Choose drawn card: " << std::endl;
unsigned num_trash = 0; int max_rational_digit_len = 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 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 (game->is_trash(card_multiplicity.card) or (play and game->cur_hand()[index] == card_multiplicity.card)) {
num_trash += card_multiplicity.multiplicity; states_to_show.front().first.multiplicity += card_multiplicity.multiplicity;
trash_discard_prob = probability; states_to_show.front().second = probability;
} else { } else {
std::cout << card_multiplicity.card << " (" << card_multiplicity.multiplicity; states_to_show.emplace_back(card_multiplicity, probability);
std::cout << " copie(s) in draw) " << probability << std::endl; }
if (probability.has_value()) {
max_rational_digit_len = std::max(max_rational_digit_len, representation_length(probability.value()));
} }
} }
if (num_trash > 0) { // Get rid of the trash collecting entry at the front
std::cout << Cards::trash << " (" << num_trash << " copie(s) in draw) " << trash_discard_prob << std::endl; if (states_to_show.front().first.multiplicity == 0) {
states_to_show.front() = std::move(states_to_show.back());
states_to_show.pop_back();
}
std::ranges::sort(states_to_show, [](const auto &left, const auto &right) {
return left.second > right.second;
});
for (const auto &[card_multiplicity, probability]: states_to_show) {
std::cout << card_multiplicity.card << " (" << card_multiplicity.multiplicity;
std::cout << " copie(s) in draw) " << std::setw(max_rational_digit_len) << probability << std::endl;
} }
std::stringstream prompt; std::stringstream prompt;
prompt << "draw? ["; prompt << "draw? [" << states_to_show.front().first.card << "] ";
if (num_trash > 0) {
prompt << Cards::trash;
} else {
prompt << next_states.front().first.card;
}
prompt << "] ";
const std::string card_str = read_line_memory_safe(prompt.str().c_str()); const std::string card_str = read_line_memory_safe(prompt.str().c_str());
const Card drawn_card = [&card_str, &num_trash, &next_states](){
const Card drawn_card = [&card_str, &states_to_show](){
if (card_str.empty()) { if (card_str.empty()) {
if (num_trash > 0) { return states_to_show.front().first.card;
return Cards::trash;
}
return next_states.front().first.card;
} }
return parse_card(card_str); return parse_card(card_str);
}(); }();