diff --git a/game.cpp b/game.cpp deleted file mode 100644 index 215c9a2..0000000 --- a/game.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include "global.hpp" - -Game::Game(int _N, int _M) : N(_N), M(_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); -} - -// Getting known information -bool Game::can(int n, int col) { - return possible[n][col]; -} -vector> Game::get_positions_of_colors(vector guess) { - auto positions_of_colors = vector>(M, vector(0)); - for(int n = 0; n < N; n++) - if(guess[n] > -1) - positions_of_colors[guess[n]].push_back(n); - 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]) { - final_col = col; - count++; - } - - 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() { - auto r = vector>(N, vector(0)); - for(int col = 0; col < M; col++) - for(int n = 0; n < N; n++) - if(possible[n][col]) - r[n].push_back(col); - - for(int n = 0; n < N; n++) - std::shuffle(r[n].begin(), r[n].end(), random_engine); - return r; -} -void Game::print() { - cout << " "; - for(int col = 0; col < M; col++) - cout << col; - cout << '\n'; - for(int i = 0; i < N; i++) { - cout << i; - for(auto col : possible[i]) - cout << col; - cout << '\n'; - } - cout << '\n'; -} - -// Learning functions -void Game::cannot_be(int n, int col) { - possible[n][col] = false; -} -void Game::must_be(int n, int must_col) { - for(int col = 0; col < M; col++) - if(col != must_col) - possible[n][col] = 0; -} -void Game::empty_color(int col) { - for(int n = 0; n < N; n++) - possible[n][col] = 0; - empty_colors[col] = true; -} - -// Specific reactions -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++) { - int possible_count = 0; - for(int n : positions_of_colors[col]) - if(possible[n][col]) - possible_count++; - 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++) - if(guess[n] > -1 && possible[n][guess[n]]) - must_be(n, guess[n]); -} -void Game::not_here(vector guess) { - for(int n = 0; n < N; n++) - if(guess[n] > -1) - cannot_be(n, guess[n]); -} -void Game::empty(vector guess) { - for(int col : guess) - if(col > -1) - empty_color(col); -} -void Game::all_are_here(vector guess) { - auto positions_of_colors = get_positions_of_colors(guess); - - for(int col = 0; col < M; col++) { - if(positions_of_colors[col].size() == 0) - empty_color(col); - } -} - -// For remembering guesses with their responses -Historic_guess::Historic_guess(vector _guess, Response _response) { - guess = _guess; - response = _response; -} diff --git a/global.hpp b/global.hpp index 10dcc53..e44e9f4 100644 --- a/global.hpp +++ b/global.hpp @@ -14,6 +14,12 @@ struct Response { int somewhere, correct; Response() {} Response(int _s, int _c) : somewhere(_s), correct(_c) {} + bool operator==(Response second) { + return somewhere == second.somewhere && correct == second.correct; + } + bool operator!=(Response second) { + return !(operator==(second)); + } }; // Game generating @@ -28,42 +34,3 @@ string format_lost_sequence(vector sequence); // Validating Response validate(vector sequence, vector guess); - -// Game remembering -struct Game { -private: - vector> possible; - vector empty_colors; - vector final; - std::default_random_engine random_engine; -public: - int N, M; - Game(int _N, int _M); - - // Get known information - bool can(int n, int col); - int final_color(int n); - bool is_empty(int col); - void print(); - vector> list_all_possibilities(); - vector> get_positions_of_colors(vector guess); - - // Utility functions - void cannot_be(int n, int col); - void must_be(int n, int must_col); - void empty_color(int col); - - // Learning functions - bool if_not_here_then_nowhere(vector guess); - void here(vector guess); - void not_here(vector guess); - void empty(vector guess); - void all_are_here(vector guess); -}; - -// Guess-response remembering -struct Historic_guess { - vector guess; - Response response; - Historic_guess(vector _guess, Response _response); -}; diff --git a/main.cpp b/main.cpp index 0946fd0..1576aec 100644 --- a/main.cpp +++ b/main.cpp @@ -18,7 +18,6 @@ int main(int argc, char* argv[]) { int N = stoi(get_input("-n", args, "Length of sequence", "5")); int M = stoi(get_input("-m", args, "Number of colors", "8")); string player = get_input("-p", args, string("Who plays [")+HUMAN+"/"+BOT+"]", "bot"); - bool learn = "y" == get_input("-l", args, "Do you want to know what bot learns [y/n]", "y"); string gen = player == HUMAN ? RANDOM : get_input("-g", args, "Who generates the seque", RANDOM); bool human_player = player == HUMAN; @@ -62,23 +61,15 @@ int main(int argc, char* argv[]) { response = validate(sequence, guess); cout << format_response(response); - - if(learn) - bot.learn(guess, response); } // Bot playing else { guess = bot.guess(); - response = validate(sequence, guess); bot.learn(guess, response); - if(learn) - cout << "Guess " << history.size() << " : " << format_guess(guess) << format_response(response); } - if(learn) - bot.print(); history.push_back(guess); if(history.back() == sequence) break; } diff --git a/solver.cpp b/solver.cpp index 5ed6f6f..5dfbb35 100644 --- a/solver.cpp +++ b/solver.cpp @@ -1,202 +1,36 @@ #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 +void Solver::generate_set(vector carry) { + if(carry.size() == N) { + possible.push_back(carry); + return; } - 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(); + for(int col = 0; col < M; col++) { + carry.push_back(col); + generate_set(carry); + carry.pop_back(); } - return r; } -// Guessing +Solver::Solver(int _N, int _M) : N(_N), M(_M) { + generate_set({}); +} + +// TODO 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(); -} -void Solver::print_unknown() { - for(auto hist : history) { - auto cleaned = clean(hist); - for(int pos : cleaned.guess) - cout << pos << " "; - cout << "[" << cleaned.response.somewhere << "/" << cleaned.response.correct << "]\n"; - } -} - -// 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}; + return {}; } +// TODO 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; - } - } + +} + +// TODO +int Solver::get_weight(vector guess) { + return 0; +} +// TODO +Weighed_guess Solver::minimax(vector> *possibilities, vector *chosen, int index) { + return {0, {}}; } diff --git a/solver.hpp b/solver.hpp index e9e0ac7..e71e07c 100644 --- a/solver.hpp +++ b/solver.hpp @@ -1,8 +1,6 @@ #pragma once #include "global.hpp" - - // For deciding the best guess struct Weighed_guess { int weight; @@ -13,24 +11,16 @@ struct Weighed_guess { // Solving the game class Solver { - Game known; int N, M; - vector history = {}; + vector> possible = vector>(0); public: Solver(int N, int M); vector guess(); - void print(); - void print_unknown(); void learn(vector guess, Response response); private: - Historic_guess clean(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); - + void generate_set(vector carry); int get_weight(vector guess); Weighed_guess minimax(vector> *possibilities, vector *chosen, int index); };