refactor attributes
This commit is contained in:
parent
a116ec45e3
commit
a6a8fb78af
2 changed files with 63 additions and 48 deletions
48
game_state.h
48
game_state.h
|
@ -25,8 +25,6 @@ namespace Hanabi {
|
||||||
using hand_index_t = std::uint8_t;
|
using hand_index_t = std::uint8_t;
|
||||||
using probability_t = boost::rational<unsigned long>;
|
using probability_t = boost::rational<unsigned long>;
|
||||||
|
|
||||||
using state_t = std::uint32_t;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We will generally assume that stacks are played from n to 0
|
* We will generally assume that stacks are played from n to 0
|
||||||
* Playing a 0 will yield a clue
|
* Playing a 0 will yield a clue
|
||||||
|
@ -230,6 +228,29 @@ private:
|
||||||
bool was_on_8_clues {false};
|
bool was_on_8_clues {false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This keeps track of the representation of the gamestate relative to some starting state
|
||||||
|
// and is used for id calculation
|
||||||
|
struct RelativeRepresentationData {
|
||||||
|
// List of unique non-trash cards in draw pile
|
||||||
|
boost::container::static_vector<Card, 30> good_cards_draw;
|
||||||
|
|
||||||
|
// Card positions of these cards. Indexes correspond to the cards stored in _good_cards_draw vector
|
||||||
|
boost::container::static_vector<boost::container::static_vector<player_t, max_card_duplicity>, 30> card_positions_draw;
|
||||||
|
|
||||||
|
// Note this is not the same as _good_cards_draw.size(), since this accounts for multiplicities
|
||||||
|
size_t initial_draw_pile_size {};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
std::bitset<num_players * hand_size> card_positions_hands {};
|
||||||
|
|
||||||
|
// Number of bits from above bitset that is meaningful
|
||||||
|
size_t num_useful_cards_in_starting_hands { 0 };
|
||||||
|
|
||||||
|
// Whether we initialized the values above and marked cards accordingly
|
||||||
|
bool initialized { false };
|
||||||
|
};
|
||||||
|
|
||||||
template<bool update_card_positions> unsigned long play_and_potentially_update(hand_index_t index);
|
template<bool update_card_positions> unsigned long play_and_potentially_update(hand_index_t index);
|
||||||
template<bool update_card_positions> unsigned long discard_and_potentially_update(hand_index_t index);
|
template<bool update_card_positions> unsigned long discard_and_potentially_update(hand_index_t index);
|
||||||
|
|
||||||
|
@ -250,6 +271,7 @@ private:
|
||||||
|
|
||||||
std::uint64_t unique_id() const;
|
std::uint64_t unique_id() const;
|
||||||
|
|
||||||
|
// Usual game state
|
||||||
player_t _turn{};
|
player_t _turn{};
|
||||||
clue_t _num_clues{};
|
clue_t _num_clues{};
|
||||||
std::uint8_t _weighted_draw_pile_size{};
|
std::uint8_t _weighted_draw_pile_size{};
|
||||||
|
@ -258,26 +280,20 @@ private:
|
||||||
std::list<CardMultiplicity> _draw_pile{};
|
std::list<CardMultiplicity> _draw_pile{};
|
||||||
std::uint8_t _endgame_turns_left{};
|
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.
|
// further values of game state that are technically determined, but we update them anyway
|
||||||
boost::container::static_vector<boost::container::static_vector<player_t, max_card_duplicity>, 10> _card_positions_draw;
|
|
||||||
// Note that 30 cards here is enough for all standard decks, since there are ot most 6 suits with 5 unique cards each.
|
|
||||||
boost::container::static_vector<Card, 30> _good_cards_draw;
|
|
||||||
|
|
||||||
// This will indicate whether cards that were in hands initially still are in hands
|
|
||||||
std::bitset<num_players * hand_size> _card_positions_hands;
|
|
||||||
|
|
||||||
size_t _num_useful_cards_in_starting_hands{};
|
|
||||||
size_t _initial_draw_pile_size{};
|
|
||||||
|
|
||||||
// further statistics that we might want to keep track of
|
|
||||||
int8_t _pace{};
|
int8_t _pace{};
|
||||||
uint8_t _score{};
|
uint8_t _score{};
|
||||||
|
|
||||||
std::uint64_t _enumerated_states {};
|
// For reverting the current game
|
||||||
|
std::stack<BacktrackAction> _actions_log;
|
||||||
|
|
||||||
|
// For calculating ids of states during backtracking
|
||||||
|
RelativeRepresentationData _relative_representation;
|
||||||
|
|
||||||
|
// Lookup table for states. Uses the ids calculated using the relative representation
|
||||||
std::unordered_map<unsigned long, probability_t> _position_tablebase;
|
std::unordered_map<unsigned long, probability_t> _position_tablebase;
|
||||||
|
|
||||||
std::stack<BacktrackAction> _actions_log;
|
std::uint64_t _enumerated_states {};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t num_suits, player_t num_players, std::size_t hand_size>
|
template <std::size_t num_suits, player_t num_players, std::size_t hand_size>
|
||||||
|
|
|
@ -81,12 +81,11 @@ namespace Hanabi {
|
||||||
_hands(),
|
_hands(),
|
||||||
_draw_pile(),
|
_draw_pile(),
|
||||||
_endgame_turns_left(no_endgame),
|
_endgame_turns_left(no_endgame),
|
||||||
_card_positions_draw(),
|
|
||||||
_card_positions_hands(),
|
|
||||||
_num_useful_cards_in_starting_hands(0),
|
|
||||||
_initial_draw_pile_size(0),
|
|
||||||
_pace(deck.size() - 5 * num_suits - num_players * (hand_size - 1)),
|
_pace(deck.size() - 5 * num_suits - num_players * (hand_size - 1)),
|
||||||
_score(0),
|
_score(0),
|
||||||
|
_actions_log(),
|
||||||
|
_relative_representation(),
|
||||||
|
_position_tablebase(),
|
||||||
_enumerated_states(0) {
|
_enumerated_states(0) {
|
||||||
std::ranges::fill(_stacks, starting_card_rank);
|
std::ranges::fill(_stacks, starting_card_rank);
|
||||||
for (const Card &card: deck) {
|
for (const Card &card: deck) {
|
||||||
|
@ -239,11 +238,11 @@ namespace Hanabi {
|
||||||
const Card discarded = _hands[_turn][index];
|
const Card discarded = _hands[_turn][index];
|
||||||
if (!discarded.initial_trash) {
|
if (!discarded.initial_trash) {
|
||||||
if (discarded.in_starting_hand) {
|
if (discarded.in_starting_hand) {
|
||||||
ASSERT(_card_positions_hands[discarded.local_index] == true);
|
ASSERT(_relative_representation.card_positions_hands[discarded.local_index] == true);
|
||||||
_card_positions_hands[discarded.local_index] = false;
|
_relative_representation.card_positions_hands[discarded.local_index] = false;
|
||||||
} else {
|
} else {
|
||||||
auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded.local_index], _turn);
|
auto replaced_card_it = std::ranges::find(_relative_representation.card_positions_draw[discarded.local_index], _turn);
|
||||||
ASSERT(replaced_card_it != _card_positions_draw[discarded.local_index].end());
|
ASSERT(replaced_card_it != _relative_representation.card_positions_draw[discarded.local_index].end());
|
||||||
*replaced_card_it = trash_or_play_stack;
|
*replaced_card_it = trash_or_play_stack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,8 +265,8 @@ namespace Hanabi {
|
||||||
// update card position of the drawn card
|
// update card position of the drawn card
|
||||||
if (!draw.card.initial_trash) {
|
if (!draw.card.initial_trash) {
|
||||||
ASSERT(draw.card.in_starting_hand == false);
|
ASSERT(draw.card.in_starting_hand == false);
|
||||||
auto new_card_it = std::ranges::find(_card_positions_draw[draw.card.local_index], draw_pile);
|
auto new_card_it = std::ranges::find(_relative_representation.card_positions_draw[draw.card.local_index], draw_pile);
|
||||||
ASSERT(new_card_it != _card_positions_draw[draw.card.local_index].end());
|
ASSERT(new_card_it != _relative_representation.card_positions_draw[draw.card.local_index].end());
|
||||||
*new_card_it = _turn;
|
*new_card_it = _turn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,8 +299,8 @@ namespace Hanabi {
|
||||||
|
|
||||||
if (!drawn.initial_trash) {
|
if (!drawn.initial_trash) {
|
||||||
ASSERT(drawn.in_starting_hand == false);
|
ASSERT(drawn.in_starting_hand == false);
|
||||||
auto drawn_card_it = std::ranges::find(_card_positions_draw[drawn.local_index], _turn);
|
auto drawn_card_it = std::ranges::find(_relative_representation.card_positions_draw[drawn.local_index], _turn);
|
||||||
ASSERT(drawn_card_it != _card_positions_draw[drawn.local_index].end());
|
ASSERT(drawn_card_it != _relative_representation.card_positions_draw[drawn.local_index].end());
|
||||||
*drawn_card_it = draw_pile;
|
*drawn_card_it = draw_pile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,12 +312,12 @@ namespace Hanabi {
|
||||||
|
|
||||||
if (!discarded_card.initial_trash) {
|
if (!discarded_card.initial_trash) {
|
||||||
if (discarded_card.in_starting_hand) {
|
if (discarded_card.in_starting_hand) {
|
||||||
ASSERT(_card_positions_hands[discarded_card.local_index] == false);
|
ASSERT(_relative_representation.card_positions_hands[discarded_card.local_index] == false);
|
||||||
_card_positions_hands[discarded_card.local_index] = true;
|
_relative_representation.card_positions_hands[discarded_card.local_index] = true;
|
||||||
} else {
|
} else {
|
||||||
auto hand_card_it = std::ranges::find(_card_positions_draw[discarded_card.local_index],
|
auto hand_card_it = std::ranges::find(_relative_representation.card_positions_draw[discarded_card.local_index],
|
||||||
trash_or_play_stack);
|
trash_or_play_stack);
|
||||||
ASSERT(hand_card_it != _card_positions_draw[discarded_card.local_index].end());
|
ASSERT(hand_card_it != _relative_representation.card_positions_draw[discarded_card.local_index].end());
|
||||||
*hand_card_it = _turn;
|
*hand_card_it = _turn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,17 +350,17 @@ namespace Hanabi {
|
||||||
_draw_pile.clear();
|
_draw_pile.clear();
|
||||||
for (suit_t suit = 0; suit < num_suits; suit++) {
|
for (suit_t suit = 0; suit < num_suits; suit++) {
|
||||||
for (rank_t rank = 0; rank < starting_card_rank; rank++) {
|
for (rank_t rank = 0; rank < starting_card_rank; rank++) {
|
||||||
Card card{suit, rank, static_cast<uint8_t>(_card_positions_draw.size()), false, is_trash(card)};
|
Card card{suit, rank, static_cast<uint8_t>(_relative_representation.card_positions_draw.size()), false, is_trash(card)};
|
||||||
if (nums_in_draw_pile[card] > 0) {
|
if (nums_in_draw_pile[card] > 0) {
|
||||||
_draw_pile.push_back({card, nums_in_draw_pile[card]});
|
_draw_pile.push_back({card, nums_in_draw_pile[card]});
|
||||||
if (!is_trash(card)) {
|
if (!is_trash(card)) {
|
||||||
_card_positions_draw.push_back({nums_in_draw_pile[card], draw_pile});
|
_relative_representation.card_positions_draw.push_back({nums_in_draw_pile[card], draw_pile});
|
||||||
_good_cards_draw.push_back(card);
|
_relative_representation.good_cards_draw.push_back(card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_initial_draw_pile_size = _weighted_draw_pile_size;
|
_relative_representation.initial_draw_pile_size = _weighted_draw_pile_size;
|
||||||
|
|
||||||
// Prepare cards in hands
|
// Prepare cards in hands
|
||||||
for (player_t player = 0; player < num_players; player++) {
|
for (player_t player = 0; player < num_players; player++) {
|
||||||
|
@ -375,17 +374,17 @@ namespace Hanabi {
|
||||||
// This card is already in hand, so just replace the second copy by some trash
|
// This card is already in hand, so just replace the second copy by some trash
|
||||||
card = trash;
|
card = trash;
|
||||||
} else {
|
} else {
|
||||||
card.local_index = _num_useful_cards_in_starting_hands;
|
card.local_index = _relative_representation.num_useful_cards_in_starting_hands;
|
||||||
_num_useful_cards_in_starting_hands++;
|
_relative_representation.num_useful_cards_in_starting_hands++;
|
||||||
|
|
||||||
good_cards_in_hand.push_back(card);
|
good_cards_in_hand.push_back(card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_card_positions_hands.reset();
|
_relative_representation.card_positions_hands.reset();
|
||||||
for (size_t i = 0; i < _num_useful_cards_in_starting_hands; i++) {
|
for (size_t i = 0; i < _relative_representation.num_useful_cards_in_starting_hands; i++) {
|
||||||
_card_positions_hands[i] = true;
|
_relative_representation.card_positions_hands[i] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,14 +559,14 @@ namespace Hanabi {
|
||||||
unsigned long id = 0;
|
unsigned long id = 0;
|
||||||
|
|
||||||
// encode all positions of cards that started in draw pile
|
// encode all positions of cards that started in draw pile
|
||||||
ASSERT(_card_positions_draw.size() == _good_cards_draw.size());
|
ASSERT(_relative_representation.card_positions_draw.size() == _relative_representation.good_cards_draw.size());
|
||||||
for(size_t i = 0; i < _card_positions_draw.size(); i++) {
|
for(size_t i = 0; i < _relative_representation.card_positions_draw.size(); i++) {
|
||||||
for(player_t player : _card_positions_draw[i]) {
|
for(player_t player : _relative_representation.card_positions_draw[i]) {
|
||||||
id *= num_players + 2;
|
id *= num_players + 2;
|
||||||
// We normalize here: If a card is already played, then the positions of its other copies
|
// We normalize here: If a card is already played, then the positions of its other copies
|
||||||
// do not matter, so we can just pretend that they are all in the trash already.
|
// do not matter, so we can just pretend that they are all in the trash already.
|
||||||
// The resulting states will be equivalent.
|
// The resulting states will be equivalent.
|
||||||
if (!is_trash(_good_cards_draw[i])) {
|
if (!is_trash(_relative_representation.good_cards_draw[i])) {
|
||||||
id += player;
|
id += player;
|
||||||
} else {
|
} else {
|
||||||
id += trash_or_play_stack;
|
id += trash_or_play_stack;
|
||||||
|
@ -589,12 +588,12 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
id *= _initial_draw_pile_size + num_players;
|
id *= _relative_representation.initial_draw_pile_size + num_players;
|
||||||
id += draw_pile_size_and_extra_turns;
|
id += draw_pile_size_and_extra_turns;
|
||||||
|
|
||||||
// encode positions of cards that started in hands
|
// encode positions of cards that started in hands
|
||||||
id = id << _num_useful_cards_in_starting_hands;
|
id = id << _relative_representation.num_useful_cards_in_starting_hands;
|
||||||
id += _card_positions_hands.to_ulong();
|
id += _relative_representation.card_positions_hands.to_ulong();
|
||||||
|
|
||||||
id *= num_players;
|
id *= num_players;
|
||||||
id += _turn;
|
id += _turn;
|
||||||
|
|
Loading…
Reference in a new issue