diff --git a/game_state.h b/game_state.h index aa00b78..4a37f3b 100644 --- a/game_state.h +++ b/game_state.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace Hanabi { @@ -21,6 +22,7 @@ namespace Hanabi { using clue_t = std::uint8_t; using player_t = std::uint8_t; using hand_index_t = std::uint8_t; + using probability_t = boost::rational; using state_t = std::uint32_t; @@ -152,7 +154,7 @@ struct BacktrackAction { class HanabiStateIF { public: - virtual double backtrack(size_t depth) = 0; + virtual probability_t backtrack(size_t depth) = 0; virtual void clue() = 0; virtual BacktrackAction discard(hand_index_t index) = 0; @@ -164,7 +166,7 @@ public: [[nodiscard]] virtual size_t draw_pile_size() const = 0; [[nodiscard]] virtual std::uint64_t enumerated_states() const = 0; - [[nodiscard]] virtual std::unordered_map visited_states() const = 0; + [[nodiscard]] virtual std::unordered_map visited_states() const = 0; virtual void normalize_draw_and_positions() = 0; @@ -182,7 +184,7 @@ public: HanabiState() = default; explicit HanabiState(const std::vector& deck); - double backtrack(size_t depth) final; + probability_t backtrack(size_t depth) final; void clue() final; BacktrackAction play(hand_index_t index) final; @@ -198,7 +200,7 @@ public: [[nodiscard]] size_t draw_pile_size() const final; [[nodiscard]] std::uint64_t enumerated_states() const final; - [[nodiscard]] std::unordered_map visited_states() const final; + [[nodiscard]] std::unordered_map visited_states() const final; void normalize_draw_and_positions() final; @@ -248,7 +250,7 @@ private: std::uint64_t _enumerated_states {}; - std::unordered_map _position_tablebase; + std::unordered_map _position_tablebase; }; template diff --git a/game_state.hpp b/game_state.hpp index 44726d3..316279c 100644 --- a/game_state.hpp +++ b/game_state.hpp @@ -419,7 +419,7 @@ namespace Hanabi { } template - double HanabiState::backtrack(size_t depth) { + probability_t HanabiState::backtrack(size_t depth) { _enumerated_states++; const unsigned long id_of_state = unique_id(); @@ -436,7 +436,7 @@ namespace Hanabi { // TODO: Have some endgame analysis here? // First, check if we have any playable cards - double best_probability = 0; + probability_t best_probability = 0; const std::array hand = _hands[_turn]; // First, check for playables @@ -445,11 +445,11 @@ namespace Hanabi { if (_draw_pile.empty()) { bool on_8_clues = _num_clues == 8; BacktrackAction action = play_and_potentially_update(index); - const double probability_for_this_play = backtrack(depth + 1); + const probability_t probability_for_this_play = backtrack(depth + 1); revert_play(action, on_8_clues); UPDATE_PROBABILITY(probability_for_this_play); } else { - double sum_of_probabilities = 0; + probability_t sum_of_probabilities = 0; uint8_t sum_of_mults = 0; for (size_t i = 0; i < _draw_pile.size(); i++) { bool on_8_clues = _num_clues == 8; @@ -460,7 +460,7 @@ namespace Hanabi { ASSERT(sum_of_mults <= _weighted_draw_pile_size); } ASSERT(sum_of_mults == _weighted_draw_pile_size); - const double probability_for_this_play = sum_of_probabilities / _weighted_draw_pile_size; + const probability_t probability_for_this_play = sum_of_probabilities / _weighted_draw_pile_size; UPDATE_PROBABILITY(probability_for_this_play); } } @@ -470,10 +470,10 @@ namespace Hanabi { if(_pace > 0 and _num_clues < max_num_clues) { for(std::uint8_t index = 0; index < hand_size; index++) { if (is_trash(hand[index])) { - double sum_of_probabilities = 0; + probability_t sum_of_probabilities = 0; if (_draw_pile.empty()) { BacktrackAction action = discard_and_potentially_update(index); - const double probability_for_this_discard = backtrack(depth + 1); + const probability_t probability_for_this_discard = backtrack(depth + 1); revert_discard(action); UPDATE_PROBABILITY(probability_for_this_discard); } else { @@ -485,7 +485,7 @@ namespace Hanabi { revert_discard(action); } ASSERT(sum_of_mults == _weighted_draw_pile_size); - const double probability_discard = sum_of_probabilities / _weighted_draw_pile_size; + const probability_t probability_discard = sum_of_probabilities / _weighted_draw_pile_size; UPDATE_PROBABILITY(probability_discard); } @@ -498,7 +498,7 @@ namespace Hanabi { // Last option is to stall if(_num_clues > 0) { clue(); - const double probability_stall = backtrack(depth + 1); + const probability_t probability_stall = backtrack(depth + 1); revert_clue(); UPDATE_PROBABILITY(probability_stall); } @@ -559,7 +559,7 @@ namespace Hanabi { } template - std::unordered_map HanabiState::visited_states() const { + std::unordered_map HanabiState::visited_states() const { return _position_tablebase; } diff --git a/main.cpp b/main.cpp index 72d79de..f17545f 100644 --- a/main.cpp +++ b/main.cpp @@ -53,22 +53,22 @@ void test() { { auto game = Download::get_game("in/1005195", 43); auto res = game->backtrack(1); - CHECK("1005195", res == static_cast(7) / 8); + CHECK("1005195", res == Hanabi::probability_t (7,8)); } } void check_games(unsigned num_players, unsigned max_draw_pile_size, unsigned first_game = 0, unsigned last_game = 9999) { - std::vector> winning_percentages(last_game + 2); + std::vector> winning_percentages(last_game + 2); for(size_t draw_pile_size = 0; draw_pile_size <= max_draw_pile_size; draw_pile_size++) { - double total_chance = 0; + Hanabi::probability_t total_chance = 0; const std::string output_fname = "games_" + std::to_string(num_players) + "p_draw_size_" + std::to_string(draw_pile_size) + ".txt"; std::ofstream file (output_fname); for(size_t game_id = first_game; game_id <= last_game; game_id++) { const std::string input_fname = "json/" + std::to_string(num_players) + "p/" + std::to_string(game_id) + ".json"; auto game = Download::get_game(input_fname.c_str(), 50, draw_pile_size); - const double chance = game->backtrack(0); + const Hanabi::probability_t chance = game->backtrack(0); winning_percentages[game_id].push_back(chance); if(chance != 1) { file << "Game " << game_id << ": " << chance << std::endl; @@ -78,7 +78,7 @@ void check_games(unsigned num_players, unsigned max_draw_pile_size, unsigned fir total_chance += chance; } - const double total_average = total_chance / (last_game - first_game + 1); + const Hanabi::probability_t total_average = total_chance / (last_game - first_game + 1); winning_percentages.back().push_back(total_average); file << "Total chance found over " << last_game - first_game + 1 << " many games: " << total_average << std::endl; file.close();