use rationals as probabilities

This commit is contained in:
Maximilian Keßler 2023-08-10 11:14:15 +02:00
parent 5f0a850c45
commit 24aa016d36
Signed by: max
GPG key ID: BCC5A619923C0BA5
3 changed files with 22 additions and 20 deletions

View file

@ -12,6 +12,7 @@
#include <boost/container/static_vector.hpp> #include <boost/container/static_vector.hpp>
#include <list> #include <list>
#include <ostream> #include <ostream>
#include <boost/rational.hpp>
namespace Hanabi { namespace Hanabi {
@ -21,6 +22,7 @@ namespace Hanabi {
using clue_t = std::uint8_t; using clue_t = std::uint8_t;
using player_t = std::uint8_t; using player_t = std::uint8_t;
using hand_index_t = std::uint8_t; using hand_index_t = std::uint8_t;
using probability_t = boost::rational<unsigned long>;
using state_t = std::uint32_t; using state_t = std::uint32_t;
@ -152,7 +154,7 @@ struct BacktrackAction {
class HanabiStateIF { class HanabiStateIF {
public: public:
virtual double backtrack(size_t depth) = 0; virtual probability_t backtrack(size_t depth) = 0;
virtual void clue() = 0; virtual void clue() = 0;
virtual BacktrackAction discard(hand_index_t index) = 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 size_t draw_pile_size() const = 0;
[[nodiscard]] virtual std::uint64_t enumerated_states() const = 0; [[nodiscard]] virtual std::uint64_t enumerated_states() const = 0;
[[nodiscard]] virtual std::unordered_map<unsigned long, double> visited_states() const = 0; [[nodiscard]] virtual std::unordered_map<unsigned long, probability_t> visited_states() const = 0;
virtual void normalize_draw_and_positions() = 0; virtual void normalize_draw_and_positions() = 0;
@ -182,7 +184,7 @@ public:
HanabiState() = default; HanabiState() = default;
explicit HanabiState(const std::vector<Card>& deck); explicit HanabiState(const std::vector<Card>& deck);
double backtrack(size_t depth) final; probability_t backtrack(size_t depth) final;
void clue() final; void clue() final;
BacktrackAction play(hand_index_t index) final; BacktrackAction play(hand_index_t index) final;
@ -198,7 +200,7 @@ public:
[[nodiscard]] size_t draw_pile_size() const final; [[nodiscard]] size_t draw_pile_size() const final;
[[nodiscard]] std::uint64_t enumerated_states() const final; [[nodiscard]] std::uint64_t enumerated_states() const final;
[[nodiscard]] std::unordered_map<unsigned long, double> visited_states() const final; [[nodiscard]] std::unordered_map<unsigned long, probability_t> visited_states() const final;
void normalize_draw_and_positions() final; void normalize_draw_and_positions() final;
@ -248,7 +250,7 @@ private:
std::uint64_t _enumerated_states {}; std::uint64_t _enumerated_states {};
std::unordered_map<unsigned long, double> _position_tablebase; std::unordered_map<unsigned long, probability_t> _position_tablebase;
}; };
template <std::size_t num_suits, player_t num_players, std::size_t hand_size> template <std::size_t num_suits, player_t num_players, std::size_t hand_size>

View file

@ -419,7 +419,7 @@ namespace Hanabi {
} }
template<suit_t num_suits, player_t num_players, hand_index_t hand_size> template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
double HanabiState<num_suits, num_players, hand_size>::backtrack(size_t depth) { probability_t HanabiState<num_suits, num_players, hand_size>::backtrack(size_t depth) {
_enumerated_states++; _enumerated_states++;
const unsigned long id_of_state = unique_id(); const unsigned long id_of_state = unique_id();
@ -436,7 +436,7 @@ namespace Hanabi {
// TODO: Have some endgame analysis here? // TODO: Have some endgame analysis here?
// First, check if we have any playable cards // First, check if we have any playable cards
double best_probability = 0; probability_t best_probability = 0;
const std::array<Card, hand_size> hand = _hands[_turn]; const std::array<Card, hand_size> hand = _hands[_turn];
// First, check for playables // First, check for playables
@ -445,11 +445,11 @@ namespace Hanabi {
if (_draw_pile.empty()) { if (_draw_pile.empty()) {
bool on_8_clues = _num_clues == 8; bool on_8_clues = _num_clues == 8;
BacktrackAction action = play_and_potentially_update<true>(index); BacktrackAction action = play_and_potentially_update<true>(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); revert_play(action, on_8_clues);
UPDATE_PROBABILITY(probability_for_this_play); UPDATE_PROBABILITY(probability_for_this_play);
} else { } else {
double sum_of_probabilities = 0; probability_t sum_of_probabilities = 0;
uint8_t sum_of_mults = 0; uint8_t sum_of_mults = 0;
for (size_t i = 0; i < _draw_pile.size(); i++) { for (size_t i = 0; i < _draw_pile.size(); i++) {
bool on_8_clues = _num_clues == 8; 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);
} }
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); UPDATE_PROBABILITY(probability_for_this_play);
} }
} }
@ -470,10 +470,10 @@ namespace Hanabi {
if(_pace > 0 and _num_clues < max_num_clues) { if(_pace > 0 and _num_clues < max_num_clues) {
for(std::uint8_t index = 0; index < hand_size; index++) { for(std::uint8_t index = 0; index < hand_size; index++) {
if (is_trash(hand[index])) { if (is_trash(hand[index])) {
double sum_of_probabilities = 0; probability_t sum_of_probabilities = 0;
if (_draw_pile.empty()) { if (_draw_pile.empty()) {
BacktrackAction action = discard_and_potentially_update<true>(index); BacktrackAction action = discard_and_potentially_update<true>(index);
const double probability_for_this_discard = backtrack(depth + 1); const probability_t probability_for_this_discard = backtrack(depth + 1);
revert_discard(action); revert_discard(action);
UPDATE_PROBABILITY(probability_for_this_discard); UPDATE_PROBABILITY(probability_for_this_discard);
} else { } else {
@ -485,7 +485,7 @@ namespace Hanabi {
revert_discard(action); revert_discard(action);
} }
ASSERT(sum_of_mults == _weighted_draw_pile_size); 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); UPDATE_PROBABILITY(probability_discard);
} }
@ -498,7 +498,7 @@ namespace Hanabi {
// Last option is to stall // Last option is to stall
if(_num_clues > 0) { if(_num_clues > 0) {
clue(); clue();
const double probability_stall = backtrack(depth + 1); const probability_t probability_stall = backtrack(depth + 1);
revert_clue(); revert_clue();
UPDATE_PROBABILITY(probability_stall); UPDATE_PROBABILITY(probability_stall);
} }
@ -559,7 +559,7 @@ namespace Hanabi {
} }
template<suit_t num_suits, player_t num_players, hand_index_t hand_size> template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
std::unordered_map<unsigned long, double> HanabiState<num_suits, num_players, hand_size>::visited_states() const { std::unordered_map<unsigned long, probability_t> HanabiState<num_suits, num_players, hand_size>::visited_states() const {
return _position_tablebase; return _position_tablebase;
} }

View file

@ -53,22 +53,22 @@ void test() {
{ {
auto game = Download::get_game("in/1005195", 43); auto game = Download::get_game("in/1005195", 43);
auto res = game->backtrack(1); auto res = game->backtrack(1);
CHECK("1005195", res == static_cast<double>(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) { void check_games(unsigned num_players, unsigned max_draw_pile_size, unsigned first_game = 0, unsigned last_game = 9999) {
std::vector<std::vector<double>> winning_percentages(last_game + 2); std::vector<std::vector<Hanabi::probability_t>> winning_percentages(last_game + 2);
for(size_t draw_pile_size = 0; draw_pile_size <= max_draw_pile_size; draw_pile_size++) { 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"; 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); std::ofstream file (output_fname);
for(size_t game_id = first_game; game_id <= last_game; game_id++) { 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"; 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); 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); winning_percentages[game_id].push_back(chance);
if(chance != 1) { if(chance != 1) {
file << "Game " << game_id << ": " << chance << std::endl; 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; 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); winning_percentages.back().push_back(total_average);
file << "Total chance found over " << last_game - first_game + 1 << " many games: " << total_average << std::endl; file << "Total chance found over " << last_game - first_game + 1 << " many games: " << total_average << std::endl;
file.close(); file.close();