Fix id clashes: Normalize all card positions

This commit is contained in:
Maximilian Keßler 2024-01-12 23:07:36 +01:00
parent 27a45561e7
commit cdf8575283
Signed by: max
GPG Key ID: BCC5A619923C0BA5
2 changed files with 45 additions and 13 deletions

View File

@ -182,8 +182,12 @@ namespace Hanabi
boost::container::static_vector<boost::container::static_vector<player_t, max_card_duplicity>
, 30> card_positions_draw;
// List of all non-trash cards in hands of base state
boost::container::static_vector<Card, num_players * hand_size> 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<CardPosition, num_players * hand_size> card_positions_hands{};
// Note this is not the same as _good_cards_draw.size(), since this accounts for multiplicities

View File

@ -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<std::underlying_type_t<typename RelativeRepresentationData::CardPosition>>(RelativeRepresentationData::CardPosition::discarded);
}
else
{
id += static_cast<std::underlying_type_t<typename RelativeRepresentationData::CardPosition>>(_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<std::underlying_type_t<typename RelativeRepresentationData::CardPosition>>(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<std::underlying_type_t<typename RelativeRepresentationData::CardPosition>>(RelativeRepresentationData::CardPosition::discarded));
}
else
{
ret.push_back(static_cast<std::underlying_type_t<typename RelativeRepresentationData::CardPosition>>(_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<std::underlying_type_t<typename RelativeRepresentationData::CardPosition>>(position));
}
ret.push_back(_turn);
// The id is unique now, since for all relevant cards, we know their position (including if they are played),