diff --git a/include/game_state.h b/include/game_state.h index 33f3ce6..94f3378 100644 --- a/include/game_state.h +++ b/include/game_state.h @@ -182,8 +182,12 @@ namespace Hanabi boost::container::static_vector , 30> card_positions_draw; + + // List of all non-trash cards in hands of base state + boost::container::static_vector good_cards_hands; + // This will indicate whether cards that were in hands initially still are in hand - // The first n bits are used and cards are assumed to have been marked with their indices in this bitset + // The first n entries are used and cards are assumed to have been marked with their indices in this vector boost::container::static_vector card_positions_hands{}; // Note this is not the same as _good_cards_draw.size(), since this accounts for multiplicities diff --git a/include/game_state.hpp b/include/game_state.hpp index be6df04..b231f42 100644 --- a/include/game_state.hpp +++ b/include/game_state.hpp @@ -583,6 +583,7 @@ namespace Hanabi num_useful_cards_in_starting_hands++; good_cards_in_hand.push_back(card); + _relative_representation.good_cards_hands.push_back(card); } } } @@ -1118,6 +1119,26 @@ namespace Hanabi } } + // encode positions of cards that started in hands + ASSERT(_relative_representation.card_positions_hands.size() == _relative_representation.good_cards_hands.size()); + for(size_t i = 0; i < _relative_representation.card_positions_hands.size(); i++) + { + id *= 3; + // we have to normalize here again and pretend that cards already played are all discarded. + // Note that implicitly, this means that when we lose the last copy of a good card, this encoding pretends that + // the card has been played already. + // However, since we only ever consider actions that do not lose the last copy of a card, this is not a problem + // (unless our base state was already lacking cards, in which case the card is never considered played in any state) + if(is_trash(_relative_representation.good_cards_hands[i])) + { + id += static_cast>(RelativeRepresentationData::CardPosition::discarded); + } + else + { + id += static_cast>(_relative_representation.card_positions_hands[i]); + } + } + // encode number of clues clue_t const scaled_clues = clue_t(2) * _num_clues; assert(scaled_clues.denominator() == 1); @@ -1139,12 +1160,6 @@ namespace Hanabi id *= _relative_representation.initial_draw_pile_size + num_players; id += draw_pile_size_and_extra_turns; - // encode positions of cards that started in hands - for (typename RelativeRepresentationData::CardPosition const & position: _relative_representation.card_positions_hands) - { - id *= 3; - id += static_cast>(position); - } id *= num_players; id += _turn; @@ -1185,6 +1200,25 @@ namespace Hanabi } } + // encode positions of cards that started in hands + ASSERT(_relative_representation.card_positions_hands.size() == _relative_representation.good_cards_hands.size()); + for(size_t i = 0; i < _relative_representation.card_positions_hands.size(); i++) + { + // we have to normalize here again and pretend that cards already played are all discarded. + // Note that implicitly, this means that when we lose the last copy of a good card, this encoding pretends that + // the card has been played already. + // However, since we only ever consider actions that do not lose the last copy of a card, this is not a problem + // (unless our base state was already lacking cards, in which case the card is never considered played in any state) + if(is_trash(_relative_representation.good_cards_hands[i])) + { + ret.push_back(static_cast>(RelativeRepresentationData::CardPosition::discarded)); + } + else + { + ret.push_back(static_cast>(_relative_representation.card_positions_hands[i])); + } + } + // encode number of clues clue_t const scaled_clues = clue_t(2) * _num_clues; assert(scaled_clues.denominator() == 1); @@ -1204,12 +1238,6 @@ namespace Hanabi ret.push_back(draw_pile_size_and_extra_turns); - // encode positions of cards that started in hands - for (typename RelativeRepresentationData::CardPosition const & position: _relative_representation.card_positions_hands) - { - ret.push_back(static_cast>(position)); - } - ret.push_back(_turn); // The id is unique now, since for all relevant cards, we know their position (including if they are played),