12
Guess-the-Word Game

In this chapter, you’ll build a voice-controlled graphical guess-the-word game. This is an interesting challenge because when playing guess-the-word, players often talk quickly, so we’ll need to fine-tune our script’s listening abilities.

As usual, we’ll go over the game rules and draw a game board; this game board uses six coins to represent your six guesses. You’ll learn how to load a picture to a Python script and create multiple images of it onscreen. You’ll also learn to make the images disappear one by one.

We’ll start the game by using written inputs. Then, when we have it working well, we’ll add the speech recognition and text-to-speech features.

All scripts in this chapter are available on the book’s resources page at https://www.nostarch.com/make-python-talk/. Start by creating the folder /mpt/ch12/ for this chapter.

Game Rules

Our guess-the-word game is loosely based on the hangman game. Our game will present only four-letter words to keep it simple, but you should try adapting it later when you’re comfortable with how it all works. Let’s go over the rules of the game first.

Similar to hangman, our guess-the-word game involves two players. The first player thinks of a word and draws a number of dashes equal to the number of letters in the word. The first player also draws six coins in the middle of the screen to represent the six incorrect guesses the second player will be allowed.

The second player tries to figure out the word by guessing one letter at a time. If the suggested letter is in the word, the first player fills in the blanks with the letter in the right places. If a suggested letter is not in the word, the first player erases a coin in the middle of the screen. If the second player completes the word before making six incorrect guesses, they win the game. If that player fails to identify the word before using up their six wrong guesses, they lose.

Draw the Game Board

Our game board will preload with four dashes to represent the word. We’ll also include the message incorrect guesses onscreen. Open your Spyder editor and enter the code in Listing 12-1, saving it as guess_word_board.py.

import turtle as t

# Set up the board
t.setup(600,500)
t.hideturtle()
t.tracer(False)
t.bgcolor("lavender")
t.title("Guess the Word Game in Turtle Graphics")
# Define a variable to count how many guesses left
1 score = 6
# Create a second turtle to show guesses left
left = t.Turtle()
left.up()
left.hideturtle()
left.goto(-290,200)
left.write(f"guesses left:   {score}",font=('Arial',20,'normal'))
# Put incorrect guesses on top
t.up()
t.goto(-290,150)
t.write("incorrect guesses:",font=('Arial',20,'normal'))
# Put four empty spaces for the four letters at bottom
2 for x in range(4):
    t.goto(-275+150*x,-200)
    t.down()
    t.goto(-175+150*x,-200)
    t.up()
t.done()
try:
    t.bye()
except t.Terminator:
    print('exit turtle')

Listing 12-1: Python script to draw the guess-the-word game board

We import the turtle module and set up the screen to be 600 by 500 pixels with a lavender background. The title will read Guess the Word Game in Turtle Graphics. Note that we omitted the last two arguments in setup(), so the game board will appear at the center of your computer screen by default.

At 1, we create a variable score to keep track of the number of guesses the player has left. It starts with a value of 6. Later in the game, every time the player guesses an incorrect letter, the value will decrease by 1. We also create a new turtle named left, representing the number of guesses remaining. We use the new turtle to write the number of chances the player has left, erasing whatever was there before. By using a new turtle, we limit the number of objects we need to redraw onscreen.

We then add the text incorrect guesses, which will later show the incorrect letters the player guessed. We draw four dashes at the bottom of the board 3 to hold the four letters in the word. Run the script and you should see a board similar to Figure 12-1.

f12001

Figure 12-1: The board for the guess-the-word game

The Text Version

In this section, you’ll place the six coins on the screen and enable the player to enter letters with the keyboard. You’ll then determine whether a player has won or lost the game. This completes the silent version of guess-the-word.

Load the Coins

You’ll place six coins at the center of the screen. In the process, you’ll learn how to load a picture to the script, resize it to any shape you like, and place as many objects on the turtle screen as you like. As noted, each coin corresponds to one incorrect guess.

Download the picture file cash.png from the book’s resources and place it in your chapter folder. Open guess_word_board.py, add the highlighted code in Listing 12-2, and save the new script as show_coins.py in the same chapter folder containing cash.png.

--snip--
from tkinter import PhotoImage
from time import sleep
--snip--
# Put four empty spaces for the four letters at bottom
for x in range(4):
    t.goto(-275+150*x,-200)
    t.down()
    t.goto(-175+150*x,-200)   
    t.up()
# Load a picture of the coin to the script
1 coin = PhotoImage(file="cash.png").subsample(10,10)
t.addshape("coin", t.Shape("image", coin))
# Create six coins on screen 
2 coins = [0]*6
for i in range(6):
    coins[i] = t.Turtle('coin')
    coins[i].up()
    coins[i].goto(-100+50*i,0)
t.update()
3 sleep(3)
# Make the coins disappear one at a time
for i in range(6):
    coins[-(i+1)].hideturtle()
    t.update()
    sleep(1)
t.done()
--snip--

Listing 12-2: Script to show and remove coins

We import the PhotoImage() class from the tkinter module and the sleep() function from the time module. We then load cash.png by using PhotoImage() 1. We use subsample() to scale the image to the size we want. In this case, we use scale factors of (10,10), which means that both the width and the height of the picture are one-tenth that of the original picture.

At 2, we create a list coins with six elements by using [0]*6. If you print out the list, it will look like this:

[0, 0, 0, 0, 0, 0]

We’ll change the elements later; the 0 values are just placeholders.

Next, we create a new turtle in each element in coins. We then make the coin turtles go to the center of the screen and line up horizontally. To demonstrate how to load and then hide the coins, we have them stay onscreen for three seconds 3 before using hideturtle() from the turtle module to make them disappear from the screen one at a time, starting with the last one.

Figure 12-2 shows the screen in the first three seconds, as the coins are lined up.

f12002

Figure 12-2: Showing coins on the guess-the-word game board

Guess the Letters

The next version of the game will use 15 four-letter words, picked from a list of the most commonly used four-letter words according to Professor Barry Keating’s website at the University of Notre Dame (https://bit.ly/3g7z7cg). Keating has done extensive work in the fields of business forecasting and data mining. He is also the coauthor of the popular textbook Forecasting and Predictive Analytics (McGraw Hill, 2018).

After we make the following modifications, the script will randomly choose one word, ask you to guess a letter, and then accept input from the IPython console. If a guess is right, the letter will show up on one of the dashes corresponding to the position of the letter in the word. In the rare case that the letter appears in the word twice, the letter will show up on two of the dashes. If the letter is not in the word, it will show up at the top of the screen in the list of incorrect guesses. We’ll skip placing the coins in this script to make testing of the code easier to follow.

Open guess_word_board.py, add the highlighted code in Listing 12-3, and save the new script as guess_letter.py.

import turtle as t
from random import choice
--snip--
# Put four empty spaces for the four letters at bottom
for x in range(4):
    t.goto(-275+150*x,-200)
    t.down()
    t.goto(-175+150*x,-200)
    t.up()
t.update()
# Put words in a dictionary and randomly pick one
1 words = ['that', 'with', 'have', 'this', 'will', 'your', 
   'from', 'they', 'know', 'want', 'been', 
   'good', 'much', 'some', 'time']
word = choice(words)
# Create a missed list
2 missed = []
# Start the game loop
3 while True:
    # Take written input 
    inp = input("What's your guess?
").lower()
    # Stop the loop if you key in "done"
    if inp == "done":
        break
    # Check if the letter is in the word
  4 elif inp in list(word):
        # If yes, put it in the right position(s)
        for w in range(4):
            if inp == list(word)[w]:
                t.goto(-250+150*w,-190)
                t.write(inp,font=('Arial',60,'normal'))
    # If the letter is not in the word, show it at the top
  5 else:
        missed.append(inp)
        t.goto(-290+80*len(missed),60)
        t.write(inp,font=('Arial',60,'normal'))
    # Update everything that happens in the iteration
    t.update()
try:
    t.bye()
except t.Terminator:
    print('exit turtle')

Listing 12-3: Script to put letters on the game board

We first import choice() from the random module so the script can randomly pick a word from the list. We put the 15 words in the list words 1 and allocate the randomly selected word to word. At 2, we create the list missed to hold all incorrectly guessed letters. We then put the script in an infinite loop 3 to continuously take your text input. If you want to stop the loop, you can enter done in the Spyder IPython console.

At 4, we check whether the letter you guess is in one of the letters in word. We use list(), which takes a string variable as input and breaks it into a list of individual letters; for example, the command list("have") produces the list ["h","a","v","e"].

If your guessed letter is in word, the function checks every letter in word to see if your guess matches the letter in that position. If so, the function writes the letter on the corresponding position onscreen.

If your guess is not in word 5, the letter is added to missed and is written at the top of the screen in the incorrect guesses section.

Note that we also removed the line t.done() in this script. This means that, once you finish guessing and enter done, the script will end and everything will disappear from your screen.

Here’s the output from one exchange with the script, when the script randomly selected the word have from the list of the 15 words, with my typed input in bold:

What's your guess?
a
What's your guess?
b
What's your guess?
v
What's your guess?
v
What's your guess?
b
What's your guess?
h
What's your guess?
e
What's your guess?
f
What's your guess?
g
What's your guess?
h
What's your guess?
u
What's your guess?
done

Figure 12-3 shows the resultant screen.

f12003

Figure 12-3: A guess-the-word game board with letters on it

It’s working, but you may have noticed that some things need improvement. To have a complete version of guess-the-word, we need the script to do the following:

  1. Prevent the players from guessing the same letter more than once. In my preceding interaction, I guessed b, v, and h twice, wasting my guesses.
  2. Notify the players when a word is complete.
  3. Stop taking input after a player completes the word.
  4. Put the six coins onscreen and remove one every time a player misses a letter.

Determine Valid Guesses, Wins, and Losses

Next, we’ll disallow duplicate-letter guesses, declare a win if you complete the word while missing fewer than six letters, and declare a loss if not.

Open guess_letter.py and add the highlighted parts in Listing 12-4. Then save the new script as guess_word.py. A block of code in guess_letter.py is modified and replaced by the newly added blocks. If you’re uncertain what’s different, download the script guess_word.py from the book’s resources page.

import turtle as t
from random import choice
from tkinter import messagebox
from tkinter import PhotoImage

--snip--
# Create a missed list
missed = []
# Load a picture of the coin to the script
1 coin = PhotoImage(file = "cash.png").subsample(10,10)
t.addshape("coin", t.Shape("image", coin))
# Create six coins on screen 
coins = [0]*6
for i in range(6):
    coins[i] = t.Turtle('coin')
    coins[i].up()
    coins[i].goto(-100+50*i,0)
2 t.update()
# Prepare the validinputs and gotright lists
3 validinputs = list('abcdefghijklmnopqrstuvwxyz')
gotright = []
# Start the game loop
while True:
    # Take written input 
    inp = input("What's your guess?
").lower()
    # Stop the loop if you key in "done"
    if inp == "done":
        break
    # If the letter is not a valid input, remind
    elif inp not in validinputs:
        messagebox.showerror("Error","Sorry, that's an invalid input!")
    # Otherwise, go ahead with the game
  4 else:
        # Check if the letter is in the word
        if inp in list(word):
            # If yes, put it in the right position(s)
            for w in range(4):
                if inp == list(word)[w]:
                    t.goto(-250+150*w,-190)
                    t.write(inp,font = ('Arial',60,'normal'))
                    gotright.append(inp)
            # If got four positions right, the player wins
            if len(gotright) == 4:
                messagebox.showinfo
                ("End Game","Great job, you got the word right!")
                break
        # If the letter is not in the word, show it at the top
      5 else:
            # Reduce guesses left by 1
            score -=  1
            # Remove a coin
            coins[-(6-score)].hideturtle()
            # Update the number of guesses left on board
            left.clear()
            left.write
            (f"guesses left:   {score}",font = ('Arial',20,'normal'))
            t.update()            
            missed.append(inp)
            t.goto(-290+80*len(missed),60)
            t.write(inp,font = ('Arial',60,'normal'))
            if len(missed) == 6:
                # If all six chances are used up, end game
                messagebox.showinfo
                ("End Game","Sorry, you used up all your six guesses!")
                break 
        # Remove the letter from the validinputs list
        validinputs.remove(inp)       
    # Update everything that happens in the iteration
    t.update()
--snip--

Listing 12-4: A graphical guess-the-word game that takes written input

We import the messagebox module from the tkinter Python package again so we can display messages to the game screen.

Starting at 1, we display the six coins onscreen. We update the screen so that everything we put there shows up properly 2.

At 3, we create the list validinputs, which has the 26 letters in the alphabet as elements. Later in the script, if the player guesses a letter, we’ll remove the letter from the list so that the same letter can’t be guessed more than once. We also create the empty list gotright. Later we’ll use it to keep track of how many positions the player has guessed right in the word.

We start an infinite while loop that asks for your keyboard input in every iteration. If you enter done, the loop stops, and the script quits taking input from you. If you enter invalid input (either a non-letter or a letter you’ve already guessed), the script will show a message box indicating Sorry, that's an invalid input!

If you enter valid input 4, the script checks whether the letter is in the word. If yes, the script checks each of the four positions in the word and, for each match, adds the letter to the list gotright. Note that since the same letter can appear in a word more than once, a letter may be added to the list gotright more than once.

The script then checks whether gotright has four elements. If yes, it means all four letters have been correctly guessed, and a message box will pop up with Great job, you got the word right!

If the guessed letter is not in the word 5, the value of score is decreased by one, meaning the player has one less guess left. The script will remove a coin from the screen by using hideturtle(). The second turtle will erase whatever it has drawn on the screen and rewrite the number of guesses left. If the length of the list missed reaches six, a message box appears: Sorry, you used up all your six guesses!

Here’s one exchange with the script with the user input in bold:

What's your guess?
a
What's your guess?
o
What's your guess?
d
What's your guess?
c
What's your guess?
b
What's your guess?
k
What's your guess?
m

My losing game is shown in Figure 12-4.

f12004

Figure 12-4: A losing game of guess-the-word

The Voice-Controlled Version

Now we’ll build on the written-input version of the game to add speech functionality. Download guess_word_hs.py and save it in your chapter folder. The new code is highlighted in Listing 12-5.

--snip--
# Import functions from the local package
from mptpkg import voice_to_text, print_say
--snip--
# Start the game loop
1 while True:
    # Ask for your move
    print_say("What's your guess?")
    # Capture your voice input
    inp = voice_to_text().lower()
    print_say(f"you said {inp}")
    inp = inp.replace('letter ','')
# Say "stop listening" or press CTRL-C to stop the game
    if inp == "stop listening":
        break
    # If the letter is not a valid input, remind
    elif inp not in validinputs:
        print_say("Sorry, that's an invalid input!")
    # Otherwise, go ahead with the game
  2 else:  
        # Check if the letter is in the word
        if inp in list(word):
            # If yes, put it in the right position(s)
            for w in range(4):
                if inp == list(word)[w]:
                    t.goto(-250+150*w,-190)
                    t.write(inp,font = ('Arial',60,'normal'))
                    gotright.append(inp)
            # If got four positions right, the player wins
            if len(gotright) == 4:
              3 print_say("Great job, you got the word right!")
                messagebox.showinfo
                ("End Game","Great job, you got the word right!")
                break
        # If the letter is not in the word, show it at the top
        else:
            # Reduce guesses left by 1
            score -= 1
            # Remove a coin
            coins[-(6-score)].hideturtle()
            # Update the number of guesses left on board
            left.clear()
            left.write
            (f"guesses left:   {score}",font = ('Arial',20,'normal'))
            t.update()            
            missed.append(inp)
            t.goto(-290+80*len(missed),60)
            t.write(inp,font = ('Arial',60,'normal'))
            if len(missed) == 6:
                # If all six changes are used up, end game
              4 print_say("Sorry, you used up all your six guesses!")
                messagebox.showinfo
                ("End Game","Sorry, you used up all your six guesses!")
--snip--

Listing 12-5: A graphical guess-the-word game that takes voice input

We import the usual functions from our local package mptpkg: voice_to_text() and print_say(). Because we installed the package (in editable mode), there’s no need to tell the system where to find it.

We start an infinite while loop that asks for your choice of letter in each iteration 1. You speak your guess into the microphone, and the script captures your voice command and stores it in inp. We make allowances so the player can say either “letter a” or just “a.” If the former, we replace letter with an empty string so that only a is left in the variable inp.

To stop the while loop, you say, “Stop listening.” If your guess is not in the list validinputs, the script will answer, “Sorry, that’s an invalid input!” out loud. If your guess is in validinputs 2, the script checks whether the letter is in the word. This time, when you complete the word without missing six times, the game will say, “Great job, you got the word right!” 3. If you guess wrong six times, the voice will say, “Sorry, you used up your six guesses!” 4.

Here’s an exchange with the script in which the player has successfully guessed the word good, missing only two letters:

What's your choice?
you said letter a
What's your choice?
you said letter d
What's your choice?
you said letter f
What's your choice?
you said letter o
What's your choice?
you said letter g
Great job, you got the word right!

You can see the screen in Figure 12-5.

f12005

Figure 12-5: Winning the voice-controlled guess-the-word game

Summary

In this chapter, you created a voice-controlled graphical guess-the-word game that talks back to you in a human voice.

You first learned how to draw the game board. You then learned to upload a picture file to the script and scale it to the size you want. You used the image to create six coins on the screen to represent monetary rewards and made them disappear from the screen one by one. You also learned how to type in your guess and have it show up onscreen. You learned how to disallow guessing the same letter twice and how to determine whether a player has won or lost the game.

You added the speech recognition and text-to-speech features so that the game can be voice controlled. Along the way, you learned how to create an image by manipulating a picture file in turtle and how to use multiple turtles to reduce the number of objects you have to redraw on the screen.

End-of-Chapter Exercises

  1. Modify show_coins.py so that the positions of the six coins are 10 pixels below their current positions vertically. Keep the positions of everything else the same.
  2. Modify show_coins.py so that the leftmost coin disappears from the screen first and the rightmost one is the last to disappear.
  3. Try to figure out what the following line of code will produce. First write down your answer and then run the code in Spyder to verify.
    list('Hi Python')
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.154.208