A simple Hangman guesser with Python
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
# Setup word list. Download if not there | |
# Note: "string" means the thing we're trying to guess | |
from urllib.request import urlopen | |
from os.path import isfile | |
from re import sub | |
from string import ascii_lowercase | |
print("Loading...") | |
if isfile("words.txt"): | |
words = open("words.txt", "r").read().split("\n") | |
else: | |
print("Downloading, stripping, and caching words...") | |
words = urlopen("https://raw.githubusercontent.com/dwyl/english-words/master/words_alpha.txt").read().decode().split("\n") | |
words = list(set([word.strip().lower() for word in words])) | |
cache = open("words.txt", "w") | |
cache.write("\n".join(words)) | |
cache.close() | |
# Init global vars | |
lenofword = int(input("How long is the word? ")) | |
constraints = {} | |
asked = [] | |
# Import operator for sorting | |
import operator | |
# Explanation: | |
# We have a dict called constraints | |
# It can say that a letter is not in the string | |
# Like {'a': False} | |
# Or that a letter is in the string a certain number of times | |
# Like {'e': 2} | |
# This makes a dict with only the ones where the letter is in the string | |
# And sums up the number of times | |
# When it reaches the length of word, it stops. | |
while sum({k: v for k, v in constraints.items() if not isinstance(v, bool)}.values()) < lenofword: | |
# Workwords is a list of words that might match the string | |
workwords = [] | |
# Loop through constraints | |
for cons, constr in constraints.items(): | |
# If the constraint isn't a bool: | |
# (That means that it isn't False) | |
if not isinstance(constr, bool): | |
# Note we only use words with it that number of times | |
print("Using words only with", cons, constr, "times") | |
else: | |
# Note we only use words without it | |
print("Using words only without "+cons) | |
# Iterate through possible words | |
print("Loading...") | |
for i, word in enumerate(words): | |
# If the word is the right length | |
if len(word) == lenofword: | |
works = True | |
for cons, constr in constraints.items(): | |
if not isinstance(constr, bool): | |
# Make sure it has the letter the right number of times | |
if word.count(cons) != constr: | |
works = False | |
else: | |
# Make sure it doesn't have the letter | |
if cons in word: | |
works = False | |
# If the conditions are right, add it to the list of working words | |
if works: | |
workwords.append(word) | |
# Remove duplicates | |
workwords = list(set(workwords)) | |
# Verbosity | |
print("Found", len(workwords), "working words") | |
if len(workwords) == 0: | |
# Let the user fix it | |
print("Hmm... Looks like we have a problem here. Try to correct it:") | |
print("Original:") | |
print(constraints) | |
print("Enter the fixed one here") | |
constraints = eval(input("As a dict: "), {}, {}) | |
else: | |
workletters = [] | |
# Loop through each letter in each word | |
for i in range(lenofword): | |
for word in workwords: | |
works = True | |
# Make sure not guessed before | |
for cons, constr in constraints.items(): | |
if word[i].lower() == cons: | |
works = False | |
# If it works, add it to the list | |
if works: | |
workletters.append(word[i].lower()) | |
# Make a list of working letters by frequency | |
bestletters = {} | |
for letter in workletters: | |
if letter in bestletters: | |
bestletters[letter] = bestletters[letter] + 1 | |
else: | |
bestletters[letter] = 1 | |
# Sort the working letters by frequency | |
sortedletters = list(dict(sorted(bestletters.items(), key=operator.itemgetter(1), reverse=True))) | |
for srt in sortedletters: | |
if srt in asked: | |
del sortedletters[sortedletters.index(srt)] | |
# Let the user know | |
print("You should guess the letter", sortedletters[0], | |
"(or maybe " + sortedletters[1] + ")" if len(sortedletters) > 1 else "") | |
asked += sortedletters[0] | |
# Ask whether it was right | |
right = input("Was it right? (\"y\" or \"n\") ") | |
if "n" in right: | |
# If it was wrong, add it to constraints | |
constraints[sortedletters[0]] = False | |
elif "y" in right: | |
# If it was right, add it to constraints | |
constraints[sortedletters[0]] = int(input("How many times was it in the string? ")) | |
print("Well done.") | |
for i, word in enumerate(words): | |
# If the word is the right length | |
if len(word) == lenofword: | |
works = True | |
for cons, constr in constraints.items(): | |
if not isinstance(constr, bool): | |
# Make sure it has the letter the right number of times | |
if word.count(cons) != constr: | |
works = False | |
else: | |
# Make sure it doesn't have the letter | |
if cons in word: | |
works = False | |
# If the conditions are right, add it to the list of working words | |
if works: | |
workwords.append(word) | |
# Remove duplicates | |
workwords = list(set(workwords)) | |
# Make sure it was right | |
if len(workwords) == 1: | |
print("It was", workwords[0]+", right?") | |
else: | |
for word in workwords: | |
print("It might have been", word) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment