Last active
February 23, 2017 09:24
-
-
Save joelburton/fc580cb7d6d19e989d64a96e1cee1a2a to your computer and use it in GitHub Desktop.
Hangman variant that sneakily changes the chosen-word to foil the player.
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
"""Evil Hangman. | |
Hangman, except avoid choosing a real word and, on a letter guess, reduce | |
the set of possible words to the set that gives the player as little | |
advantage as possible. | |
""" | |
import random, collections | |
word_len = int(input("what length word do you want (2-20) [6]") or 6) | |
with open("/usr/share/dict/words") as words: | |
words = (w.strip() for w in words) | |
words = [w.lower() for w in words if w.isalpha() and len(w) == word_len] | |
nguesses_left = int(input("how many guesses do you want [10] > ") or 10) | |
guessed_letters = set() | |
while True: | |
# cheat: tell us what letters would be best to choose | |
# print("best letters to choose & counts", collections.Counter( | |
# c for w in words for c in set(w) - guessed_letters).most_common(3)) | |
ltr = input("\nguess a letter (%d guesses left) > " % nguesses_left).lower() | |
if len(ltr) != 1: | |
print("hey, doofus, one letter at a time.") | |
continue | |
if ltr in guessed_letters: | |
print("pay attention, yo. you already guessed that.") | |
continue | |
guessed_letters.add(ltr) | |
# group words in dict by 'family' (key is tuple-of-indices-where-letter-appears) | |
# for example, if words=[FOO OFF MOO ORE CAT DOG] & ltr=O, our families will be: | |
# { (1,2): [FOO MOO], (0): [OFF ORE], (1): [DOG], (): [CAT] } | |
families = collections.defaultdict(list) | |
for word in words: | |
indices = [i for i, c in enumerate(word) if ltr==c] | |
families[tuple(indices)].append(word) | |
# print("families:", families) | |
# find #-words of families with longest-list-of-words (in our example above, 2) | |
longest_fam_len = max(len(f) for f in families.values()) | |
# find all families w/ that word length ( (1,2):[FOO MOO] and (0):[OFF ORE] ) | |
longest_fams = [f for f in families.items() if len(f[1]) == longest_fam_len] | |
# choose family that uses chosen letter the fewest # of times ([OFF ORE]) | |
fams_with_fewest_match = sorted(longest_fams, key=lambda f: len(f[0])) | |
fam_with_fewest_match = random.choice(fams_with_fewest_match) | |
_, words = fam_with_fewest_match | |
# now words is list of words that are still legal | |
# print("still legal words:", words) | |
# to make the next parts easier to read, let's just arbitrarily pick the first | |
# word from our still-legal words. we're not committing to it; just using it | |
# so we can check for letter-in-legal-words and to draw-the-success-so-far line. | |
word = words[0] | |
if ltr in word: | |
print("correct") | |
if not set(word) - guessed_letters: # are there no missing letters? | |
print("\n\n *** you rock ***\n") | |
break | |
else: | |
print("wrong") | |
nguesses_left -= 1 | |
if nguesses_left == 0: | |
print("\n\n *** you suck ***\n") | |
break | |
print(" ".join(c if c in guessed_letters else "_" for c in word)) | |
print("guessed", " ".join(sorted(guessed_letters))) | |
print("word was", word) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment