compact storage of positions of draw pile cards
This commit is contained in:
parent
0100e8c987
commit
f29e3d1202
2 changed files with 60 additions and 42 deletions
49
game_state.h
49
game_state.h
|
@ -15,13 +15,13 @@
|
|||
|
||||
namespace Hanabi {
|
||||
|
||||
using rank_t = std::uint8_t;
|
||||
using suit_t = std::uint8_t;
|
||||
using clue_t = std::uint8_t;
|
||||
using player_t = std::int8_t;
|
||||
using hand_index_t = std::uint8_t;
|
||||
using rank_t = std::uint8_t;
|
||||
using suit_t = std::uint8_t;
|
||||
using clue_t = std::uint8_t;
|
||||
using player_t = std::uint8_t;
|
||||
using hand_index_t = std::uint8_t;
|
||||
|
||||
using state_t = std::uint32_t;
|
||||
using state_t = std::uint32_t;
|
||||
|
||||
/**
|
||||
* We will generally assume that stacks are played from n to 0
|
||||
|
@ -31,25 +31,39 @@ using state_t = std::uint32_t;
|
|||
* This is just easier to implement, since then the remaining number of cards
|
||||
* to be played is always the current number of the stack
|
||||
*/
|
||||
constexpr rank_t starting_card_rank = 5;
|
||||
constexpr suit_t max_suit_index = 5;
|
||||
constexpr size_t max_card_duplicity = 3;
|
||||
constexpr clue_t max_num_clues = 8;
|
||||
constexpr uint8_t not_in_starting_hand = std::numeric_limits<uint8_t>::max();
|
||||
constexpr rank_t starting_card_rank = 5;
|
||||
constexpr suit_t max_suit_index = 5;
|
||||
constexpr size_t max_card_duplicity = 3;
|
||||
constexpr clue_t max_num_clues = 8;
|
||||
constexpr uint8_t not_in_starting_hand = std::numeric_limits<uint8_t>::max();
|
||||
|
||||
constexpr std::array<std::string, 6> suit_initials{"r", "y", "g", "b", "p", "t"};
|
||||
constexpr std::array<std::string, 6> suit_initials{"r", "y", "g", "b", "p", "t"};
|
||||
|
||||
struct Card {
|
||||
struct Card {
|
||||
suit_t suit;
|
||||
rank_t rank;
|
||||
uint8_t local_index_among_good_starting_hand_cards;
|
||||
uint8_t local_index;
|
||||
bool in_starting_hand;
|
||||
bool initial_trash;
|
||||
|
||||
Card &operator++();
|
||||
|
||||
const Card operator++(int);
|
||||
|
||||
auto operator<=>(const Card &) const = default;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<Hanabi::Card> {
|
||||
std::size_t operator()(Hanabi::Card const& card) const noexcept {
|
||||
return card.suit * 6 + card.rank;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Hanabi {
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const Card &card);
|
||||
|
||||
|
@ -213,11 +227,12 @@ private:
|
|||
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
|
||||
CardArray<num_suits, boost::container::static_vector<player_t, max_card_duplicity>> _card_positions_draw;
|
||||
// TODO: currently, we support at most 10 useful different cards in draw pile
|
||||
boost::container::static_vector<boost::container::static_vector<player_t, max_card_duplicity>, 10> _card_positions_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;
|
||||
uint8_t _num_useful_cards_in_starting_hands;
|
||||
|
||||
// further statistics that we might want to keep track of
|
||||
uint8_t _pace{};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "myassert.h"
|
||||
#include "game_state.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace Hanabi {
|
||||
|
||||
|
@ -222,12 +223,12 @@ namespace Hanabi {
|
|||
if constexpr(update_card_positions) {
|
||||
const Card discarded = _hands[_turn][index];
|
||||
if (!discarded.initial_trash) {
|
||||
if (discarded.local_index_among_good_starting_hand_cards != not_in_starting_hand) {
|
||||
ASSERT(_card_positions_hands[discarded.local_index_among_good_starting_hand_cards] == true);
|
||||
_card_positions_hands[discarded.local_index_among_good_starting_hand_cards] = false;
|
||||
if (discarded.in_starting_hand) {
|
||||
ASSERT(_card_positions_hands[discarded.local_index] == true);
|
||||
_card_positions_hands[discarded.local_index] = false;
|
||||
} else {
|
||||
auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded], _turn);
|
||||
ASSERT(replaced_card_it != _card_positions_draw[discarded].end());
|
||||
auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded.local_index], _turn);
|
||||
ASSERT(replaced_card_it != _card_positions_draw[discarded.local_index].end());
|
||||
*replaced_card_it = trash_or_play_stack;
|
||||
}
|
||||
}
|
||||
|
@ -249,8 +250,9 @@ namespace Hanabi {
|
|||
if constexpr(update_card_positions) {
|
||||
// update card position of the drawn card
|
||||
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());
|
||||
ASSERT(draw.card.in_starting_hand == false);
|
||||
auto new_card_it = std::ranges::find(_card_positions_draw[draw.card.local_index], draw_pile);
|
||||
ASSERT(new_card_it != _card_positions_draw[draw.card.local_index].end());
|
||||
*new_card_it = _turn;
|
||||
}
|
||||
}
|
||||
|
@ -282,8 +284,9 @@ namespace Hanabi {
|
|||
}
|
||||
|
||||
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());
|
||||
ASSERT(drawn.in_starting_hand == false);
|
||||
auto drawn_card_it = std::ranges::find(_card_positions_draw[drawn.local_index], _turn);
|
||||
ASSERT(drawn_card_it != _card_positions_draw[drawn.local_index].end());
|
||||
*drawn_card_it = draw_pile;
|
||||
}
|
||||
|
||||
|
@ -294,12 +297,12 @@ namespace Hanabi {
|
|||
}
|
||||
|
||||
if (!discarded_card.initial_trash) {
|
||||
if (discarded_card.local_index_among_good_starting_hand_cards != not_in_starting_hand) {
|
||||
ASSERT(_card_positions_hands[discarded_card.local_index_among_good_starting_hand_cards] == false);
|
||||
_card_positions_hands[discarded_card.local_index_among_good_starting_hand_cards] = true;
|
||||
if (discarded_card.in_starting_hand) {
|
||||
ASSERT(_card_positions_hands[discarded_card.local_index] == false);
|
||||
_card_positions_hands[discarded_card.local_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());
|
||||
auto hand_card_it = std::ranges::find(_card_positions_draw[discarded_card.local_index], trash_or_play_stack);
|
||||
ASSERT(hand_card_it != _card_positions_draw[discarded_card.local_index].end());
|
||||
*hand_card_it = _turn;
|
||||
}
|
||||
}
|
||||
|
@ -313,7 +316,7 @@ namespace Hanabi {
|
|||
const Card trash = [this]() -> Card {
|
||||
for(suit_t suit = 0; suit < num_suits; suit++) {
|
||||
if(_stacks[suit] < starting_card_rank) {
|
||||
return {suit, starting_card_rank - 1, not_in_starting_hand, true};
|
||||
return {suit, starting_card_rank - 1, 0, false, true};
|
||||
}
|
||||
}
|
||||
return {0,0};
|
||||
|
@ -332,12 +335,11 @@ namespace Hanabi {
|
|||
_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, not_in_starting_hand, is_trash(card)};
|
||||
_card_positions_draw[card].clear();
|
||||
Card card {suit, rank, static_cast<uint8_t>(_card_positions_draw.size()), false, is_trash(card)};
|
||||
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].resize(nums_in_draw_pile[card], draw_pile);
|
||||
_card_positions_draw.push_back({nums_in_draw_pile[card], draw_pile});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -347,6 +349,7 @@ namespace Hanabi {
|
|||
for(player_t player = 0; player < num_players; player++) {
|
||||
for(Card& card : _hands[player]) {
|
||||
card.initial_trash = is_trash(card);
|
||||
card.in_starting_hand = true;
|
||||
// Needed to check for dupes in same hand
|
||||
boost::container::static_vector<Card, hand_size> good_cards_in_hand;
|
||||
if(!is_trash(card)) {
|
||||
|
@ -354,7 +357,7 @@ namespace Hanabi {
|
|||
// This card is already in hand, so just replace the second copy by some trash
|
||||
card = trash;
|
||||
} else {
|
||||
card.local_index_among_good_starting_hand_cards = _num_useful_cards_in_starting_hands;
|
||||
card.local_index = _num_useful_cards_in_starting_hands;
|
||||
_num_useful_cards_in_starting_hands++;
|
||||
|
||||
good_cards_in_hand.push_back(card);
|
||||
|
|
Loading…
Reference in a new issue