Search code examples
pythonartificial-intelligence

Improve the odds of winning at hangman with ai


I'm new (and to stack overflow, this is the first question I have ever asked) to python, I have been self-teaching myself for a couple of weeks. I was doing some beginner projects when I decided to make a hangman ai.

#importing
import random
import time
import sys
from collections import Counter

#---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

#defining some variables
list_of_words = open("dictionary.txt", "r")

list_of_words = list_of_words.read().split()

SYMBOL = "abcdefghijklmnopqrstuvwxyz"

#---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

#main game loop
def main():

    while True:

        print("\nGenerating word...")
        word = list_of_words[random.randint(0, len(list_of_words) - 1)].lower()
        word_guess = []
        wrong_attempts = 0
        wrong_letters = []
        game_state = True

        for symbol in word:
            if symbol in SYMBOL:
                word_guess.append("_")

            else:
                word_guess.append(symbol)

        word_show = " ".join(word_guess)
        word = list(word)

        while game_state != False:
            print("\n" + word_show)
            print("\nWrong attempts [{0}/5]" .format(wrong_attempts))

            if len(wrong_letters) > 0:
                print("\nLetters guessed [{0}]" .format(", ".join(wrong_letters)))

            letter = "-"

            while letter not in SYMBOL or letter == "" or len(letter) > 1:

                try:
                   letter = input("\nGuess a letter or enter 0 to call the ai: ")

                except:
                    print("\nUnexpected error ocurred, try again")

                if letter == "0":
                    correct_letters = [letter for letter in word_guess if letter in SYMBOL]
                    letter = ai_solver(wrong_letters, word_guess)

                elif letter in wrong_letters or letter in word_guess:
                    print("\nYou already guessed letter [{0}]" .format(letter))
                    letter = ""

            if letter in word:
                for i in range(len(word)):
                    if letter == word[i]:
                        word_guess[i] = letter

            else:
                wrong_letters.append(letter)
                wrong_attempts += 1

            word_show = " ".join(word_guess)

            if "".join(word_guess) == "".join(word):
                print("\nYou won!")
                game_state = False

            elif wrong_attempts == 5:
                print("\nYou lost!")
                print("The word was [{0}]" .format("".join(word)))
                game_state = False

        option = input("\nWant to play again?[Y/N]: ")

        if option.lower().startswith("n"):
            sys.exit(0)


def ai_solver(letters_attempted, word_guess):

    letters_attempted = letters_attempted if len(letters_attempted) != 0 else ""

    available_words = []

    for word in list_of_words:

        append = False

        if len(word) == len(word_guess):

            append = True
            for i in range(len(word_guess)):
                if word[i] in letters_attempted:
                    append = False
                    break
                if word_guess[i] != "_":
                    if word[i] != word_guess[i]:
                        append = False
                        break

        if append == True:
            print("[{0}]" .format(word))
            available_words.append(word)

    common_letters = [letter for letter in "".join(available_words) if letter not in word_guess]

    common_letters = Counter("".join(common_letters)).most_common(1)

    return common_letters[0][0]


main()

What I tried to do is, to filter all the possible words that have the same length as word_guess. Then filter out any words that contained a letter that was guessed incorrectly by checking letters_attempted.

Then it would filter out all words that had letters that did not match with word_guess.

if word_guess[i] != "_":
    if word[i] != word_guess[i]:
        append = False
        break

Although it works fine, sometimes it would lose, what can I add to increase the chances of winning? Thank you!


Solution

  • Your two filter steps are a good first start. There are several different steps you could take to try to improve things. Let's call the words that fit the criteria so far the candidate words.

    The first step would be to analyze all the candidate words and figure out which letter appears most frequently in the candidate words. (Not counting repeated letters multiple times.) That letter would make a good next guess.

    A slightly more sophisticated approach would look at information gain from a guess. That is, it might be that half the candidate words have a 's', but all such words end in 's'. There might be slight fewer candidate words with a 't', but the 't' can appear anywhere in the word. So, when you guess 't' you actually get a lot more information about what the word could be, because you are shown the location of the 't' when you guess it correctly. Particularly when you don't have enough guesses to figure out every word, such a strategy may help you figure out more words in the guesses that you have.