diff --git a/game_state.h b/game_state.h index 60eee5f..be7541e 100644 --- a/game_state.h +++ b/game_state.h @@ -44,6 +44,7 @@ struct Card { suit_t suit; rank_t rank; bool was_in_initial_hand; + bool initial_trash; Card &operator++(); const Card operator++(int); @@ -143,10 +144,6 @@ public: virtual BacktrackAction discard(hand_index_t index) = 0; virtual BacktrackAction play(hand_index_t index) = 0; - virtual void revert_clue() = 0; - virtual void revert_play(const BacktrackAction &action, bool was_on_8_clues) = 0; - virtual void revert_discard(const BacktrackAction &action) = 0; - [[nodiscard]] virtual hand_index_t find_card_in_hand(const Card& card) const = 0; [[nodiscard]] virtual bool is_trash(const Card& card) const = 0; [[nodiscard]] virtual bool is_playable(const Card& card) const = 0; @@ -175,9 +172,9 @@ public: BacktrackAction play(hand_index_t index) final; BacktrackAction discard(hand_index_t index) final; - void revert_clue() final; - void revert_play(const BacktrackAction &action, bool was_on_8_clues) final; - void revert_discard(const BacktrackAction &action) final; + void revert_clue(); + void revert_play(const BacktrackAction &action, bool was_on_8_clues); + void revert_discard(const BacktrackAction &action); [[nodiscard]] hand_index_t find_card_in_hand(const Card& card) const final; [[nodiscard]] bool is_trash(const Card& card) const final; @@ -193,9 +190,10 @@ protected: void print(std::ostream& os) const final; private: - BacktrackAction play_no_strike(hand_index_t index); + template BacktrackAction play_and_potentially_update(hand_index_t index); + template BacktrackAction discard_and_potentially_update(hand_index_t index); - hand_index_t draw(hand_index_t index); + template hand_index_t draw(hand_index_t index); void revert_draw(hand_index_t index, Card discarded_card); void incr_turn(); @@ -214,7 +212,7 @@ private: // This will save the card positions of all cards that are in the draw pile when we start backtracking CardArray> _card_positions_draw; // This will indicate whether cards that were in hands initially still are in hands - CardArray _card_positions_hands; + std::array, num_players> _card_positions_hands; // A list of cards (set up once upon initialization) of all good cards that were in starting hands std::array, num_players> _good_cards_in_initial_draw_pile; diff --git a/game_state.hpp b/game_state.hpp index f4d45a5..a0a3cfd 100644 --- a/game_state.hpp +++ b/game_state.hpp @@ -77,7 +77,7 @@ namespace Hanabi { } for(player_t player = 0; player < num_players; player++) { for(std::uint8_t index = 0; index < hand_size; index++) { - draw(index); + draw(index); } incr_turn(); } @@ -127,15 +127,16 @@ namespace Hanabi { BacktrackAction HanabiState::play(Hanabi::hand_index_t index) { const Card card = _hands[_turn][index]; if (!is_playable(card)) { - BacktrackAction ret{card, index, draw(index)}; + BacktrackAction ret{card, index, draw(index)}; incr_turn(); return ret; } - return play_no_strike(index); + return play_and_potentially_update(index); } template - BacktrackAction HanabiState::play_no_strike(Hanabi::hand_index_t index) { + template + BacktrackAction HanabiState::play_and_potentially_update(hand_index_t index) { ASSERT(index < _hands[_turn].size()); const Card card = _hands[_turn][index]; ASSERT(is_playable(card)); @@ -148,7 +149,7 @@ namespace Hanabi { _num_clues++; } - BacktrackAction ret{card, index, draw(index)}; + BacktrackAction ret{card, index, draw(index)}; incr_turn(); return ret; @@ -156,6 +157,12 @@ namespace Hanabi { template BacktrackAction HanabiState::discard(std::uint8_t index) { + return discard_and_potentially_update(index); + } + + template + template + BacktrackAction HanabiState::discard_and_potentially_update(hand_index_t index) { ASSERT(index < _hands[_turn].size()); ASSERT(_num_clues != max_num_clues); @@ -163,7 +170,7 @@ namespace Hanabi { _num_clues++; _pace--; - BacktrackAction ret{discarded, index, draw(index)}; + BacktrackAction ret{discarded, index, draw(index)}; incr_turn(); return ret; @@ -203,6 +210,7 @@ namespace Hanabi { } template + template std::uint8_t HanabiState::draw(uint8_t index) { ASSERT(index < _hands[_turn].size()); @@ -219,6 +227,26 @@ namespace Hanabi { _draw_pile.back().multiplicity--; } + if constexpr(update_card_positions) { + const Card discarded = _hands[_turn][index]; + if (!discarded.initial_trash) { + if (discarded.was_in_initial_hand) { + ASSERT(_card_positions_hands[_turn][index] == true); + _card_positions_hands[_turn][index] = false; + } else { + auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded], _turn); + ASSERT(replaced_card_it != _card_positions_draw[discarded].end()); + *replaced_card_it = trash_or_play_stack; + } + } + + if (!draw.card.initial_trash) { + auto new_card_it = std::ranges::find(_card_positions_draw[draw.card], draw_pile); + ASSERT(new_card_it != _card_positions_draw[draw.card].end()); + *new_card_it = _turn; + } + } + _hands[_turn][index] = draw.card; if(_draw_pile.empty()) { @@ -244,9 +272,27 @@ namespace Hanabi { } else { _draw_pile.push_back({drawn, 1}); } + + if (!drawn.initial_trash) { + auto drawn_card_it = std::ranges::find(_card_positions_draw[drawn], _turn); + ASSERT(drawn_card_it != _card_positions_draw[drawn].end()); + *drawn_card_it = draw_pile; + } + _weighted_draw_pile_size++; _endgame_turns_left = no_endgame; } + + if (!discarded_card.initial_trash) { + if (discarded_card.was_in_initial_hand) { + ASSERT(_card_positions_hands[_turn][index] == false); + _card_positions_hands[_turn][index] = true; + } else { + auto hand_card_it = std::ranges::find(_card_positions_draw[discarded_card], trash_or_play_stack); + ASSERT(hand_card_it != _card_positions_draw[discarded_card].end()); + } + } + _hands[_turn][index] = discarded_card; } @@ -256,7 +302,7 @@ namespace Hanabi { const Card trash = [this]() -> Card { for(suit_t suit = 0; suit < num_suits; suit++) { if(_stacks[suit] < starting_card_rank) { - return {suit, starting_card_rank - 1}; + return {suit, starting_card_rank - 1, false, true}; } } return {0,0}; @@ -275,12 +321,13 @@ namespace Hanabi { _draw_pile.clear(); for(suit_t suit = 0; suit < num_suits; suit++) { for(rank_t rank = 0; rank < starting_card_rank; rank++) { - Card card {suit, rank, false}; + Card card {suit, rank, false, is_trash(card)}; + _card_positions_draw[card].clear(); if (nums_in_draw_pile[card] > 0) { _draw_pile.push_back({card, nums_in_draw_pile[card]}); if(!is_trash(card)) { - _card_positions_draw[card].clear(); _card_positions_draw[card].resize(nums_in_draw_pile[card], draw_pile); + std::cout << _card_positions_draw[card].size() << std::endl; } } } @@ -289,18 +336,20 @@ namespace Hanabi { // Prepare cards in hands for(player_t player = 0; player < num_players; player++) { for(Card& card : _hands[player]) { + card.was_in_initial_hand = true; + card.initial_trash = is_trash(card); if(!is_trash(card)) { if(std::count(_good_cards_in_initial_draw_pile[player].begin(), _good_cards_in_initial_draw_pile[player].end(), card) > 0) { // This card is already in hand, so just replace the second copy by some trash card = trash; } else { _good_cards_in_initial_draw_pile[player].push_back(card); - card.was_in_initial_hand = true; } } } + _card_positions_hands[player].reset(); + _card_positions_hands[player].flip(); } - _card_positions_draw.fill(draw_pile); } template @@ -358,7 +407,7 @@ namespace Hanabi { if(is_playable(hand[index])) { if (_draw_pile.empty()) { bool on_8_clues = _num_clues == 8; - BacktrackAction action = play_no_strike(index); + BacktrackAction action = play_and_potentially_update(index); const double probability_for_this_play = backtrack(depth + 1); revert_play(action, on_8_clues); UPDATE_PROBABILITY(probability_for_this_play); @@ -367,7 +416,7 @@ namespace Hanabi { uint8_t sum_of_mults = 0; for (size_t i = 0; i < _draw_pile.size(); i++) { bool on_8_clues = _num_clues == 8; - BacktrackAction action = play_no_strike(index); + BacktrackAction action = play_and_potentially_update(index); sum_of_probabilities += backtrack(depth + 1) * action.multiplicity; sum_of_mults += action.multiplicity; revert_play(action, on_8_clues); @@ -386,14 +435,14 @@ namespace Hanabi { if (is_trash(hand[index])) { double sum_of_probabilities = 0; if (_draw_pile.empty()) { - BacktrackAction action = discard(index); + BacktrackAction action = discard_and_potentially_update(index); const double probability_for_this_discard = backtrack(depth + 1); revert_discard(action); UPDATE_PROBABILITY(probability_for_this_discard); } else { uint8_t sum_of_mults = 0; for (size_t i = 0; i < _draw_pile.size(); i++) { - BacktrackAction action = discard(index); + BacktrackAction action = discard_and_potentially_update(index); sum_of_probabilities += backtrack(depth + 1) * action.multiplicity; sum_of_mults += action.multiplicity; revert_discard(action);