track card positions
This commit is contained in:
parent
ed6f3949b0
commit
2735e66ade
2 changed files with 72 additions and 25 deletions
18
game_state.h
18
game_state.h
|
@ -44,6 +44,7 @@ struct Card {
|
||||||
suit_t suit;
|
suit_t suit;
|
||||||
rank_t rank;
|
rank_t rank;
|
||||||
bool was_in_initial_hand;
|
bool was_in_initial_hand;
|
||||||
|
bool initial_trash;
|
||||||
|
|
||||||
Card &operator++();
|
Card &operator++();
|
||||||
const Card operator++(int);
|
const Card operator++(int);
|
||||||
|
@ -143,10 +144,6 @@ public:
|
||||||
virtual BacktrackAction discard(hand_index_t index) = 0;
|
virtual BacktrackAction discard(hand_index_t index) = 0;
|
||||||
virtual BacktrackAction play(hand_index_t index) = 0;
|
virtual BacktrackAction play(hand_index_t index) = 0;
|
||||||
|
|
||||||
virtual void revert_clue() = 0;
|
|
||||||
virtual void revert_play(const BacktrackAction &action, bool was_on_8_clues) = 0;
|
|
||||||
virtual void revert_discard(const BacktrackAction &action) = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual hand_index_t find_card_in_hand(const Card& card) const = 0;
|
[[nodiscard]] virtual hand_index_t find_card_in_hand(const Card& card) const = 0;
|
||||||
[[nodiscard]] virtual bool is_trash(const Card& card) const = 0;
|
[[nodiscard]] virtual bool is_trash(const Card& card) const = 0;
|
||||||
[[nodiscard]] virtual bool is_playable(const Card& card) const = 0;
|
[[nodiscard]] virtual bool is_playable(const Card& card) const = 0;
|
||||||
|
@ -175,9 +172,9 @@ public:
|
||||||
BacktrackAction play(hand_index_t index) final;
|
BacktrackAction play(hand_index_t index) final;
|
||||||
BacktrackAction discard(hand_index_t index) final;
|
BacktrackAction discard(hand_index_t index) final;
|
||||||
|
|
||||||
void revert_clue() final;
|
void revert_clue();
|
||||||
void revert_play(const BacktrackAction &action, bool was_on_8_clues) final;
|
void revert_play(const BacktrackAction &action, bool was_on_8_clues);
|
||||||
void revert_discard(const BacktrackAction &action) final;
|
void revert_discard(const BacktrackAction &action);
|
||||||
|
|
||||||
[[nodiscard]] hand_index_t find_card_in_hand(const Card& card) const final;
|
[[nodiscard]] hand_index_t find_card_in_hand(const Card& card) const final;
|
||||||
[[nodiscard]] bool is_trash(const Card& card) const final;
|
[[nodiscard]] bool is_trash(const Card& card) const final;
|
||||||
|
@ -193,9 +190,10 @@ protected:
|
||||||
void print(std::ostream& os) const final;
|
void print(std::ostream& os) const final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BacktrackAction play_no_strike(hand_index_t index);
|
template<bool update_card_positions> BacktrackAction play_and_potentially_update(hand_index_t index);
|
||||||
|
template<bool update_card_positions> BacktrackAction discard_and_potentially_update(hand_index_t index);
|
||||||
|
|
||||||
hand_index_t draw(hand_index_t index);
|
template<bool update_card_positions> hand_index_t draw(hand_index_t index);
|
||||||
void revert_draw(hand_index_t index, Card discarded_card);
|
void revert_draw(hand_index_t index, Card discarded_card);
|
||||||
|
|
||||||
void incr_turn();
|
void incr_turn();
|
||||||
|
@ -214,7 +212,7 @@ private:
|
||||||
// This will save the card positions of all cards that are in the draw pile when we start backtracking
|
// This will save the card positions of all cards that are in the draw pile when we start backtracking
|
||||||
CardArray<num_suits, boost::container::static_vector<player_t, max_card_duplicity>> _card_positions_draw;
|
CardArray<num_suits, boost::container::static_vector<player_t, max_card_duplicity>> _card_positions_draw;
|
||||||
// This will indicate whether cards that were in hands initially still are in hands
|
// This will indicate whether cards that were in hands initially still are in hands
|
||||||
CardArray<num_suits, bool> _card_positions_hands;
|
std::array<std::bitset<hand_size>, num_players> _card_positions_hands;
|
||||||
|
|
||||||
// A list of cards (set up once upon initialization) of all good cards that were in starting hands
|
// A list of cards (set up once upon initialization) of all good cards that were in starting hands
|
||||||
std::array<boost::container::static_vector<Card, hand_size>, num_players> _good_cards_in_initial_draw_pile;
|
std::array<boost::container::static_vector<Card, hand_size>, num_players> _good_cards_in_initial_draw_pile;
|
||||||
|
|
|
@ -77,7 +77,7 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
for(player_t player = 0; player < num_players; player++) {
|
for(player_t player = 0; player < num_players; player++) {
|
||||||
for(std::uint8_t index = 0; index < hand_size; index++) {
|
for(std::uint8_t index = 0; index < hand_size; index++) {
|
||||||
draw(index);
|
draw<false>(index);
|
||||||
}
|
}
|
||||||
incr_turn();
|
incr_turn();
|
||||||
}
|
}
|
||||||
|
@ -127,15 +127,16 @@ namespace Hanabi {
|
||||||
BacktrackAction HanabiState<num_suits, num_players, hand_size>::play(Hanabi::hand_index_t index) {
|
BacktrackAction HanabiState<num_suits, num_players, hand_size>::play(Hanabi::hand_index_t index) {
|
||||||
const Card card = _hands[_turn][index];
|
const Card card = _hands[_turn][index];
|
||||||
if (!is_playable(card)) {
|
if (!is_playable(card)) {
|
||||||
BacktrackAction ret{card, index, draw(index)};
|
BacktrackAction ret{card, index, draw<false>(index)};
|
||||||
incr_turn();
|
incr_turn();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return play_no_strike(index);
|
return play_and_potentially_update<false>(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
BacktrackAction HanabiState<num_suits, num_players, hand_size>::play_no_strike(Hanabi::hand_index_t index) {
|
template<bool update_card_positions>
|
||||||
|
BacktrackAction HanabiState<num_suits, num_players, hand_size>::play_and_potentially_update(hand_index_t index) {
|
||||||
ASSERT(index < _hands[_turn].size());
|
ASSERT(index < _hands[_turn].size());
|
||||||
const Card card = _hands[_turn][index];
|
const Card card = _hands[_turn][index];
|
||||||
ASSERT(is_playable(card));
|
ASSERT(is_playable(card));
|
||||||
|
@ -148,7 +149,7 @@ namespace Hanabi {
|
||||||
_num_clues++;
|
_num_clues++;
|
||||||
}
|
}
|
||||||
|
|
||||||
BacktrackAction ret{card, index, draw(index)};
|
BacktrackAction ret{card, index, draw<update_card_positions>(index)};
|
||||||
|
|
||||||
incr_turn();
|
incr_turn();
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -156,6 +157,12 @@ namespace Hanabi {
|
||||||
|
|
||||||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
BacktrackAction HanabiState<num_suits, num_players, hand_size>::discard(std::uint8_t index) {
|
BacktrackAction HanabiState<num_suits, num_players, hand_size>::discard(std::uint8_t index) {
|
||||||
|
return discard_and_potentially_update<false>(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
|
template<bool update_card_positions>
|
||||||
|
BacktrackAction HanabiState<num_suits, num_players, hand_size>::discard_and_potentially_update(hand_index_t index) {
|
||||||
ASSERT(index < _hands[_turn].size());
|
ASSERT(index < _hands[_turn].size());
|
||||||
ASSERT(_num_clues != max_num_clues);
|
ASSERT(_num_clues != max_num_clues);
|
||||||
|
|
||||||
|
@ -163,7 +170,7 @@ namespace Hanabi {
|
||||||
_num_clues++;
|
_num_clues++;
|
||||||
_pace--;
|
_pace--;
|
||||||
|
|
||||||
BacktrackAction ret{discarded, index, draw(index)};
|
BacktrackAction ret{discarded, index, draw<update_card_positions>(index)};
|
||||||
|
|
||||||
incr_turn();
|
incr_turn();
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -203,6 +210,7 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
|
template<bool update_card_positions>
|
||||||
std::uint8_t HanabiState<num_suits, num_players, hand_size>::draw(uint8_t index) {
|
std::uint8_t HanabiState<num_suits, num_players, hand_size>::draw(uint8_t index) {
|
||||||
ASSERT(index < _hands[_turn].size());
|
ASSERT(index < _hands[_turn].size());
|
||||||
|
|
||||||
|
@ -219,6 +227,26 @@ namespace Hanabi {
|
||||||
_draw_pile.back().multiplicity--;
|
_draw_pile.back().multiplicity--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if constexpr(update_card_positions) {
|
||||||
|
const Card discarded = _hands[_turn][index];
|
||||||
|
if (!discarded.initial_trash) {
|
||||||
|
if (discarded.was_in_initial_hand) {
|
||||||
|
ASSERT(_card_positions_hands[_turn][index] == true);
|
||||||
|
_card_positions_hands[_turn][index] = false;
|
||||||
|
} else {
|
||||||
|
auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded], _turn);
|
||||||
|
ASSERT(replaced_card_it != _card_positions_draw[discarded].end());
|
||||||
|
*replaced_card_it = trash_or_play_stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
*new_card_it = _turn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_hands[_turn][index] = draw.card;
|
_hands[_turn][index] = draw.card;
|
||||||
|
|
||||||
if(_draw_pile.empty()) {
|
if(_draw_pile.empty()) {
|
||||||
|
@ -244,9 +272,27 @@ namespace Hanabi {
|
||||||
} else {
|
} else {
|
||||||
_draw_pile.push_back({drawn, 1});
|
_draw_pile.push_back({drawn, 1});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
*drawn_card_it = draw_pile;
|
||||||
|
}
|
||||||
|
|
||||||
_weighted_draw_pile_size++;
|
_weighted_draw_pile_size++;
|
||||||
_endgame_turns_left = no_endgame;
|
_endgame_turns_left = no_endgame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!discarded_card.initial_trash) {
|
||||||
|
if (discarded_card.was_in_initial_hand) {
|
||||||
|
ASSERT(_card_positions_hands[_turn][index] == false);
|
||||||
|
_card_positions_hands[_turn][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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_hands[_turn][index] = discarded_card;
|
_hands[_turn][index] = discarded_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +302,7 @@ namespace Hanabi {
|
||||||
const Card trash = [this]() -> Card {
|
const Card trash = [this]() -> Card {
|
||||||
for(suit_t suit = 0; suit < num_suits; suit++) {
|
for(suit_t suit = 0; suit < num_suits; suit++) {
|
||||||
if(_stacks[suit] < starting_card_rank) {
|
if(_stacks[suit] < starting_card_rank) {
|
||||||
return {suit, starting_card_rank - 1};
|
return {suit, starting_card_rank - 1, false, true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {0,0};
|
return {0,0};
|
||||||
|
@ -275,12 +321,13 @@ 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, false};
|
Card card {suit, rank, false, is_trash(card)};
|
||||||
|
_card_positions_draw[card].clear();
|
||||||
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[card].clear();
|
|
||||||
_card_positions_draw[card].resize(nums_in_draw_pile[card], draw_pile);
|
_card_positions_draw[card].resize(nums_in_draw_pile[card], draw_pile);
|
||||||
|
std::cout << _card_positions_draw[card].size() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,18 +336,20 @@ namespace Hanabi {
|
||||||
// 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++) {
|
||||||
for(Card& card : _hands[player]) {
|
for(Card& card : _hands[player]) {
|
||||||
|
card.was_in_initial_hand = true;
|
||||||
|
card.initial_trash = is_trash(card);
|
||||||
if(!is_trash(card)) {
|
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) {
|
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
|
// This card is already in hand, so just replace the second copy by some trash
|
||||||
card = trash;
|
card = trash;
|
||||||
} else {
|
} else {
|
||||||
_good_cards_in_initial_draw_pile[player].push_back(card);
|
_good_cards_in_initial_draw_pile[player].push_back(card);
|
||||||
card.was_in_initial_hand = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_card_positions_hands[player].reset();
|
||||||
|
_card_positions_hands[player].flip();
|
||||||
}
|
}
|
||||||
_card_positions_draw.fill(draw_pile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
|
@ -358,7 +407,7 @@ namespace Hanabi {
|
||||||
if(is_playable(hand[index])) {
|
if(is_playable(hand[index])) {
|
||||||
if (_draw_pile.empty()) {
|
if (_draw_pile.empty()) {
|
||||||
bool on_8_clues = _num_clues == 8;
|
bool on_8_clues = _num_clues == 8;
|
||||||
BacktrackAction action = play_no_strike(index);
|
BacktrackAction action = play_and_potentially_update<true>(index);
|
||||||
const double probability_for_this_play = backtrack(depth + 1);
|
const double probability_for_this_play = backtrack(depth + 1);
|
||||||
revert_play(action, on_8_clues);
|
revert_play(action, on_8_clues);
|
||||||
UPDATE_PROBABILITY(probability_for_this_play);
|
UPDATE_PROBABILITY(probability_for_this_play);
|
||||||
|
@ -367,7 +416,7 @@ namespace Hanabi {
|
||||||
uint8_t sum_of_mults = 0;
|
uint8_t sum_of_mults = 0;
|
||||||
for (size_t i = 0; i < _draw_pile.size(); i++) {
|
for (size_t i = 0; i < _draw_pile.size(); i++) {
|
||||||
bool on_8_clues = _num_clues == 8;
|
bool on_8_clues = _num_clues == 8;
|
||||||
BacktrackAction action = play_no_strike(index);
|
BacktrackAction action = play_and_potentially_update<true>(index);
|
||||||
sum_of_probabilities += backtrack(depth + 1) * action.multiplicity;
|
sum_of_probabilities += backtrack(depth + 1) * action.multiplicity;
|
||||||
sum_of_mults += action.multiplicity;
|
sum_of_mults += action.multiplicity;
|
||||||
revert_play(action, on_8_clues);
|
revert_play(action, on_8_clues);
|
||||||
|
@ -386,14 +435,14 @@ namespace Hanabi {
|
||||||
if (is_trash(hand[index])) {
|
if (is_trash(hand[index])) {
|
||||||
double sum_of_probabilities = 0;
|
double sum_of_probabilities = 0;
|
||||||
if (_draw_pile.empty()) {
|
if (_draw_pile.empty()) {
|
||||||
BacktrackAction action = discard(index);
|
BacktrackAction action = discard_and_potentially_update<true>(index);
|
||||||
const double probability_for_this_discard = backtrack(depth + 1);
|
const double probability_for_this_discard = backtrack(depth + 1);
|
||||||
revert_discard(action);
|
revert_discard(action);
|
||||||
UPDATE_PROBABILITY(probability_for_this_discard);
|
UPDATE_PROBABILITY(probability_for_this_discard);
|
||||||
} else {
|
} else {
|
||||||
uint8_t sum_of_mults = 0;
|
uint8_t sum_of_mults = 0;
|
||||||
for (size_t i = 0; i < _draw_pile.size(); i++) {
|
for (size_t i = 0; i < _draw_pile.size(); i++) {
|
||||||
BacktrackAction action = discard(index);
|
BacktrackAction action = discard_and_potentially_update<true>(index);
|
||||||
sum_of_probabilities += backtrack(depth + 1) * action.multiplicity;
|
sum_of_probabilities += backtrack(depth + 1) * action.multiplicity;
|
||||||
sum_of_mults += action.multiplicity;
|
sum_of_mults += action.multiplicity;
|
||||||
revert_discard(action);
|
revert_discard(action);
|
||||||
|
|
Loading…
Reference in a new issue