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
61
game_state.h
61
game_state.h
|
@ -15,13 +15,13 @@
|
||||||
|
|
||||||
namespace Hanabi {
|
namespace Hanabi {
|
||||||
|
|
||||||
using rank_t = std::uint8_t;
|
using rank_t = std::uint8_t;
|
||||||
using suit_t = std::uint8_t;
|
using suit_t = std::uint8_t;
|
||||||
using clue_t = std::uint8_t;
|
using clue_t = std::uint8_t;
|
||||||
using player_t = std::int8_t;
|
using player_t = std::uint8_t;
|
||||||
using hand_index_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
|
* 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
|
* This is just easier to implement, since then the remaining number of cards
|
||||||
* to be played is always the current number of the stack
|
* to be played is always the current number of the stack
|
||||||
*/
|
*/
|
||||||
constexpr rank_t starting_card_rank = 5;
|
constexpr rank_t starting_card_rank = 5;
|
||||||
constexpr suit_t max_suit_index = 5;
|
constexpr suit_t max_suit_index = 5;
|
||||||
constexpr size_t max_card_duplicity = 3;
|
constexpr size_t max_card_duplicity = 3;
|
||||||
constexpr clue_t max_num_clues = 8;
|
constexpr clue_t max_num_clues = 8;
|
||||||
constexpr uint8_t not_in_starting_hand = std::numeric_limits<uint8_t>::max();
|
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;
|
suit_t suit;
|
||||||
rank_t rank;
|
rank_t rank;
|
||||||
uint8_t local_index_among_good_starting_hand_cards;
|
uint8_t local_index;
|
||||||
bool initial_trash;
|
bool in_starting_hand;
|
||||||
|
bool initial_trash;
|
||||||
|
|
||||||
Card &operator++();
|
Card &operator++();
|
||||||
const Card operator++(int);
|
|
||||||
|
|
||||||
auto operator<=>(const Card &) const = default;
|
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);
|
std::ostream &operator<<(std::ostream &os, const Card &card);
|
||||||
|
|
||||||
|
@ -213,11 +227,12 @@ private:
|
||||||
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
|
// 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
|
// This will indicate whether cards that were in hands initially still are in hands
|
||||||
std::bitset<num_players * hand_size> _card_positions_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
|
// further statistics that we might want to keep track of
|
||||||
uint8_t _pace{};
|
uint8_t _pace{};
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "myassert.h"
|
#include "myassert.h"
|
||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace Hanabi {
|
namespace Hanabi {
|
||||||
|
|
||||||
|
@ -222,12 +223,12 @@ namespace Hanabi {
|
||||||
if constexpr(update_card_positions) {
|
if constexpr(update_card_positions) {
|
||||||
const Card discarded = _hands[_turn][index];
|
const Card discarded = _hands[_turn][index];
|
||||||
if (!discarded.initial_trash) {
|
if (!discarded.initial_trash) {
|
||||||
if (discarded.local_index_among_good_starting_hand_cards != not_in_starting_hand) {
|
if (discarded.in_starting_hand) {
|
||||||
ASSERT(_card_positions_hands[discarded.local_index_among_good_starting_hand_cards] == true);
|
ASSERT(_card_positions_hands[discarded.local_index] == true);
|
||||||
_card_positions_hands[discarded.local_index_among_good_starting_hand_cards] = false;
|
_card_positions_hands[discarded.local_index] = false;
|
||||||
} else {
|
} else {
|
||||||
auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded], _turn);
|
auto replaced_card_it = std::ranges::find(_card_positions_draw[discarded.local_index], _turn);
|
||||||
ASSERT(replaced_card_it != _card_positions_draw[discarded].end());
|
ASSERT(replaced_card_it != _card_positions_draw[discarded.local_index].end());
|
||||||
*replaced_card_it = trash_or_play_stack;
|
*replaced_card_it = trash_or_play_stack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,8 +250,9 @@ namespace Hanabi {
|
||||||
if constexpr(update_card_positions) {
|
if constexpr(update_card_positions) {
|
||||||
// update card position of the drawn card
|
// update card position of the drawn card
|
||||||
if (!draw.card.initial_trash) {
|
if (!draw.card.initial_trash) {
|
||||||
auto new_card_it = std::ranges::find(_card_positions_draw[draw.card], draw_pile);
|
ASSERT(draw.card.in_starting_hand == false);
|
||||||
ASSERT(new_card_it != _card_positions_draw[draw.card].end());
|
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;
|
*new_card_it = _turn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,8 +284,9 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!drawn.initial_trash) {
|
if (!drawn.initial_trash) {
|
||||||
auto drawn_card_it = std::ranges::find(_card_positions_draw[drawn], _turn);
|
ASSERT(drawn.in_starting_hand == false);
|
||||||
ASSERT(drawn_card_it != _card_positions_draw[drawn].end());
|
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;
|
*drawn_card_it = draw_pile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,12 +297,12 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!discarded_card.initial_trash) {
|
if (!discarded_card.initial_trash) {
|
||||||
if (discarded_card.local_index_among_good_starting_hand_cards != not_in_starting_hand) {
|
if (discarded_card.in_starting_hand) {
|
||||||
ASSERT(_card_positions_hands[discarded_card.local_index_among_good_starting_hand_cards] == false);
|
ASSERT(_card_positions_hands[discarded_card.local_index] == false);
|
||||||
_card_positions_hands[discarded_card.local_index_among_good_starting_hand_cards] = true;
|
_card_positions_hands[discarded_card.local_index] = true;
|
||||||
} else {
|
} else {
|
||||||
auto hand_card_it = std::ranges::find(_card_positions_draw[discarded_card], trash_or_play_stack);
|
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].end());
|
ASSERT(hand_card_it != _card_positions_draw[discarded_card.local_index].end());
|
||||||
*hand_card_it = _turn;
|
*hand_card_it = _turn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,7 +316,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, not_in_starting_hand, true};
|
return {suit, starting_card_rank - 1, 0, false, true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {0,0};
|
return {0,0};
|
||||||
|
@ -332,12 +335,11 @@ 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, not_in_starting_hand, is_trash(card)};
|
Card card {suit, rank, static_cast<uint8_t>(_card_positions_draw.size()), 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].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(player_t player = 0; player < num_players; player++) {
|
||||||
for(Card& card : _hands[player]) {
|
for(Card& card : _hands[player]) {
|
||||||
card.initial_trash = is_trash(card);
|
card.initial_trash = is_trash(card);
|
||||||
|
card.in_starting_hand = true;
|
||||||
// Needed to check for dupes in same hand
|
// Needed to check for dupes in same hand
|
||||||
boost::container::static_vector<Card, hand_size> good_cards_in_hand;
|
boost::container::static_vector<Card, hand_size> good_cards_in_hand;
|
||||||
if(!is_trash(card)) {
|
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
|
// This card is already in hand, so just replace the second copy by some trash
|
||||||
card = trash;
|
card = trash;
|
||||||
} else {
|
} 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++;
|
_num_useful_cards_in_starting_hands++;
|
||||||
|
|
||||||
good_cards_in_hand.push_back(card);
|
good_cards_in_hand.push_back(card);
|
||||||
|
|
Loading…
Reference in a new issue