implement tablebase
This commit is contained in:
parent
1f4949c1e5
commit
a77de7efe8
2 changed files with 86 additions and 25 deletions
50
download.h
50
download.h
|
@ -111,11 +111,15 @@ namespace Download {
|
||||||
std::unique_ptr<Hanabi::HanabiStateIF> produce_state(
|
std::unique_ptr<Hanabi::HanabiStateIF> produce_state(
|
||||||
const std::vector<Hanabi::Card>& deck,
|
const std::vector<Hanabi::Card>& deck,
|
||||||
const std::vector<Action>& actions,
|
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));
|
auto game = std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<num_suits, num_players, hand_size>(deck));
|
||||||
std::uint8_t index;
|
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) {
|
switch(actions[i].type) {
|
||||||
case Hanabi::ActionType::color_clue:
|
case Hanabi::ActionType::color_clue:
|
||||||
case Hanabi::ActionType::rank_clue:
|
case Hanabi::ActionType::rank_clue:
|
||||||
|
@ -140,7 +144,7 @@ namespace Download {
|
||||||
return game;
|
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]() {
|
const boost::json::object game_json = [&game_spec]() {
|
||||||
if (game_spec.index() == 0) {
|
if (game_spec.index() == 0) {
|
||||||
return download_game_json(std::get<int>(game_spec));
|
return download_game_json(std::get<int>(game_spec));
|
||||||
|
@ -157,65 +161,65 @@ namespace Download {
|
||||||
case 2:
|
case 2:
|
||||||
switch(num_suits) {
|
switch(num_suits) {
|
||||||
case 3:
|
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:
|
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:
|
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:
|
case 6:
|
||||||
return produce_state<6,2,5>(deck, actions, turn);
|
return produce_state<6,2,5>(deck, actions, turn, draw_pile_break);
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
switch(num_suits) {
|
switch(num_suits) {
|
||||||
case 3:
|
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:
|
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:
|
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:
|
case 6:
|
||||||
return produce_state<6,3,5>(deck, actions, turn);
|
return produce_state<6,3,5>(deck, actions, turn, draw_pile_break);
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
switch(num_suits) {
|
switch(num_suits) {
|
||||||
case 3:
|
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:
|
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:
|
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:
|
case 6:
|
||||||
return produce_state<6,4,4>(deck, actions, turn);
|
return produce_state<6,4,4>(deck, actions, turn, draw_pile_break);
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
case 5:
|
case 5:
|
||||||
switch(num_suits) {
|
switch(num_suits) {
|
||||||
case 3:
|
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:
|
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:
|
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:
|
case 6:
|
||||||
return produce_state<6,5,4>(deck, actions, turn);
|
return produce_state<6,5,4>(deck, actions, turn, draw_pile_break);
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
case 6:
|
case 6:
|
||||||
switch(num_suits) {
|
switch(num_suits) {
|
||||||
case 3:
|
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:
|
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:
|
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:
|
case 6:
|
||||||
return produce_state<6,6,3>(deck, actions, turn);
|
return produce_state<6,6,3>(deck, actions, turn, draw_pile_break);
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
|
|
61
main.cpp
61
main.cpp
|
@ -16,12 +16,18 @@
|
||||||
namespace Hanabi {
|
namespace Hanabi {
|
||||||
void download(int game_id, int turn) {
|
void download(int game_id, int turn) {
|
||||||
auto game = Download::get_game(game_id, 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);
|
auto res = game->backtrack(1);
|
||||||
std::cout.precision(10);
|
std::cout.precision(10);
|
||||||
|
std::cout << std::endl;
|
||||||
std::cout << "Probability with optimal play: " << res << std::endl;
|
std::cout << "Probability with optimal play: " << res << std::endl;
|
||||||
std::cout << "Enumerated " << game->enumerated_states() << " states" << std::endl;
|
std::cout << "Enumerated " << game->enumerated_states() << " states" << std::endl;
|
||||||
std::cout << "Visited " << game->visited_states().size() << " unique game 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() {
|
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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
test();
|
#ifndef NDEBUG
|
||||||
|
test();
|
||||||
|
#endif
|
||||||
|
check_games(4, 9);
|
||||||
if(argc == 3) {
|
if(argc == 3) {
|
||||||
std::string game (argv[1]);
|
std::string game (argv[1]);
|
||||||
std::string turn (argv[2]);
|
std::string turn (argv[2]);
|
||||||
|
|
Loading…
Reference in a new issue