diff --git a/game_state.h b/game_state.h index bfd1d88..4138559 100644 --- a/game_state.h +++ b/game_state.h @@ -15,13 +15,13 @@ namespace Hanabi { -using rank_t = std::uint8_t; -using suit_t = std::uint8_t; -using clue_t = std::uint8_t; -using player_t = std::int8_t; -using hand_index_t = std::uint8_t; + using rank_t = std::uint8_t; + using suit_t = std::uint8_t; + using clue_t = std::uint8_t; + using player_t = std::uint8_t; + using hand_index_t = std::uint8_t; -using state_t = std::uint32_t; + using state_t = std::uint32_t; /** * We will generally assume that stacks are played from n to 0 @@ -31,25 +31,39 @@ using state_t = std::uint32_t; * This is just easier to implement, since then the remaining number of cards * to be played is always the current number of the stack */ -constexpr rank_t starting_card_rank = 5; -constexpr suit_t max_suit_index = 5; -constexpr size_t max_card_duplicity = 3; -constexpr clue_t max_num_clues = 8; -constexpr uint8_t not_in_starting_hand = std::numeric_limits::max(); + constexpr rank_t starting_card_rank = 5; + constexpr suit_t max_suit_index = 5; + constexpr size_t max_card_duplicity = 3; + constexpr clue_t max_num_clues = 8; + constexpr uint8_t not_in_starting_hand = std::numeric_limits::max(); -constexpr std::array suit_initials{"r", "y", "g", "b", "p", "t"}; + constexpr std::array suit_initials{"r", "y", "g", "b", "p", "t"}; -struct Card { - suit_t suit; - rank_t rank; - uint8_t local_index_among_good_starting_hand_cards; - bool initial_trash; + struct Card { + suit_t suit; + rank_t rank; + uint8_t local_index; + bool in_starting_hand; + bool initial_trash; - Card &operator++(); - const Card operator++(int); + Card &operator++(); - auto operator<=>(const Card &) const = default; -}; + const Card operator++(int); + + auto operator<=>(const Card &) const = default; + }; +} + +namespace std { + template<> + struct hash { + std::size_t operator()(Hanabi::Card const& card) const noexcept { + return card.suit * 6 + card.rank; + } + }; +} + +namespace Hanabi { std::ostream &operator<<(std::ostream &os, const Card &card); @@ -213,11 +227,12 @@ private: std::uint8_t _endgame_turns_left{}; // This will save the card positions of all cards that are in the draw pile when we start backtracking - CardArray> _card_positions_draw; + // TODO: currently, we support at most 10 useful different cards in draw pile + boost::container::static_vector, 10> _card_positions_draw; // This will indicate whether cards that were in hands initially still are in hands std::bitset _card_positions_hands; - size_t _num_useful_cards_in_starting_hands; + uint8_t _num_useful_cards_in_starting_hands; // further statistics that we might want to keep track of uint8_t _pace{}; diff --git a/game_state.hpp b/game_state.hpp index 727d8e2..5acc4ad 100644 --- a/game_state.hpp +++ b/game_state.hpp @@ -3,6 +3,7 @@ #include "myassert.h" #include "game_state.h" #include +#include namespace Hanabi { @@ -222,12 +223,12 @@ namespace Hanabi { if constexpr(update_card_positions) { const Card discarded = _hands[_turn][index]; if (!discarded.initial_trash) { - if (discarded.local_index_among_good_starting_hand_cards != not_in_starting_hand) { - ASSERT(_card_positions_hands[discarded.local_index_among_good_starting_hand_cards] == true); - _card_positions_hands[discarded.local_index_among_good_starting_hand_cards] = false; + if (discarded.in_starting_hand) { + ASSERT(_card_positions_hands[discarded.local_index] == true); + _card_positions_hands[discarded.local_index] = false; } else { - auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded], _turn); - ASSERT(replaced_card_it != _card_positions_draw[discarded].end()); + auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded.local_index], _turn); + ASSERT(replaced_card_it != _card_positions_draw[discarded.local_index].end()); *replaced_card_it = trash_or_play_stack; } } @@ -249,8 +250,9 @@ namespace Hanabi { if constexpr(update_card_positions) { // update card position of the drawn card 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()); + ASSERT(draw.card.in_starting_hand == false); + auto new_card_it = std::ranges::find(_card_positions_draw[draw.card.local_index], draw_pile); + ASSERT(new_card_it != _card_positions_draw[draw.card.local_index].end()); *new_card_it = _turn; } } @@ -282,8 +284,9 @@ namespace Hanabi { } 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()); + ASSERT(drawn.in_starting_hand == false); + auto drawn_card_it = std::ranges::find(_card_positions_draw[drawn.local_index], _turn); + ASSERT(drawn_card_it != _card_positions_draw[drawn.local_index].end()); *drawn_card_it = draw_pile; } @@ -294,12 +297,12 @@ namespace Hanabi { } if (!discarded_card.initial_trash) { - if (discarded_card.local_index_among_good_starting_hand_cards != not_in_starting_hand) { - ASSERT(_card_positions_hands[discarded_card.local_index_among_good_starting_hand_cards] == false); - _card_positions_hands[discarded_card.local_index_among_good_starting_hand_cards] = true; + if (discarded_card.in_starting_hand) { + ASSERT(_card_positions_hands[discarded_card.local_index] == false); + _card_positions_hands[discarded_card.local_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()); + auto hand_card_it = std::ranges::find(_card_positions_draw[discarded_card.local_index], trash_or_play_stack); + ASSERT(hand_card_it != _card_positions_draw[discarded_card.local_index].end()); *hand_card_it = _turn; } } @@ -313,7 +316,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, not_in_starting_hand, true}; + return {suit, starting_card_rank - 1, 0, false, true}; } } return {0,0}; @@ -332,12 +335,11 @@ 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, not_in_starting_hand, is_trash(card)}; - _card_positions_draw[card].clear(); + Card card {suit, rank, static_cast(_card_positions_draw.size()), false, is_trash(card)}; 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].resize(nums_in_draw_pile[card], draw_pile); + _card_positions_draw.push_back({nums_in_draw_pile[card], draw_pile}); } } } @@ -347,6 +349,7 @@ namespace Hanabi { for(player_t player = 0; player < num_players; player++) { for(Card& card : _hands[player]) { card.initial_trash = is_trash(card); + card.in_starting_hand = true; // Needed to check for dupes in same hand boost::container::static_vector good_cards_in_hand; if(!is_trash(card)) { @@ -354,7 +357,7 @@ namespace Hanabi { // This card is already in hand, so just replace the second copy by some trash card = trash; } else { - card.local_index_among_good_starting_hand_cards = _num_useful_cards_in_starting_hands; + card.local_index = _num_useful_cards_in_starting_hands; _num_useful_cards_in_starting_hands++; good_cards_in_hand.push_back(card);