diff --git a/src/cli_interface.cpp b/src/cli_interface.cpp index 0752238..b975eb3 100644 --- a/src/cli_interface.cpp +++ b/src/cli_interface.cpp @@ -1,6 +1,9 @@ #include +#include #include +#include #include +#include #include #include #include @@ -84,44 +87,59 @@ namespace Hanabi { } } + int representation_length(probability_t probability) { + return 1 + static_cast(std::ceil(std::log10(probability.denominator()))) + \ + static_cast(std::ceil(std::log10(probability.numerator()))); + } + bool ask_for_card_and_rotate_draw(const std::shared_ptr& game, hand_index_t index, bool play) { const auto next_states = game->possible_next_states(index, play); + if (next_states.size() <= 1) { + // No need to ask for anything if draw contains only one possible type of card return true; } + + std::vector>> states_to_show; + states_to_show.push_back({{Hanabi::Cards::trash, 0}, 0}); + std::cout << "Choose drawn card: " << std::endl; - unsigned num_trash = 0; - std::optional trash_discard_prob = 0; + int max_rational_digit_len = 0; for(const auto &[card_multiplicity, probability]: next_states) { // 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)) { - num_trash += card_multiplicity.multiplicity; - trash_discard_prob = probability; + states_to_show.front().first.multiplicity += card_multiplicity.multiplicity; + states_to_show.front().second = probability; } else { - std::cout << card_multiplicity.card << " (" << card_multiplicity.multiplicity; - std::cout << " copie(s) in draw) " << probability << std::endl; + states_to_show.emplace_back(card_multiplicity, probability); + } + if (probability.has_value()) { + max_rational_digit_len = std::max(max_rational_digit_len, representation_length(probability.value())); } } - if (num_trash > 0) { - std::cout << Cards::trash << " (" << num_trash << " copie(s) in draw) " << trash_discard_prob << std::endl; + // Get rid of the trash collecting entry at the front + 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; - prompt << "draw? ["; - if (num_trash > 0) { - prompt << Cards::trash; - } else { - prompt << next_states.front().first.card; - } - prompt << "] "; + prompt << "draw? [" << states_to_show.front().first.card << "] "; 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 (num_trash > 0) { - return Cards::trash; - } - return next_states.front().first.card; + return states_to_show.front().first.card; } return parse_card(card_str); }();