diff --git a/download.h b/download.h index cb82d7c..cba4405 100644 --- a/download.h +++ b/download.h @@ -102,13 +102,13 @@ namespace Download { return boost::json::parse(game_json).as_object(); } - template - Hanabi::HanabiState produce_state( + template + Hanabi::HanabiState produce_state( const std::vector& deck, const std::vector& actions, size_t num_turns_to_replicate ) { - Hanabi::HanabiState game(deck); + Hanabi::HanabiState game(deck); std::uint8_t index; for (size_t i = 0; i < num_turns_to_replicate; i++) { switch(actions[i].type) { @@ -134,7 +134,8 @@ namespace Download { return game; } - void get_game(std::variant game_spec) { + template + void get_game(std::variant game_spec, unsigned turn) { const boost::json::object game_json = [&game_spec]() { if (game_spec.index() == 0) { return download_game_json(std::get(game_spec)); @@ -144,11 +145,11 @@ namespace Download { }(); const std::vector deck = parse_deck(game_json.at("deck")); const std::vector actions = parse_actions(game_json.at("actions")); - const unsigned num_players = game_json.at("players").as_array().size(); - std::cout << deck.size() << std::endl; - std::cout << num_players; + const size_t num_players_js = game_json.at("players").as_array().size(); + assert (num_players_js == num_players); - auto game = produce_state<6, 3, 5, 0>(deck, actions, 40); + auto game = produce_state(deck, actions, turn); + game.normalize_draw_and_positions(); std::cout << game << std::endl; } diff --git a/game_state.h b/game_state.h index 29d9baf..752d993 100644 --- a/game_state.h +++ b/game_state.h @@ -87,18 +87,37 @@ struct CardMultiplicity { auto operator<=>(const CardMultiplicity &) const = default; }; -template struct CardPositions { +template +struct CardArrayMember { +}; - CardPositions(); +template +struct CardArrayMember { + auto operator<=>(const CardArrayMember &) const = default; + std::array, starting_card_rank>, num_suits> array {}; +}; - const player_t &operator[](const Card &card) const; +template +struct CardArrayMember { + auto operator<=>(const CardArrayMember &) const = default; + std::array, num_suits> array {}; +}; - player_t &operator[](const Card &card); +template struct CardArray { - auto operator<=>(const CardPositions &) const = default; + using value_type = T; + + CardArray() = default; + explicit CardArray(value_type default_val); + + const value_type &operator[](const Card &card) const; + + value_type &operator[](const Card &card); + + auto operator<=>(const CardArray &) const = default; private: - std::array, starting_card_rank>, num_suits> _card_positions; + CardArrayMember _vals; }; enum class ActionType { @@ -117,7 +136,7 @@ struct BacktrackAction { std::uint8_t index{}; }; -template +template class HanabiState { public: HanabiState() = default; @@ -135,6 +154,8 @@ public: std::uint8_t find_card_in_hand(const Card& card) const; + void normalize_draw_and_positions(); + void revert(const BacktrackAction &action); void draw(std::uint8_t index); @@ -150,7 +171,7 @@ public: std::uint8_t _draw_pile_size{}; Stacks _stacks{}; std::array, num_players> _hands{}; - CardPositions _card_positions{}; + CardArray _card_positions{}; std::list _draw_pile{}; // further statistics that we might want to keep track of @@ -159,10 +180,10 @@ public: auto operator<=>(const HanabiState &) const = default; }; -template -std::ostream & operator<<(std::ostream &os, HanabiState hanabi_state); +template +std::ostream & operator<<(std::ostream &os, HanabiState hanabi_state); -template class HanabiState<5, 3, 4, 20>; +template class HanabiState<5, 3, 4>; } diff --git a/game_state.hpp b/game_state.hpp index 12ba3e1..871f129 100644 --- a/game_state.hpp +++ b/game_state.hpp @@ -20,39 +20,51 @@ namespace Hanabi { template std::ostream &operator<<(std::ostream &os, const Stacks &stacks) { for (size_t i = 0; i < stacks.size() - 1; i++) { - os << +stacks[i] << ", "; + os << starting_card_rank - stacks[i] << ", "; } - os << +stacks.back(); + os << starting_card_rank - stacks.back(); return os; } - template - CardPositions::CardPositions() { + template + CardArray::CardArray(T default_val) { for(size_t suit = 0; suit < num_suits; suit++) { - for(rank_t rank = 0; rank < starting_card_rank; rank++) { - std::ranges::fill(_card_positions[suit][rank], draw_pile); + for (rank_t rank = 0; rank < starting_card_rank; rank++) { + if constexpr (respect_card_duplicity) { + std::ranges::fill(_vals.array[suit][rank], default_val); + } else { + _vals.array[suit][rank] = default_val; + } } } } - template - const player_t &CardPositions::operator[](const Card &card) const { - return _card_positions[card.suit][card.rank][card.copy]; + template + const T& CardArray::operator[](const Card &card) const { + if constexpr (respect_card_duplicity) { + return _vals.array[card.suit][card.rank][card.copy]; + } else { + return _vals.array[card.suit][card.rank]; + } }; - template - player_t &CardPositions::operator[](const Card &card) { - return _card_positions[card.suit][card.rank][card.copy]; + template + T& CardArray::operator[](const Card &card) { + if constexpr (respect_card_duplicity) { + return _vals.array[card.suit][card.rank][card.copy]; + } else { + return _vals.array[card.suit][card.rank]; + } }; - template - HanabiState::HanabiState(const std::vector &deck): + template + HanabiState::HanabiState(const std::vector &deck): _turn(0), _num_clues(max_num_clues), _draw_pile_size(deck.size() - num_players * hand_size), _stacks(), _hands(), - _card_positions(), + _card_positions(draw_pile), _draw_pile() { std::ranges::fill(_stacks, starting_card_rank); for(const Card& card: deck) { @@ -67,8 +79,8 @@ namespace Hanabi { assert(_turn == 0); } - template - BacktrackAction HanabiState::clue() { + template + BacktrackAction HanabiState::clue() { assert(_num_clues > 0); --_num_clues; @@ -77,20 +89,18 @@ namespace Hanabi { return BacktrackAction{ActionType::clue, {}, {}}; } - template - void HanabiState::incr_turn() { + template + void HanabiState::incr_turn() { _turn = (_turn + 1) % num_players; } - template - void HanabiState::decr_turn() { + template + void HanabiState::decr_turn() { _turn = (_turn + num_players - 1) % num_players; } - template - BacktrackAction HanabiState::play( + template + BacktrackAction HanabiState::play( std::uint8_t index) { assert(index < _hands[_turn].size()); const Card card = _hands[_turn][index]; @@ -111,8 +121,8 @@ namespace Hanabi { return ret; } - template - BacktrackAction HanabiState::discard( + template + BacktrackAction HanabiState::discard( std::uint8_t index) { assert(index < _hands[_turn].size()); assert(_num_clues != max_num_clues); @@ -127,8 +137,8 @@ namespace Hanabi { return ret; } - template - std::uint8_t HanabiState::find_card_in_hand( + template + std::uint8_t HanabiState::find_card_in_hand( const Hanabi::Card &card) const { for(std::uint8_t i = 0; i < hand_size; i++) { if(_hands[_turn][i].rank == card.rank && _hands[_turn][i].suit == card.suit) { @@ -138,8 +148,8 @@ namespace Hanabi { return -1; } - template - std::ostream &operator<<(std::ostream &os, const HanabiState hanabi_state) { + template + std::ostream &operator<<(std::ostream &os, const HanabiState hanabi_state) { os << "Stacks: " << hanabi_state._stacks << std::endl; os << "Draw pile: "; for (const auto &[card, mul]: hanabi_state._draw_pile) { @@ -147,6 +157,7 @@ namespace Hanabi { if (mul > 1) { os << " (" << +mul << ")"; } + os << ", "; } os << std::endl; os << "Hands: "; @@ -159,11 +170,14 @@ namespace Hanabi { return os; } - template - void HanabiState::draw(std::uint8_t index) { + template + void HanabiState::draw(std::uint8_t index) { assert(index < _hands[_turn].size()); - _card_positions[_hands[_turn][index]] = trash_or_play_stack; + const Card& discarded = _hands[_turn][index]; + if (_stacks[discarded.suit] > discarded.rank) { + _card_positions[_hands[_turn][index]] = trash_or_play_stack; + } // draw a new card if the draw pile is not empty if (!_draw_pile.empty()) { @@ -175,16 +189,21 @@ namespace Hanabi { draw.multiplicity--; _draw_pile.push_back(draw); } + draw.card.copy = draw.multiplicity - 1; _hands[_turn][index] = draw.card; - _card_positions[draw.card] = _turn; + if (_stacks[draw.card.suit] > draw.card.rank) { + _card_positions[draw.card] = _turn; + } } } - template - void HanabiState::revert_draw(std::uint8_t index, Card card) { + template + void HanabiState::revert_draw(std::uint8_t index, Card card) { assert(index < _hands[_turn].size()); - - _card_positions[_hands[_turn][index]] = draw_pile; + const Card& discarded = _hands[_turn][index]; + if (_stacks[discarded.suit] > discarded.rank) { + _card_positions[discarded] = draw_pile; + } // put card back into draw pile (at the back) if (!_draw_pile.empty() and _draw_pile.back().card == _hands[_turn][index]) { @@ -194,12 +213,60 @@ namespace Hanabi { } _hands[_turn][index] = card; - _card_positions[card] = _turn; + if (_stacks[card.suit] > card.rank) { + _card_positions[card] = _turn; + } _draw_pile_size++; } - template - void HanabiState::revert( + template + void HanabiState::normalize_draw_and_positions() { + 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, 0}; + } + } + return {0,0,0}; + }(); + + CardArray nums_in_draw_pile; + std::uint8_t num_trash_in_draw_pile = 0; + for(const auto [card, multiplicity] : _draw_pile) { + if (_stacks[card.suit] > card.rank) { + nums_in_draw_pile[card] += multiplicity; + } else { + num_trash_in_draw_pile++; + } + } + + _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, 0}; + if (nums_in_draw_pile[card] > 0) { + _draw_pile.push_back({card, nums_in_draw_pile[card]}); + for (std::uint8_t copy = 0; copy < nums_in_draw_pile[card]; copy++) { + card.copy = copy; + _card_positions[card] = draw_pile; + } + } + } + } + _draw_pile.push_back({trash, num_trash_in_draw_pile}); + + for(player_t player = 0; player < num_players; player++) { + for(Card& card : _hands[player]) { + if (_stacks[card.suit] > card.rank) { + card.copy = nums_in_draw_pile[card]; + nums_in_draw_pile[card]++; + } + } + } + } + + template + void HanabiState::revert( const BacktrackAction &action) { decr_turn(); switch (action.type) { diff --git a/main.cpp b/main.cpp index 77d4f5f..79cc03a 100644 --- a/main.cpp +++ b/main.cpp @@ -14,7 +14,7 @@ namespace Hanabi { void test_game() { - HanabiState<2, 2, 5, 10> state; + HanabiState<2, 2, 5> state; state._stacks[0] = 2; state._stacks[1] = 3; Card r41 = {0, 4, 1}; @@ -38,10 +38,10 @@ void test_game() { assert(state == state2); } -void download() { Download::get_game("1004116.json"); } +void download() { Download::get_game<6,3,5>("1004116.json", 40); } void print_sizes() { - std::cout << "size of card -> hand map: " << sizeof(HanabiState<5, 3, 4, 5>) + std::cout << "size of card -> hand map: " << sizeof(HanabiState<5, 3, 4>) << std::endl; unsigned exp = 32;