Skip to content

Instantly share code, notes, and snippets.

@Lakhan-Nad
Last active June 25, 2020 11:40
Show Gist options
  • Save Lakhan-Nad/fae662b6d13c3a4bdd1016cb9aa41bbf to your computer and use it in GitHub Desktop.
Save Lakhan-Nad/fae662b6d13c3a4bdd1016cb9aa41bbf to your computer and use it in GitHub Desktop.
Donald E. Knuth's method to solve MasterMind Game
#include <iostream>
class solution {
private:
// game configs
int positions;
int options;
char *secret;
int chances;
// structures needed
char *set;
int set_size;
// private methods
void init();
char evaluate(char *, char *);
void remove(char *, char);
void move(char *, char *);
char *minmax();
void print(char *);
public:
solution() = delete;
solution(const solution &c) = delete;
solution(int, int, int, char *);
~solution();
void solve();
};
/*
* game constructor
* takes input as no of holes, no of diffrent colors
* and also the secret code
* max holes = 8
* max choices = 10
* secret code must be all small latin characters
* from a-j depending on no of choices
*/
solution::solution(int holes, int choices, int moves, char *code) {
positions = holes < 4 ? 4 : (holes > 8 ? 8 : holes);
options = choices < 6 ? 6 : (choices > 10 ? 10 : choices);
chances = moves < positions ? positions : (moves > 100 ? 100 : moves);
secret = new char[positions];
for (int i = 0; i < positions; i++) {
secret[i] = code[i];
}
init();
}
/*
*Free Dynamic Memory
*/
solution::~solution() {
delete set;
delete secret;
}
/*
* Allocate Memory
* Building the set
*/
void solution::init() {
int powers[positions + 1];
powers[0] = 1;
for (int i = 1; i <= positions; i++) {
powers[i] = powers[i - 1] * options;
}
set_size = powers[positions] * positions;
set = new char[set_size];
char *base;
int itercount;
for (int i = 0; i < positions; i++) {
for (int j = 0; j < powers[i]; j++) {
for (int k = 0; k < options; k++) {
base = (set + i) +
(j * powers[positions - i] + k * powers[positions - i - 1]) *
positions;
itercount = powers[positions - i - 1] * positions;
for (int l = 0; l < itercount; l += positions) {
base[l] = 'a' + k;
}
}
}
}
}
/*
* evaluate two codes for score
*/
char solution::evaluate(char *guess, char *code) {
char arr1[options] = {0};
char arr2[options] = {0};
char black = 0;
char white = 0;
for (char i = 0; i < positions; i++) {
if (guess[i] == code[i]) black++;
arr1[guess[i] - 'a']++;
arr2[code[i] - 'a']++;
}
for (int i = 0; i < options; i++) {
white += (arr1[i] < arr2[i] ? arr1[i] : arr2[i]);
}
white -= black;
return (black * positions + white);
}
/*
* move contents of array
*/
void solution::move(char *a, char *b) {
for (char i = 0; i < positions; i++) {
*(a + i) = *(b + i);
}
}
/*
* remove from array impossible guesses
*/
void solution::remove(char *guess, char score) {
for (int i = 0; i < set_size; i += positions) {
if (evaluate(guess, set + i) != score) {
move(set + i, set + set_size - positions);
set_size -= positions;
i -= positions;
}
}
}
/*
* use minmax to find next best guess
*/
char *solution::minmax() {
if (set_size == positions) {
return (set + 0);
}
int pos = (positions * positions) + 1;
int count = set_size / positions;
char score;
char **scores = new char *[count];
for (int i = 0; i < count; i++) {
scores[i] = new char[pos]();
}
for (int i = 0; i < count; i++) {
for (int j = i; j < count; j++) {
score = evaluate(set + i * positions, set + j * positions);
scores[i][score]++;
scores[j][score]++;
}
}
int max = 0;
char *guess = nullptr;
for (int i = 0; i < count; i++) {
char max2 = 0;
for (int j = 0; j < pos; j++) {
max2 = max2 > scores[i][j] ? max2 : scores[i][j];
}
delete scores[i];
if (count - (int)max2 > max) {
guess = set + i * positions;
max = count - (int)max2;
}
}
delete scores;
return guess;
}
/*
* play the game
* takes input as number of channces
*/
void solution::solve() {
char winScore = positions * positions;
char guess[positions];
char score;
char *nextBest;
char win = 0;
for (int i = 0; i < positions / 2; i++) {
guess[i] = 'a';
}
for (int i = positions / 2; i < positions; i++) {
guess[i] = 'b';
}
for (int i = 0; i < chances; i++) {
if (set_size >= positions) {
print(guess);
score = evaluate(guess, secret);
if (score == winScore) {
win = 1;
break;
}
remove(guess, score);
nextBest = minmax();
if (nextBest != nullptr) {
move(guess, nextBest);
}
} else {
std::cout << "Invalid Secret Given!";
std::cout << std::endl;
return;
}
}
// Win or Not
if (win) {
std::cout << "You Win";
} else {
std::cout << "You Lose";
}
std::cout << std::endl;
return;
}
/*
* print guess
*/
void solution::print(char *s) {
std::cout << "Guess: ";
for (int i = 0; i < positions; i++) {
std::cout << s[i];
}
std::cout << std::endl;
}
/*
* Example Game
*/
int main() {
char x[5] = {'1', '2', '3', '4', '5'};
solution t(5, 8, 10, x);
t.solve();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment