diff --git a/game_state.h b/game_state.h index 21726bc..7a73e2b 100644 --- a/game_state.h +++ b/game_state.h @@ -146,7 +146,7 @@ public: HanabiState() = default; explicit HanabiState(const std::vector& deck); - double backtrack(); + double backtrack(size_t depth); BacktrackAction clue(); @@ -166,7 +166,7 @@ public: uint8_t draw(uint8_t index); - void revert_draw(std::uint8_t index, Card card); + void revert_draw(std::uint8_t index, Card discarded_card); void incr_turn(); @@ -184,6 +184,8 @@ public: std::list _draw_pile{}; std::uint8_t endgame_turns_left; + static constexpr uint8_t no_endgame = std::numeric_limits::max(); + // further statistics that we might want to keep track of uint8_t _pace{}; uint8_t _score{}; diff --git a/game_state.hpp b/game_state.hpp index ea0885d..957524f 100644 --- a/game_state.hpp +++ b/game_state.hpp @@ -94,7 +94,7 @@ namespace Hanabi { template void HanabiState::incr_turn() { _turn = (_turn + 1) % num_players; - if(endgame_turns_left != -1) { + if(endgame_turns_left != no_endgame) { endgame_turns_left--; } } @@ -102,8 +102,11 @@ namespace Hanabi { template void HanabiState::decr_turn() { _turn = (_turn + num_players - 1) % num_players; - if (endgame_turns_left != -1) { + if (endgame_turns_left != no_endgame) { endgame_turns_left++; + if(endgame_turns_left == num_players) { + endgame_turns_left = no_endgame; + } } } @@ -229,26 +232,29 @@ namespace Hanabi { } template - void HanabiState::revert_draw(std::uint8_t index, Card card) { - endgame_turns_left = -1; - assert(index < _hands[_turn].size()); - const Card& discarded = _hands[_turn][index]; - if (_stacks[discarded.suit] > discarded.rank) { - _card_positions[discarded] = draw_pile; - } + void HanabiState::revert_draw(std::uint8_t index, Card discarded_card) { + if (endgame_turns_left == no_endgame) { + // Put the card that is currently in hand back into the draw pile + assert(index < _hands[_turn].size()); + const Card &drawn = _hands[_turn][index]; + if (_stacks[drawn.suit] > drawn.rank) { + _card_positions[drawn] = draw_pile; + } - // put card back into draw pile (at the back) - if (!_draw_pile.empty() and _draw_pile.back().card.suit == _hands[_turn][index].suit and _draw_pile.back().card.rank == _hands[_turn][index].rank) { - _draw_pile.back().multiplicity++; - } else { - _draw_pile.push_back({_hands[_turn][index], 1}); + // put discarded_card back into draw pile (at the back) + if (!_draw_pile.empty() and _draw_pile.back().card.suit == drawn.suit and + _draw_pile.back().card.rank == drawn.rank) { + _draw_pile.back().multiplicity++; + } else { + _draw_pile.push_back({drawn, 1}); + } + _weighted_draw_pile_size++; } - - _hands[_turn][index] = card; - if (_stacks[card.suit] > card.rank) { - _card_positions[card] = _turn; + endgame_turns_left = no_endgame; + _hands[_turn][index] = discarded_card; + if (_stacks[discarded_card.suit] > discarded_card.rank) { + _card_positions[discarded_card] = _turn; } - _weighted_draw_pile_size++; } template @@ -331,7 +337,7 @@ namespace Hanabi { } template - double HanabiState::backtrack() { + double HanabiState::backtrack(size_t depth) { std::cout << *this << std::endl; if (_score == 5 * num_suits) { return 1; @@ -349,9 +355,10 @@ namespace Hanabi { // First, check for playables for(std::uint8_t index = 0; index < hand_size; index++) { if(is_playable(hand[index])) { + std::cout << std::string("---------------------", depth) << "playing " << hand[index] << std::endl; if (_draw_pile.empty()) { BacktrackAction action = play(index); - const double probability_for_this_play = backtrack(); + const double probability_for_this_play = backtrack(depth + 1); revert(action); UPDATE_PROBABILITY(probability_for_this_play); } else { @@ -359,7 +366,7 @@ namespace Hanabi { uint8_t sum_of_mults = 0; for (size_t i = 0; i < _draw_pile.size(); i++) { BacktrackAction action = play(index); - sum_of_probabilities += backtrack() * action.multiplicity; + sum_of_probabilities += backtrack(depth + 1) * action.multiplicity; sum_of_mults += action.multiplicity; revert(action); assert(sum_of_mults <= _weighted_draw_pile_size); @@ -375,18 +382,22 @@ namespace Hanabi { if(_pace > 0) { for(std::uint8_t index = 0; index < hand_size; index++) { if (is_trash(hand[index])) { + std::cout << std::string("---------------------------", depth) << "discarding " << hand[index] << std::endl; double sum_of_probabilities = 0; if (_draw_pile.empty()) { BacktrackAction action = discard(index); - const double probability_for_this_discard = backtrack(); + const double probability_for_this_discard = backtrack(depth + 1); revert(action); UPDATE_PROBABILITY(probability_for_this_discard); } else { + uint8_t sum_of_mults = 0; for (size_t i = 0; i < _draw_pile.size(); i++) { BacktrackAction action = discard(index); - sum_of_probabilities += backtrack() * action.multiplicity; + sum_of_probabilities += backtrack(depth + 1) * action.multiplicity; + sum_of_mults += action.multiplicity; revert(action); } + assert(sum_of_mults == _weighted_draw_pile_size); const double probability_discard = sum_of_probabilities / _weighted_draw_pile_size; UPDATE_PROBABILITY(probability_discard); } @@ -399,8 +410,9 @@ namespace Hanabi { // Last option is to stall if(_num_clues > 0) { + std::cout << std::string("--------------------", depth) << "stalling " << std::endl; BacktrackAction action = clue(); - const double probability_stall = backtrack(); + const double probability_stall = backtrack(depth + 1); revert(action); UPDATE_PROBABILITY(probability_stall); } diff --git a/main.cpp b/main.cpp index 85be21a..838dac3 100644 --- a/main.cpp +++ b/main.cpp @@ -39,10 +39,10 @@ void test_game() { } void download() { - auto game = Download::get_game<4,3,5>("1004480.json", 36); - std::cout << game << std::endl; - auto res = game.backtrack(); - std::cout << res << std::endl; + auto game = Download::get_game<4,3,5>("1004480.json", 30); +// std::cout << game << std::endl; + auto res = game.backtrack(1); + std::cout << "Probability with optimal play: " << res << std::endl; } void print_sizes() {