Post

a robust Hangman game in python

Hi everyone

I just finished working on hangman game project in python and I want to share with you what I’ve done and what I’ve learned and the mindset behind creating this game.

I was busy working on my master thesis during last year (more info!) and now I make some free space to make experience in Python and taking some great courses like CS50x by Harvard University.

beginning point

OK, I decided to hand an experience on Python to learn more and expand my knowledge. as everyone knows in programming, the most important thing is hands and experience or how much experience you have in programming in a specific language.

I searched through the Internet, and I found a YouTube video which published by freecodecamp page, the title is “12 begin beginner python project” I start to create this project by myself without watching the whole video of creating the program.

start

First of all, I had to figure it out. How can I do it and at this point I didn’t watch the whole section of the video and searched through the Internet and gathered information about hangman because I didn’t know.

Hangman is a game where one player thinks of a word and the other player tries to guess it by suggesting letters. The word to be guessed is represented by a series of underscores, with each underscore representing a letter in the word. The guessing player suggests letters one at a time, and if the letter is present in the word, it is revealed in the correct position(s). If the letter is not in the word, a part of a hangman’s gallows is drawn. The guessing player continues to suggest letters until they either guess the word correctly or the hangman’s figure is completed, resulting in a loss.

Afterward, I started to create my roadmap to create this game and I had to find out any possible situation that the application may run into.

I have to note that the programmer (in video) decided to start the game without giving any letter to the player, but as I read on the Internet, the hangman starts with some letters of the word given to the player and limited number of possible guesses, so I created the game in my way, but after watching the video, I realize that I did much more than the programmer in the video did. 🫠😂

codes

I don’t know how to explain it because my program developed during time and it wasn’t happened suddenly or in a day and the idea just kicked in and I tried to use this ideas.

  1. Using the word list and import the word list to python without implementing the whole award list to a .py file (the word list was introduced during the video). and because I knew that I can import another .py to my main .py file using import syntax I decided to use this knowledge. (like importing modules)
  2. Afterward, I create a function to create a list of the word letters.
  3. Of course, I need a function to create a string of my lists, and these lists are different types of the word. One of them.
  4. And the last, and the most important function in my code is a function that decide to how many letters have to be removed, which letters have to be removed and prevent removing letters that are duplicated like in “door”

the word.py file content:

1
2
3
4
5
6
7
8
9
10
11
import json

# Open the JSON file
with open('04-words.json', 'r') as file:
    # Load the JSON data as a list
    words = json.load(file)['data']

## remove words that contains special characters
for _ in words:
    if "-" in _ or " " in _ or len(_) < 4:
        words.pop(words.index(_))

so, I can import it using

1
2
from random import choice, randint, sample
from words import words

Functions

two handy function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def choose_word(words=words):
## select a word randomly from wordlist which has been imported in `word.py` file
    word = choice(words).upper()
    word_letters = tuple(set(word))
    word_spell = []
    for _ in word:
        word_spell.append(_)
    return word_letters, word_spell, word

def make_string(spells):
## create a string of the word
    word = spells[0]
    for l in spells[1:]:
        word = word + "  " + l
    return word

and the most important function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def remove_letters_and_get_ready():
## Decide how many letters and which letters have to be removed and 
    letters, spelled, pure_word = choose_word()     # get word from proper function
    ## 
    spell_removed = [l for l in spelled]
    
    
    ### Create DICTIONARY of letters and their indices in the word-string
    ### (not that duplicated letters will be more visible)
    #### This brainstorm happend in the last steps of solving the program bug
    #### but in my opinion it is really helpful in some scenarios:
    letter_dict = {}
    for index, letter in enumerate(pure_word.upper()):
        if letter in letter_dict:
            letter_dict[letter].append(index)
        else:
            letter_dict[letter] = [index]
    ## dict returns something like this
    ## {'p': [0], 'r': [1, 4], 'o': [2], 'g': [3, 10], 'a': [5], 'm': [6, 7], 'i': [8], 'n': [9]}
    ## for word "programming"
        
    ## a list of unique letters indices: letters indices that letters are unique in the word
    clear_index = [item[0] for item in letter_dict.values() if len(item) < 2]
    
    ## select some random letter to remove from word and replace them with "__"
    num_of_removes = (len(spelled) - int(round((len(spelled) * (9/20)), 0)))

    ## this section is designed due to above section (removing duplicate-letters indices) to prevent error
    ## If the `num_of_removes` is more than quantity of `clear_index`, then it will raise an error!
    try:
        indx_to_remove = sample(clear_index, num_of_removes)
    except ValueError:
        num_of_removes -= 1      ## lower `num_of_removes` by one
    finally:
        try:
            indx_to_remove = sample(clear_index, num_of_removes)
        except ValueError:
            num_of_removes -= 1    ## do it one more time just to be sure :)
        finally:
            indx_to_remove = sample(clear_index, num_of_removes)
    
    remained_indices = [item for item in range(0, len(spelled)) if item not in set(indx_to_remove)]    
    
    for _ in indx_to_remove:
        spell_removed[_] = "__"        
    word = make_string(spell_removed)
    
    return word, letters, spelled, spell_removed, pure_word

Main section (function) of program

The main function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def hangman():
    word, word_letters, word_spell, spell_removed, pure_word = remove_letters_and_get_ready()
    ## The line below, add shown letter to a list named "Past guesses" to prevent entering letters that are entered previously or is shown.
    past_guess = [ l for l in spell_removed if l != "  " or l != "__" ]
    counter = 0
    count_limit = int(len(word_letters) * round((3/5)*2))
    ## repeat until you hit the guessing limitation or find the whole word :)
    while counter < count_limit and "__" in spell_removed:
        print()
        print("✦ This is the word that you have to guess: ", word)
        current_guess = input("-----> Please give me a letter: ").upper()
        print()
        if current_guess in past_guess or len(current_guess) > 1 or current_guess == "":
            print("Are you okay?? you checked this letter before or is shown or you entered nothing :\/! thank god, I wont count it toward your worng answers")
            past_guess.append(current_guess)
        elif current_guess not in word_letters:
            past_guess.append(current_guess)
            counter += 1
            print("Oh, Wrong guess! please try again! WRONG GUESSES: {}/{}".format(counter, count_limit))
        elif current_guess in word_letters:
            past_guess.append(current_guess)
            print("\n★ Congrates - continue ★\n")
            indx_of_letter = [index for index, value in enumerate(word_spell) if value == current_guess]
            for _ in indx_of_letter:
                spell_removed[_] = current_guess
            word = make_string(spell_removed)

    ## you win or you lost:
    ## when interpreter comes to this line, you guessed the whole word (there is no "__" in geussing variable) or
    ## or you are out of chance to guess more
    if "__" in spell_removed:
        print("\n\n\t ## You Lost :(  the word was: >> {} << ## \n\n".format(pure_word.lower()))
    else:
        print(f"\n\t✮ Congratulations, you are done ✮ >> {pure_word.lower()} \n")

enjoy it


now its time to say:

when I was writing this piece of code, only God and me knew how it worked. Now, Only GOD KNOWS 🤣

This post is licensed under CC BY 4.0 by the author.