diff --git a/game_state.h b/game_state.h index a6f5abd..60eee5f 100644 --- a/game_state.h +++ b/game_state.h @@ -43,6 +43,7 @@ constexpr std::array suit_initials{"r", "y", "g", "b", "p", "t"} struct Card { suit_t suit; rank_t rank; + bool was_in_initial_hand; Card &operator++(); const Card operator++(int); @@ -84,12 +85,26 @@ struct CardMultiplicity { auto operator<=>(const CardMultiplicity &) const = default; }; +template +struct InnerCardArray { + template + using array_t = std::array; +}; + +template<> +struct InnerCardArray { + template + using array_t = std::bitset; +}; + template struct CardArray { using value_type = T; CardArray() = default; explicit CardArray(value_type default_val); + void fill(value_type val); + const value_type &operator[](const Card &card) const; value_type &operator[](const Card &card); @@ -97,7 +112,8 @@ template struct CardArray { auto operator<=>(const CardArray &) const = default; private: - std::array, num_suits> _array {}; + using inner_array_t = typename InnerCardArray::template array_t; + std::array _array {}; }; enum class ActionType { @@ -195,6 +211,14 @@ private: static constexpr uint8_t no_endgame = std::numeric_limits::max(); + // 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; + + // 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; + // further statistics that we might want to keep track of uint8_t _pace{}; uint8_t _score{}; diff --git a/game_state.hpp b/game_state.hpp index 9320866..f4d45a5 100644 --- a/game_state.hpp +++ b/game_state.hpp @@ -37,14 +37,19 @@ namespace Hanabi { } template - CardArray::CardArray(T default_val) { - for(size_t suit = 0; suit < num_suits; suit++) { + void CardArray::fill(T val) { + for (size_t suit = 0; suit < num_suits; suit++) { for (rank_t rank = 0; rank < starting_card_rank; rank++) { - _array[suit][rank] = default_val; + _array[suit][rank] = val; } } } + template + CardArray::CardArray(T default_val) { + fill(default_val); + } + template const T& CardArray::operator[](const Card &card) const { return _array[card.suit][card.rank]; @@ -247,6 +252,7 @@ namespace Hanabi { template void HanabiState::normalize_draw_and_positions() { + // Note that this function does not have to be particularly performant, we only call it once to initialize. const Card trash = [this]() -> Card { for(suit_t suit = 0; suit < num_suits; suit++) { if(_stacks[suit] < starting_card_rank) { @@ -257,25 +263,44 @@ namespace Hanabi { }(); CardArray nums_in_draw_pile; - std::uint8_t num_trash_in_draw_pile = 0; for(const auto [card, multiplicity] : _draw_pile) { if (_stacks[card.suit] > card.rank) { nums_in_draw_pile[card] += multiplicity; } else { - num_trash_in_draw_pile++; + nums_in_draw_pile[trash] += multiplicity; } } + // Prepare draw pile _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}; + Card card {suit, rank, false}; 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); + } } } } - _draw_pile.push_back({trash, num_trash_in_draw_pile}); + + // Prepare cards in hands + for(player_t player = 0; player < num_players; player++) { + for(Card& card : _hands[player]) { + 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_draw.fill(draw_pile); } template diff --git a/main.cpp b/main.cpp index b088764..18d86d6 100644 --- a/main.cpp +++ b/main.cpp @@ -40,7 +40,18 @@ namespace Hanabi { } +#define CHECK(test, condition) if (!(condition)) { std::cerr << "Test " << (test) << " failed." << std::endl; } + +void test() { + { + auto game = Download::get_game("1005195", 43); + auto res = game->backtrack(1); + CHECK("1005195", res == static_cast(7) / 8); + } +} + int main(int argc, char *argv[]) { + test(); if(argc == 3) { std::string game (argv[1]); std::string turn (argv[2]);