Created
July 16, 2014 14:29
-
-
Save agfor/4e8c53eb9736bbf9c448 to your computer and use it in GitHub Desktop.
Mastermind
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
assert sys.version[0] == '3' | |
from random import choice | |
from itertools import product | |
from collections import Counter, defaultdict | |
class Settings: | |
def __init__(self, *, | |
colors = '123456', | |
correct = 'B', | |
near = 'W', | |
wrong = '.', | |
pegs = 4, | |
max_guesses = 10, | |
_all_answers = None): | |
for setting, value in locals().items(): | |
setattr(self, setting, value) | |
self.victory = [correct] * pegs | |
self.all_wrong = [wrong] * pegs | |
def random_pegs(self): | |
return [choice(self.colors) for _ in range(self.pegs)] | |
@property | |
def all_answers(self): | |
if not self._all_answers: | |
self._all_answers = set(product(self.colors, repeat = self.pegs)) | |
return self._all_answers | |
def calculate_score(guess, answer, settings): | |
score = [] | |
wrong_guess_pegs = [] | |
wrong_answer_pegs = [] | |
for guess_peg, answer_peg in zip(guess, answer): | |
if guess_peg == answer_peg: | |
score.append(settings.correct) | |
else: | |
wrong_guess_pegs.append(guess_peg) | |
wrong_answer_pegs.append(answer_peg) | |
for peg in wrong_guess_pegs: | |
if peg in wrong_answer_pegs: | |
wrong_answer_pegs.remove(peg) | |
score.append(settings.near) | |
return score + settings.all_wrong[len(score):] | |
class Mastermind: | |
def __init__(self, settings = Settings()): | |
self.settings = settings | |
def reset(self, answer = None): | |
self.guesses = 0 | |
self.score = self.settings.all_wrong | |
self.answer = answer or self.settings.random_pegs() | |
@property | |
def guesses_left(self): | |
return self.settings.max_guesses - self.guesses | |
@property | |
def won(self): | |
return self.score == self.settings.victory | |
def guess(self, guess): | |
if self.guesses_left and len(guess) == self.settings.pegs: | |
self.guesses += 1; | |
self.score = calculate_score(guess, self.answer, self.settings) | |
def play(self, answer = None): | |
self.reset(answer) | |
guesser = self.guesser() | |
while self.guesses_left and not self.won: | |
self.guess(next(guesser)) | |
guesser.send(True) | |
return self.guesses | |
def guesser(self): | |
print("Guesses Remaining:", self.guesses_left) | |
while not (yield input("Guess: ")): | |
print("Score:", "".join(self.score)) | |
print("Guesses Remaining:", self.guesses_left) | |
if self.won: | |
print("Correct! It took you", self.guesses, "guesses.") | |
else: | |
print("Sorry! The correct answer was", self.answer) | |
yield | |
class AutoMastermind(Mastermind): | |
def answer_matches(self, answer, guess, score): | |
return calculate_score(guess, answer, self.settings) == score | |
def matching_answers(self, guess, score, answers): | |
return {answer for answer in answers if self.answer_matches(answer, guess, score)} | |
def guesser(self): | |
working_set = all_answers = self.settings.all_answers | |
guess = "1122" | |
print("".join(guess)) | |
while not (yield guess): | |
print("".join(self.score)) | |
working_set = self.matching_answers(guess, self.score, working_set) | |
answers, guess = min((len(self.matching_answers(answer, self.score, working_set)), answer) | |
for answer in working_set) | |
print("".join(guess), ":", answers) | |
print(self.guesses if self.won else self.answer) | |
yield | |
class KnuthMastermind(AutoMastermind): | |
def __init__(self, *args, _tables = None, **kwargs): | |
super().__init__(*args, **kwargs) | |
self._tables = _tables or defaultdict(dict) | |
@property | |
def tables(self): | |
if not self._tables: | |
for guess, answer in product(self.settings.all_answers, repeat = 2): | |
self._tables[guess][answer] = "".join(calculate_score(guess, answer, self.settings)) | |
return self._tables | |
def guesser(self): | |
tables = self.tables.copy() | |
working_set = self.settings.all_answers | |
guess = "1122" | |
print("".join(guess), ":", True, ":", len(working_set)) | |
while not(yield guess): | |
print("".join(self.score)) | |
working_set = self.matching_answers(guess, self.score, working_set) | |
guesses = [] | |
for answer, table in tables.items(): | |
tables[answer] = table = {key: value for key, value in table.items() if key in working_set} | |
guesses.append((max(Counter(table.values()).values()), answer not in working_set, answer)) | |
answers, not_in_working_set, guess = min(guesses) | |
print("".join(guess), ":", not not_in_working_set, ":", len(working_set)) | |
print(self.guesses if self.won else self.answer) | |
yield | |
if __name__ == "__main__": | |
game = KnuthMastermind() | |
while True: | |
game.play() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment