From 863baf3acda2ecfa637e1c271317d6ddfb996c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Tue, 16 Jan 2024 17:19:42 +0100 Subject: [PATCH] consider playing trash at 8 clues --- include/game_interface.h | 2 ++ include/game_state.h | 3 +++ include/game_state.hpp | 20 ++++++++++++++++---- include/hanabi_types.hpp | 1 + src/state_explorer.cpp | 13 ++++++++++++- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/include/game_interface.h b/include/game_interface.h index 44ce794..931e717 100644 --- a/include/game_interface.h +++ b/include/game_interface.h @@ -48,6 +48,8 @@ namespace Hanabi [[nodiscard]] virtual clue_t num_clues() const = 0; + [[nodiscard]] virtual unsigned num_strikes() const = 0; + [[nodiscard]] virtual unsigned score() const = 0; [[nodiscard]] virtual std::vector> hands() const = 0; diff --git a/include/game_state.h b/include/game_state.h index 4d55682..8e6cc97 100644 --- a/include/game_state.h +++ b/include/game_state.h @@ -94,6 +94,8 @@ namespace Hanabi [[nodiscard]] clue_t num_clues() const final; + [[nodiscard]] unsigned num_strikes() const final; + [[nodiscard]] unsigned score() const final; [[nodiscard]] std::vector> hands() const final; @@ -233,6 +235,7 @@ namespace Hanabi player_t _turn{}; clue_t _num_clues{}; + unsigned _num_strikes{}; std::uint8_t _weighted_draw_pile_size{}; Stacks _stacks{}; std::array, num_players> _hands{}; diff --git a/include/game_state.hpp b/include/game_state.hpp index 53187c7..a48cb42 100644 --- a/include/game_state.hpp +++ b/include/game_state.hpp @@ -77,7 +77,7 @@ namespace Hanabi template HanabiState::HanabiState(const std::vector & deck, uint8_t score_goal, clue_t num_clues_gained_on_discard_or_stack_finished): _clues_gained_on_discard_or_stack_finished(num_clues_gained_on_discard_or_stack_finished) - , _score_goal(score_goal), _turn(0), _num_clues(max_num_clues), _weighted_draw_pile_size(deck.size()), _stacks(), _hands(), _draw_pile() + , _score_goal(score_goal), _turn(0), _num_clues(max_num_clues), _num_strikes(0), _weighted_draw_pile_size(deck.size()), _stacks(), _hands(), _draw_pile() , _endgame_turns_left(no_endgame), _pace(deck.size() - score_goal - num_players * (hand_size - 1)), _score(0) , _actions_log(), _relative_representation(), _position_tablebase() , _enumerated_states(0) @@ -214,6 +214,7 @@ namespace Hanabi _num_clues += _clues_gained_on_discard_or_stack_finished; } } else { + _num_strikes++; _num_copies_left[played_card]--; } @@ -271,7 +272,7 @@ namespace Hanabi void HanabiState::print(std::ostream & os) const { os << "Stacks: " << _stacks << " (score " << +_score << ")"; - os << ", clues: " << +_num_clues << ", turn: " << +_turn; + os << ", clues: " << +_num_clues << ", strikes: " << +_num_strikes << ", turn: " << +_turn; if (_endgame_turns_left != no_endgame) { os << ", " << +_endgame_turns_left << " turns left"; @@ -644,6 +645,7 @@ namespace Hanabi } else { // If we misplayed, then we lost the card and have to regain it now _num_copies_left[last_action.discarded]++; + _num_strikes--; } CHECK_DRAW_PILE_INTEGRITY; } @@ -738,6 +740,12 @@ namespace Hanabi return _num_clues; } + template + unsigned HanabiState::num_strikes() const + { + return _num_strikes; + } + template unsigned HanabiState::score() const { @@ -805,7 +813,7 @@ namespace Hanabi for (std::uint8_t index = 0; index < hand_size; index++) { Card card = hand[index]; - bool const consider_playing = is_playable(card) or (not is_critical(card) and not reasonable and (not is_trash(card) or not played_trash)); + bool const consider_playing = is_playable(card) or (_num_strikes < max_num_strikes and not is_critical(card) and (not reasonable or _num_clues == max_num_clues) and (not is_trash(card) or not played_trash)); if (consider_playing) { if (is_trash(card)) @@ -1005,10 +1013,14 @@ namespace Hanabi const std::array & hand = _hands[_turn]; // First, check for playables + bool played_trash = false; for (std::uint8_t index = 0; index < hand_size; index++) { - if (is_playable(hand[index])) + if (is_playable(hand[index]) or (_num_clues == max_num_clues and _num_strikes < max_num_strikes and not is_critical(hand[index]) and (not is_trash(hand[index]) or not played_trash))) { + if (is_trash(hand[index])) { + played_trash = true; + } probability_t const probability_play = check_play_or_discard(index, true); best_probability = std::max(best_probability, probability_play); diff --git a/include/hanabi_types.hpp b/include/hanabi_types.hpp index 522a513..93980b8 100644 --- a/include/hanabi_types.hpp +++ b/include/hanabi_types.hpp @@ -52,6 +52,7 @@ namespace Hanabi constexpr suit_t max_suit_index = 5; constexpr size_t max_card_duplicity = 3; const clue_t max_num_clues = 8; + constexpr unsigned max_num_strikes = 2; /** Maximum number of allowed strikes */ constexpr hand_index_t invalid_hand_idx = std::numeric_limits::max(); // We might want to change these at runtime to adapt to other variants. diff --git a/src/state_explorer.cpp b/src/state_explorer.cpp index f42b67a..098561c 100644 --- a/src/state_explorer.cpp +++ b/src/state_explorer.cpp @@ -397,7 +397,18 @@ namespace Hanabi if (prompt.find("actions") == 0) { - auto reasonable_actions = game.state->get_reasonable_actions(); + std::vector>>> reasonable_actions = game.state->get_reasonable_actions(); + std::sort(reasonable_actions.begin(), reasonable_actions.end(), + [](std::pair> const & left, + std::pair> const & right){ + if (not left.second.has_value()) { + return false; + } + if (not right.second.has_value()) { + return true; + } + return left.second.value() < right.second.value(); + }); int max_rational_digit_len = std::accumulate( reasonable_actions.begin(), reasonable_actions.end(), 0, []( int old, const std::pair