Skip to content

Instantly share code, notes, and snippets.

@agfor
Created July 16, 2014 14:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agfor/4e8c53eb9736bbf9c448 to your computer and use it in GitHub Desktop.
Save agfor/4e8c53eb9736bbf9c448 to your computer and use it in GitHub Desktop.
Mastermind
#!/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