logik/solver.cpp

207 lines
4.9 KiB
C++

#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<int> 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;
}
// Try all remaining sequences
vector<int> Solver::brute_force(vector<vector<int>> *possibilities, vector<int> *chosen, int index) {
vector<int> r = vector<int>(N, -1);
if(index == N) {
if(all_are_consistent(*chosen))
r = *chosen;
return r;
}
for(int col : (*possibilities)[index]) {
chosen->push_back(col);
r = brute_force(possibilities, chosen, index+1);
if(r[0] != -1)
return r;
chosen->pop_back();
}
return r;
}
int Solver::get_weight(vector<int> guess) {
if(!all_are_consistent(guess))
return -1;
// Get weight
for(auto hist : history) {
// TODO get worst-case weight
}
return 1;
}
// Now featuring: minimax pick
Weighed_guess Solver::minimax(vector<vector<int>> *possibilities, vector<int> *chosen, int index) {
// If complete guess, get weight and return
if(index == N)
return {get_weight(*chosen), *chosen};
// Get max-weighted child
Weighed_guess r = {-2, {}};
for(int col : (*possibilities)[index]) {
chosen->push_back(col);
Weighed_guess r2 = minimax(possibilities, chosen, index+1);
if(r2.weight > r.weight || r.weight == -2)
r = r2;
chosen->pop_back();
}
return r;
}
// Guessing
vector<int> Solver::guess() {
// TODO make it smart
auto possibilities = known.list_all_possibilities();
auto chosen = vector<int>(0);
return brute_force(&possibilities, &chosen, 0);
}
// 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<bool> already_used_in_cleaning(N, 0);
// 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] = 1;
}
}
// 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] = 1;
break;
}
}
}
return hist;
}
// Here there be learning
vector<bool> 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++)
if(guess[n] > -1 && known.can(n, guess[n]))
possible_count++;
// None of these colors are there
if(hist.response.somewhere == 0 && hist.response.correct == 0) {
known.empty(hist.guess);
something_to_learn = false;
learned_something = true;
}
// None at the right spot
else if(response.correct == 0) {
if(possible_count > 0) {
known.not_here(guess);
learned_something = true;
}
}
// At least only on the right spot
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;
}
}
}
// Nonzero / nonzero
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<int> guess, Response response) {
// All guessed colors are in the sequence
if(response.somewhere + response.correct == N)
known.all_are_here(guess);
if(guess[0] == -1)
return;
// 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;
}
}
}