consider playing trash at 8 clues

This commit is contained in:
Maximilian Keßler 2024-01-16 17:19:42 +01:00
parent 03abd08418
commit 863baf3acd
Signed by: max
GPG key ID: BCC5A619923C0BA5
5 changed files with 34 additions and 5 deletions

View file

@ -48,6 +48,8 @@ namespace Hanabi
[[nodiscard]] virtual clue_t num_clues() const = 0; [[nodiscard]] virtual clue_t num_clues() const = 0;
[[nodiscard]] virtual unsigned num_strikes() const = 0;
[[nodiscard]] virtual unsigned score() const = 0; [[nodiscard]] virtual unsigned score() const = 0;
[[nodiscard]] virtual std::vector<std::vector<Card>> hands() const = 0; [[nodiscard]] virtual std::vector<std::vector<Card>> hands() const = 0;

View file

@ -94,6 +94,8 @@ namespace Hanabi
[[nodiscard]] clue_t num_clues() const final; [[nodiscard]] clue_t num_clues() const final;
[[nodiscard]] unsigned num_strikes() const final;
[[nodiscard]] unsigned score() const final; [[nodiscard]] unsigned score() const final;
[[nodiscard]] std::vector<std::vector<Card>> hands() const final; [[nodiscard]] std::vector<std::vector<Card>> hands() const final;
@ -233,6 +235,7 @@ namespace Hanabi
player_t _turn{}; player_t _turn{};
clue_t _num_clues{}; clue_t _num_clues{};
unsigned _num_strikes{};
std::uint8_t _weighted_draw_pile_size{}; std::uint8_t _weighted_draw_pile_size{};
Stacks<num_suits> _stacks{}; Stacks<num_suits> _stacks{};
std::array<std::array<Card, hand_size>, num_players> _hands{}; std::array<std::array<Card, hand_size>, num_players> _hands{};

View file

@ -77,7 +77,7 @@ namespace Hanabi
template<suit_t num_suits, player_t num_players, hand_index_t hand_size> template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
HanabiState<num_suits, num_players, hand_size>::HanabiState(const std::vector<Card> & deck, uint8_t score_goal, clue_t num_clues_gained_on_discard_or_stack_finished): HanabiState<num_suits, num_players, hand_size>::HanabiState(const std::vector<Card> & 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) _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) , _endgame_turns_left(no_endgame), _pace(deck.size() - score_goal - num_players * (hand_size - 1)), _score(0)
, _actions_log(), _relative_representation(), _position_tablebase() , _actions_log(), _relative_representation(), _position_tablebase()
, _enumerated_states(0) , _enumerated_states(0)
@ -214,6 +214,7 @@ namespace Hanabi
_num_clues += _clues_gained_on_discard_or_stack_finished; _num_clues += _clues_gained_on_discard_or_stack_finished;
} }
} else { } else {
_num_strikes++;
_num_copies_left[played_card]--; _num_copies_left[played_card]--;
} }
@ -271,7 +272,7 @@ namespace Hanabi
void HanabiState<num_suits, num_players, hand_size>::print(std::ostream & os) const void HanabiState<num_suits, num_players, hand_size>::print(std::ostream & os) const
{ {
os << "Stacks: " << _stacks << " (score " << +_score << ")"; 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) if (_endgame_turns_left != no_endgame)
{ {
os << ", " << +_endgame_turns_left << " turns left"; os << ", " << +_endgame_turns_left << " turns left";
@ -644,6 +645,7 @@ namespace Hanabi
} else { } else {
// If we misplayed, then we lost the card and have to regain it now // If we misplayed, then we lost the card and have to regain it now
_num_copies_left[last_action.discarded]++; _num_copies_left[last_action.discarded]++;
_num_strikes--;
} }
CHECK_DRAW_PILE_INTEGRITY; CHECK_DRAW_PILE_INTEGRITY;
} }
@ -738,6 +740,12 @@ namespace Hanabi
return _num_clues; return _num_clues;
} }
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
unsigned HanabiState<num_suits, num_players, hand_size>::num_strikes() const
{
return _num_strikes;
}
template<suit_t num_suits, player_t num_players, hand_index_t hand_size> template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
unsigned HanabiState<num_suits, num_players, hand_size>::score() const unsigned HanabiState<num_suits, num_players, hand_size>::score() const
{ {
@ -805,7 +813,7 @@ namespace Hanabi
for (std::uint8_t index = 0; index < hand_size; index++) for (std::uint8_t index = 0; index < hand_size; index++)
{ {
Card card = hand[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 (consider_playing)
{ {
if (is_trash(card)) if (is_trash(card))
@ -1005,10 +1013,14 @@ namespace Hanabi
const std::array<Card, hand_size> & hand = _hands[_turn]; const std::array<Card, hand_size> & hand = _hands[_turn];
// First, check for playables // First, check for playables
bool played_trash = false;
for (std::uint8_t index = 0; index < hand_size; index++) 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); probability_t const probability_play = check_play_or_discard(index, true);
best_probability = std::max(best_probability, probability_play); best_probability = std::max(best_probability, probability_play);

View file

@ -52,6 +52,7 @@ namespace Hanabi
constexpr suit_t max_suit_index = 5; constexpr suit_t max_suit_index = 5;
constexpr size_t max_card_duplicity = 3; constexpr size_t max_card_duplicity = 3;
const clue_t max_num_clues = 8; 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<hand_index_t>::max(); constexpr hand_index_t invalid_hand_idx = std::numeric_limits<hand_index_t>::max();
// We might want to change these at runtime to adapt to other variants. // We might want to change these at runtime to adapt to other variants.

View file

@ -397,7 +397,18 @@ namespace Hanabi
if (prompt.find("actions") == 0) if (prompt.find("actions") == 0)
{ {
auto reasonable_actions = game.state->get_reasonable_actions(); std::vector<std::pair<Hanabi::Action, std::optional<boost::rational<Hanabi::probability_base_type>>>> reasonable_actions = game.state->get_reasonable_actions();
std::sort(reasonable_actions.begin(), reasonable_actions.end(),
[](std::pair<Hanabi::Action, std::optional<probability_t>> const & left,
std::pair<Hanabi::Action, std::optional<probability_t>> 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( int max_rational_digit_len = std::accumulate(
reasonable_actions.begin(), reasonable_actions.end(), 0, []( reasonable_actions.begin(), reasonable_actions.end(), 0, [](
int old, const std::pair<Action int old, const std::pair<Action