From 3d359cef83ea890019a56dd1fe6bd04eccb5f814 Mon Sep 17 00:00:00 2001 From: Matuush Date: Thu, 14 Nov 2024 14:30:25 +0100 Subject: [PATCH] Solver optimizations - only repeat history learning, if something changed and game queries are now constant if repeated --- solver.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++----------- solver.hpp | 7 +++-- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/solver.cpp b/solver.cpp index 40c592a..1d50b2d 100644 --- a/solver.cpp +++ b/solver.cpp @@ -3,6 +3,8 @@ Game::Game(int p_N, int p_M) : N(p_N), M(p_M) { possible = vector>(N, vector(M, 1)); + empty_colors = vector(M, 0); + final = vector(N, -1); unsigned int seed = std::chrono::system_clock::now().time_since_epoch().count(); random_engine = std::default_random_engine(seed); @@ -20,6 +22,8 @@ vector> Game::get_positions_of_colors(vector guess) { return positions_of_colors; } int Game::final_color(int n) { + if(final[n] > -1) + return final[n]; int final_col, count = 0; for(int col = 0; col < M; col++) if(possible[n][col]) { @@ -27,14 +31,20 @@ int Game::final_color(int n) { count++; } - if(count == 1) + if(count == 1) { + final[n] = final_col; return final_col; + } return -1; } bool Game::is_empty(int col) { + if(empty_colors[col]) + return true; + for(int n = 0; n < N; n++) if(possible[n][col]) return false; + empty_colors[col] = true; return true; } vector> Game::list_all_possibilities() { @@ -77,8 +87,9 @@ void Game::empty_color(int col) { } // Specific reactions -void Game::if_not_here_then_nowhere(vector guess) { +bool Game::if_not_here_then_nowhere(vector guess) { auto positions_of_colors = get_positions_of_colors(guess); + bool learned_something = false; // If color isn't here, it can't be in the sequence for(int col = 0; col < M; col++) { @@ -86,9 +97,13 @@ void Game::if_not_here_then_nowhere(vector guess) { for(int n : positions_of_colors[col]) if(possible[n][col]) possible_count++; - if(possible_count == 0 && positions_of_colors[col].size() > 0) + if(possible_count == 0 && positions_of_colors[col].size() > 0 && !is_empty(col)) { empty_color(col); + learned_something = true; + } } + + return learned_something; } void Game::here(vector guess) { for(int n = 0; n < N; n++) @@ -152,9 +167,15 @@ vector Solver::brute_force(vector> *possibilities, vector } return r; } +// Now featuring: minimax pick +vector Solver::minimax(vector> *possibilities, vector *chosen, int index) { + // TODO + return vector(N, -1); +} // Guessing vector Solver::guess() { + // TODO make it smart auto possibilities = known.list_all_possibilities(); auto chosen = vector(0); return brute_force(&possibilities, &chosen, 0); @@ -172,7 +193,7 @@ Historic_guess Solver::clean(Historic_guess hist) { // Clean empty colors for(int n = 0; n < N; n++) if(known.is_empty(hist.guess[n])) - hist.guess[n] = -2; + hist.guess[n] = -1; // The in-place colors we know for(int n = 0; n < N; n++) { @@ -206,14 +227,22 @@ Historic_guess Solver::clean(Historic_guess hist) { // Here there be learning -bool Solver::extract_info(Historic_guess hist) { - bool something_to_learn = true; +vector Solver::extract_info(Historic_guess hist) { + bool something_to_learn = true, learned_something = false; // A bit of cleaning auto cleaned = clean(hist); auto guess = cleaned.guess; auto response = cleaned.response; + bool something = false; + for(int n = 0; n < N; n++) + if(guess[n] > -1) + something = true; + + if(!something) + return {false, false}; + // Get number of colors, that can be on their positions int possible_count = 0; for(int n = 0; n < N; n++) @@ -224,11 +253,16 @@ bool Solver::extract_info(Historic_guess hist) { if(hist.response[0] == 0 && hist.response[1] == 0) { known.empty(hist.guess); something_to_learn = false; + learned_something = true; } // None at the right spot - else if(response[1] == 0) - known.not_here(guess); + else if(response[1] == 0) { + if(possible_count > 0) { + known.not_here(guess); + learned_something = true; + } + } // At least only on the right spot else if(response[0] == 0) { @@ -236,21 +270,25 @@ bool Solver::extract_info(Historic_guess hist) { if(response[1] == possible_count) { known.here(guess); something_to_learn = false; + learned_something = true; } - // TODO tady else if(hist.response[0] == 0){ - known.if_not_here_then_nowhere(hist.guess); + if(known.if_not_here_then_nowhere(hist.guess)) { + learned_something = true; + } } } // Nonzero / nonzero else { // Only colors that can be on these positions are left - if(response[1] == possible_count) + if(response[1] == possible_count) { known.here(guess); + learned_something = true; + } } - return something_to_learn; + return {something_to_learn, learned_something}; } void Solver::learn(vector p_guess, vector p_response) { // All guessed colors are in the sequence @@ -264,12 +302,20 @@ void Solver::learn(vector p_guess, vector p_response) { history.push_back({p_guess, p_response}); // Repeat multiple times, if new information turned out - for(auto _ : history) + bool learned_something = true; + while(learned_something) { + learned_something = false; + // Learn from previous guesses - for(int i = 0; i < history.size(); i++) - if(!extract_info(history[i])) { + for(int i = 0; i < history.size(); i++) { + auto info = extract_info(history[i]); + if(!info[0]) { // If there is nothing left to learn from the guess history.erase(history.begin()+i); i--; } + if(info[1]) + learned_something = true; + } + } } diff --git a/solver.hpp b/solver.hpp index 74a560a..78f6cd2 100644 --- a/solver.hpp +++ b/solver.hpp @@ -8,6 +8,8 @@ struct Game { private: vector> possible; + vector empty_colors; + vector final; std::default_random_engine random_engine; public: int N, M; @@ -27,7 +29,7 @@ public: void empty_color(int col); // Learning functions - void if_not_here_then_nowhere(vector guess); + bool if_not_here_then_nowhere(vector guess); void here(vector guess); void not_here(vector guess); void empty(vector guess); @@ -54,8 +56,9 @@ public: private: Historic_guess clean(Historic_guess hist); - bool extract_info(Historic_guess hist); + vector extract_info(Historic_guess hist); bool all_are_consistent(vector supposed_sequence); vector brute_force(vector> *possibilities, vector *chosen, int index); + vector minimax(vector> *possibilities, vector *chosen, int index); };