use rationals as probabilities
This commit is contained in:
parent
5f0a850c45
commit
24aa016d36
3 changed files with 22 additions and 20 deletions
12
game_state.h
12
game_state.h
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
main.cpp
10
main.cpp
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue