implement tablebase

This commit is contained in:
Maximilian Keßler 2023-08-08 00:29:19 +02:00
parent 1f4949c1e5
commit a77de7efe8
Signed by: max
GPG Key ID: BCC5A619923C0BA5
2 changed files with 86 additions and 25 deletions

View File

@ -111,11 +111,15 @@ namespace Download {
std::unique_ptr<Hanabi::HanabiStateIF> produce_state(
const std::vector<Hanabi::Card>& deck,
const std::vector<Action>& actions,
size_t num_turns_to_replicate
size_t num_turns_to_replicate,
size_t draw_pile_break = 0
) {
auto game = std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<num_suits, num_players, hand_size>(deck));
std::uint8_t index;
for (size_t i = 0; i < num_turns_to_replicate; i++) {
for (size_t i = 0; i < std::min(num_turns_to_replicate, actions.size()); i++) {
if (game->draw_pile_size() == draw_pile_break) {
break;
}
switch(actions[i].type) {
case Hanabi::ActionType::color_clue:
case Hanabi::ActionType::rank_clue:
@ -140,7 +144,7 @@ namespace Download {
return game;
}
std::unique_ptr<Hanabi::HanabiStateIF> get_game(std::variant<int, const char*> game_spec, unsigned turn) {
std::unique_ptr<Hanabi::HanabiStateIF> get_game(std::variant<int, const char*> game_spec, unsigned turn, size_t draw_pile_break = 0) {
const boost::json::object game_json = [&game_spec]() {
if (game_spec.index() == 0) {
return download_game_json(std::get<int>(game_spec));
@ -157,65 +161,65 @@ namespace Download {
case 2:
switch(num_suits) {
case 3:
return produce_state<3,2,5>(deck, actions, turn);
return produce_state<3,2,5>(deck, actions, turn, draw_pile_break);
case 4:
return produce_state<4,2,5>(deck, actions, turn);
return produce_state<4,2,5>(deck, actions, turn, draw_pile_break);
case 5:
return produce_state<5,2,5>(deck, actions, turn);
return produce_state<5,2,5>(deck, actions, turn, draw_pile_break);
case 6:
return produce_state<6,2,5>(deck, actions, turn);
return produce_state<6,2,5>(deck, actions, turn, draw_pile_break);
default:
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
}
case 3:
switch(num_suits) {
case 3:
return produce_state<3,3,5>(deck, actions, turn);
return produce_state<3,3,5>(deck, actions, turn, draw_pile_break);
case 4:
return produce_state<4,3,5>(deck, actions, turn);
return produce_state<4,3,5>(deck, actions, turn, draw_pile_break);
case 5:
return produce_state<5,3,5>(deck, actions, turn);
return produce_state<5,3,5>(deck, actions, turn, draw_pile_break);
case 6:
return produce_state<6,3,5>(deck, actions, turn);
return produce_state<6,3,5>(deck, actions, turn, draw_pile_break);
default:
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
}
case 4:
switch(num_suits) {
case 3:
return produce_state<3,4,4>(deck, actions, turn);
return produce_state<3,4,4>(deck, actions, turn, draw_pile_break);
case 4:
return produce_state<4,4,4>(deck, actions, turn);
return produce_state<4,4,4>(deck, actions, turn, draw_pile_break);
case 5:
return produce_state<5,4,4>(deck, actions, turn);
return produce_state<5,4,4>(deck, actions, turn, draw_pile_break);
case 6:
return produce_state<6,4,4>(deck, actions, turn);
return produce_state<6,4,4>(deck, actions, turn, draw_pile_break);
default:
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
}
case 5:
switch(num_suits) {
case 3:
return produce_state<3,5,4>(deck, actions, turn);
return produce_state<3,5,4>(deck, actions, turn, draw_pile_break);
case 4:
return produce_state<4,5,4>(deck, actions, turn);
return produce_state<4,5,4>(deck, actions, turn, draw_pile_break);
case 5:
return produce_state<5,5,4>(deck, actions, turn);
return produce_state<5,5,4>(deck, actions, turn, draw_pile_break);
case 6:
return produce_state<6,5,4>(deck, actions, turn);
return produce_state<6,5,4>(deck, actions, turn, draw_pile_break);
default:
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
}
case 6:
switch(num_suits) {
case 3:
return produce_state<3,6,3>(deck, actions, turn);
return produce_state<3,6,3>(deck, actions, turn, draw_pile_break);
case 4:
return produce_state<4,6,3>(deck, actions, turn);
return produce_state<4,6,3>(deck, actions, turn, draw_pile_break);
case 5:
return produce_state<5,6,3>(deck, actions, turn);
return produce_state<5,6,3>(deck, actions, turn, draw_pile_break);
case 6:
return produce_state<6,6,3>(deck, actions, turn);
return produce_state<6,6,3>(deck, actions, turn, draw_pile_break);
default:
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
}

View File

@ -16,12 +16,18 @@
namespace Hanabi {
void download(int game_id, int turn) {
auto game = Download::get_game(game_id, turn);
std::cout << "Analysing state: " << *game << std::endl;
std::cout << "Analysing state: " << std::endl << *game << std::endl;
auto res = game->backtrack(1);
std::cout.precision(10);
std::cout << std::endl;
std::cout << "Probability with optimal play: " << res << std::endl;
std::cout << "Enumerated " << game->enumerated_states() << " states" << std::endl;
std::cout << "Visited " << game->visited_states().size() << " unique game states. " << std::endl;
unsigned long biggest_key = 0;
for(const auto& [key, prob] : game->visited_states()) {
biggest_key = std::max(biggest_key, key);
}
std::cout << "Biggest key generated is " << biggest_key << std::endl;
}
void print_sizes() {
@ -51,8 +57,59 @@ void test() {
}
}
void check_games(unsigned num_players, unsigned max_draw_pile_size, unsigned first_game = 4, unsigned last_game = 9999) {
std::vector<std::vector<double>> 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;
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);
winning_percentages[game_id].push_back(chance);
if(chance != 1) {
file << "Game " << game_id << ": " << chance << std::endl;
file << *game << std::endl << std::endl;
}
std::cout << "Finished game " << game_id << ": " << chance << std::endl;
total_chance += chance;
}
const double 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();
}
const std::string results_file_name {"results_" + std::to_string(num_players) + "p.txt"};
std::ofstream results_file (results_file_name);
results_file << "game_id, ";
for(size_t draw_pile_size = 0; draw_pile_size <= max_draw_pile_size; draw_pile_size++) {
results_file << std::to_string(draw_pile_size) << ", ";
}
results_file << "\n";
for(size_t game_id = first_game; game_id <= last_game; game_id++) {
results_file << game_id << ", ";
for(size_t draw_pile_size = 0; draw_pile_size <= max_draw_pile_size; draw_pile_size++) {
results_file << winning_percentages[game_id][draw_pile_size] << ", ";
}
results_file << std::endl;
}
results_file << "total, ";
for(size_t draw_pile_size = 0; draw_pile_size <= max_draw_pile_size; draw_pile_size++) {
results_file << winning_percentages.back()[draw_pile_size] << ", ";
}
results_file << std::endl;
results_file.close();
}
int main(int argc, char *argv[]) {
test();
#ifndef NDEBUG
test();
#endif
check_games(4, 9);
if(argc == 3) {
std::string game (argv[1]);
std::string turn (argv[2]);