#include "solver.hpp" Solver::Solver(int _N, int _M) : N(_N), M(_M), known({_N, _M}) {} // Check, if it could have been this bool Solver::all_are_consistent(vector supposed_sequence) { for(auto hist : history) { auto response = validate(supposed_sequence, hist.guess); if(response.somewhere != hist.response.somewhere || response.correct != hist.response.correct) return false; } return true; } int Solver::get_weight(vector guess) { if(!all_are_consistent(guess)) return -1; // Get weight for(auto hist : history) { // TODO get worst-case weight // Possibly get how many sequences it eliminates } return 1; } // Now featuring: minimax pick Weighed_guess Solver::minimax(vector> *possibilities, vector *chosen, int index) { // If complete guess, get weight and return if(index == N) return {get_weight(*chosen), *chosen}; // Get max-weighted children Weighed_guess r = {-2, {}}; for(int col : (*possibilities)[index]) { chosen->push_back(col); auto r2 = minimax(possibilities, chosen, index+1); if((r2.weight > r.weight || r.weight == -2) && r2.weight > -1) r = r2; chosen->pop_back(); } return r; } // Guessing vector Solver::guess() { auto possibilities = known.list_all_possibilities(); auto chosen = vector(0); return minimax(&possibilities, &chosen, 0).guess; } // Prints what the solver deduced void Solver::print() { known.print(); } // Clean guess and response from info we know Historic_guess Solver::clean(Historic_guess hist) { vector already_used_in_cleaning(N, false); // Clean empty colors for(int n = 0; n < N; n++) if(known.is_empty(hist.guess[n])) hist.guess[n] = -1; // The in-place colors we know for(int n = 0; n < N; n++) { if(hist.guess[n] <= -1) continue; if(known.final_color(n) == hist.guess[n]) { hist.guess[n] = -1; hist.response.correct -= 1; already_used_in_cleaning[n] = true; } } // The out-of-place colors we know for(int n = 0; n < N; n++) { if(hist.guess[n] <= -1 || known.can(n, hist.guess[n])) continue; for(int i = 0; i < N; i++) { if(i == n || hist.guess[i] <= -1 || already_used_in_cleaning[i]) continue; if(known.final_color(i) == hist.guess[n]) { hist.guess[n] = -1; hist.response.somewhere -= 1; already_used_in_cleaning[i] = true; break; } } } return hist; } // Here there be learning 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; // Get number of colors, that can be on their positions int possible_count = 0; for(int n = 0; n < N; n++) if(guess[n] > -1 && known.can(n, guess[n])) possible_count++; // The color isn't in the sequence, except for known info [0/0] if(response.somewhere == 0 && response.correct == 0) { // Deduce what was cleaned vector col_was_cleaned(M, false); for(int n = 0; n < N; n++) if(guess[n] != hist.guess[n]) col_was_cleaned[hist.guess[n]] = true; for(int n = 0; n < N; n++) if(guess[n] > -1) for(int n2 = 0; n2 < N; n2++) if(known.final_color(n2) != guess[n]) known.cannot_be(n2, guess[n]); something_to_learn = false; learned_something = true; } // None at the right spot [X/0] else if(response.correct == 0) { if(possible_count > 0) { known.not_here(guess); learned_something = true; } } // At least only on the right spot [0/X] else if(response.somewhere == 0) { // Only colors that can be on these positions are left if(response.correct == possible_count) { known.here(guess); something_to_learn = false; learned_something = true; } else if(hist.response.somewhere == 0) { if(known.if_not_here_then_nowhere(hist.guess)) { learned_something = true; } } } // The rest [X/X] else { // Only colors that can be on these positions are left if(response.correct == possible_count) { known.here(guess); learned_something = true; } } return {something_to_learn, learned_something}; } void Solver::learn(vector guess, Response response) { // All guessed colors are in the sequence if(response.somewhere + response.correct == N) known.all_are_here(guess); // Write to history history.push_back({guess, response}); // Repeat multiple times, if new information turned out bool learned_something = true; while(learned_something) { learned_something = false; // Learn from previous guesses 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; } } }