Python Hangman Game

newbiegreen4ever

newbiegreen4ever

Posted on July 8, 2021

Python Hangman Game

2021.07.08 16:38

Hangman game

Introduction

I wrote a command-line word-guessing game using basic features of Python. It is a project under the Python Bundle - Python I of codingnomads.

I wrote this follow-up article as my learning log.

1) Generate a random word

reference: Geeksforgeeks - pulling a random word or a string from a line in a text file

Since I have some minimal experiences with JavaScript before, I believe that I have the ability to walk the extra mile and to do the optional part.

I wrote a list of words separated by space and store them in a .txt file.

hangman.txt
programming strong codingnomads rewarding frustrating
Enter fullscreen mode Exit fullscreen mode

The list of words in the .txt file is treated like a list. Random integer is used to randomly pick one word out of the list.

import random
with open(r"C:\Users\User\Desktop\codingnomads\Python-101\projects\hangman.txt", "r") as file:
    data = file.read()
    words = data.split()
    words_pos = random.randint(0, len(words) - 1)
    print(f"position: {words_pos}")
    print(f"word at position: {words[words_pos]}")
Enter fullscreen mode Exit fullscreen mode

1.1) details with reading the path

reference: stackoverflow - unicode escape

An error will occur if the path is not treated by unicode escape r"path"

2) Split a word into a list of character

reference: stackoverflow - is there a function in python to split a word into a list

The syntax of Python is really intuitive. The syntax to split a word into a list of character is simply list(word).

2.1) why list? why not empty string?

Because strings in Python are immutable, which means that I cannot change the content of an empty string. List is a simple structure which supports item assignment.

# container = "    "
# container[1] = "a"
# TypeError: 'str' object does not support item assignment
Enter fullscreen mode Exit fullscreen mode

3) Check input

I use a while-loop inside the main while-loop to check user input.

The user can only input:

  • alphabet guess.isalpha()
  • a single alphabet len(guess) == 1
  • alphabet not yet guessed correctly guess in container
while container != word:
    guess = input("Guess an alphabet: ").lower()
    while not guess.isalpha() or len(guess) > 1:
        guess = input("We only allow single character input. Please guess again: ").lower()
  while guess.lower() in container:
        guess = input(f"{guess.upper()} is already guessed. Please guess another alphabet: ").lower()
Enter fullscreen mode Exit fullscreen mode

3.1) remember indentation level

Indentation level is important in Python.

If the guess in container loop is indented below not guess.isalpha() or len(guess) > 1 loop, Python will only check guess which is not an alphabet and with a length longer than 1. The consequences are that Python will only check the bad guesses are in container, and the answer will always be no. This makes the snippet not meaningful.

4) Create a counter for how many attempts a user has taken

Position is important. Although Python does not have a strict namespace rule meaning that variable declared inside loop can be accessed outside loop, where to declare a variable is still important.

Remeber to declare the counter outside the main while-loop while container != word, otherwise the counter will be re-assigned the value of 0 in every loop. This means the counter not meaningful as it only shows 0 then 1 then 0.

5) Return all index with same value

reference: codegrepper - how to return all index with same value in list

Codegrepper suggests a one-line solution with enumerate().

indices = [i for i, x in enumerate(word) if x == guess]

reference: stackoverflow - index of duplicate items in a python list

Stackoverflow suggests a simple answer which uses the counter as the index to locate the element with the desired value.

if elem in string_list:
     counter = 0
     elem_pos = []
     for i in string_list:
         if i == elem:
             elem_pos.append(counter)
         counter = counter + 1
     print(elem_pos)
Enter fullscreen mode Exit fullscreen mode

6) Assign new value to container list

Now I am working with mutable list not immutable string. Therefore, value of an element in a string can be reassigned.

for index in indices:
        if word[index] == guess:
                container[index] = guess
Enter fullscreen mode Exit fullscreen mode

7) Display container

It is handy to know that print() function takes the argument of sep (seperation) and end (ending). I change the default ending from "\n" to "" such that every element of the container will be displaced in the same line.

for char in container:
    if char.isalpha():
        print(f" {char} ", sep=" ", end="")
    else:
        print(" _  ", sep=" ", end="")
print("\n")
Enter fullscreen mode Exit fullscreen mode

8) End of game message

Python can easily turn a string into a list by list(str), and turn a list into a string by "".join(list).

I want a string here because I want to apply the str.format and str.upper() methods to make my output prettier.

if container == word:
    print(f"""
            Congratulation! You made it!
            The word is {"".join(word).upper()!r}!
    """)

else:
    print(f"""
            Your guesses are close.
            The word is {"".join(word).upper()!r}.
    """)
Enter fullscreen mode Exit fullscreen mode

Full code

import random
with open(r"C:\Users\User\Desktop\codingnomads\Python-101\projects\hangman.txt", "r") as file:
    data = file.read()
    words = data.split()
    words_pos = random.randint(0, len(words) - 1)
    # print(f"position: {words_pos}")
    # print(f"word at position: {words[words_pos]}")

word = list(words[words_pos])
container = list(len(word) * " ")
limit = len(word) + 5
counter = 0 
# print(word)
# print(container)

print(f"""
        Welcome! Let's play Hangman, the word-guessing game.
        You will have {limit} chances to guess the word
        Let's begin! 
""")

for char in word:
    print(" _ ", sep=" ", end="")
print("\n\n\n")

while container != word:
    guess = input("Guess an alphabet: ").lower()
    while not guess.isalpha() or len(guess) > 1:
        guess = input("We only allow single character input. Please guess again: ").lower()
    while guess.lower() in container:
        guess = input(f"{guess.upper()} is already guessed. Please guess another alphabet: ").lower()
    counter += 1
    print(f"Number of guessess: {counter}")

        if counter > limit:
        print(f"You have already guessed {counter} times. You run out of time.")
        break

    for char in word:
        if guess == char:
            indices = [i for i, x in enumerate(word) if x == guess]
            # print(indices)
            for index in indices:
                if word[index] == guess:
                    container[index] = guess

    for char in container:
        if char.isalpha():
            print(f" {char} ", sep=" ", end="")
        else:
            print(" _  ", sep=" ", end="")
    print("\n")

if container == word:
    print(f"""
            Congratulation! You made it!
            The word is {"".join(word).upper()!r}!
    """)
    # how to join list into a string
    # https://www.simplilearn.com/tutorials/python-tutorial/list-to-string-in-python
else:
    print(f"""
            Your guesses are close.
            The word is {"".join(word).upper()!r}.
    """)
Enter fullscreen mode Exit fullscreen mode

Thank you for reading my article. Please feel free to leave any comment.

💖 💪 🙅 🚩
newbiegreen4ever
newbiegreen4ever

Posted on July 8, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related