Step up your coding game withAI-powered Code Explainer. Get insights like never before!
Hangman is a pretty popular game project for beginner programmers. It is a word guessing game by guessing each secret word's letter one by one and for every error guess you got, a man's body part is drawn hanging in the gallow. If the figure of a man is finished (head, body, arms, and legs are drawn), that means you are out of guesses and the game is over.
In a previous tutorial, we've madea text-based hangman game. However, in this tutorial, we'll learn about how to create a graphical hangman game using Pygame in Python.
Let's start by installing the Pygame module withpip orpip3 in our terminal:
$ pip install pygameThen createhangman.py in your app directory. I name minehangman-gui. Another file we need is thewords.txt which contains words where we pick a secret word to guess.
Now head tohangman.py and importpygame module,pygame.locals for font and other stuff,os for accessing thewords.txt file,random module for random word picking, andascii_letters to make sure the player's guess is ASCII.
# hangman-gui/hangman.pyimport pygamefrom pygame.locals import *import osimport randomfrom string import ascii_lettersLet's check first if everything will works:
pygame.init()pygame.font.init()font = pygame.font.SysFont("Courier New", 40)running = Truescreen = pygame.display.set_mode((400, 500))screen.fill("white")message = font.render("hello world", True, ("red"))screen.blit(message,(0,0))pygame.display.update()while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = Falsepygame.quit()Let's run our app by typingpython hangman.py in our terminal in thehangman-gui directory:

Now that we see Pygame is working we can delete all the codes (left the imports) and type:
pygame.init()pygame.font.init()screen = pygame.display.set_mode((400, 500))pygame.display.set_caption("Hangman")Thepygame.init() initiates the game while thepygame.font.init() initiates the fonts so we can use the Pygame's built-in fonts. Thescreen creates the game window with the caption "Hangman" at the top.
Let's start coding by adding theHangman class and inside it, make a function named__init__() which needs components such asself.secret_word,self.guessed_word,self.wrong_guesses (a list of false guess),self.wrong_guess_count,self.taking_guess, andself.running:
class Hangman(): def __init__(self): with open("./words.txt", "r") as file: # picks secret word and passing it's length words = file.read().split("\n") self.secret_word = random.choice(words) # passing secret word's length for making letter blanks self.guessed_word = "*" * len(self.secret_word) self.wrong_guesses = [] self.wrong_guess_count = 0 self.taking_guess = True self.running = TrueAs you noticed, I put two boolean variables, theself.taking_guess and theself.running, with the same value.
Theself.running manage to run the game. While true, it will render the game window continuously so it won't automatically collapse. When the user hitsthe exit button of the window, the value of theself.running will turn toFalse causing to collapse the game window to.
On the other hand, theself.taking_guess controls the ability to take guesses. While its value isTrue, it allows the user to enter a guess and when the game is over (the user had won/lost), its value will turn toFalse, not allowing the player to make guesses anymore.
Let's also add other game components colors, font, etc. we were using later.
class Hangman(): def __init__(self): ... self.background_color = (155, 120, 70) self.gallow_color = (0,0,0) self.body_color = (255,253,175) self.font = pygame.font.SysFont("Courier New", 20) self.FPS = pygame.time.Clock()Now let's proceed to the other parts of the game.
The gallow is where our man figure will be hanging. To draw the gallow, we are using PygameRects. TheRect is short for rectangle, with the syntax ofpygame.Rect(X, Y, Width, Height)). To draw it on the game surface, you can dopygame.draw.rect(screen, color, pygame.Rect(X, Y, Width, Height)) thenpygame.display.flip() to update the screen so we can see theRect.
Inside theHangman class, make a function named_gallow(). This function will draw the gallow image using the rects:
def _gallow(self): stand = pygame.draw.rect(screen, self.gallow_color, pygame.Rect(75, 280, 120, 10)) body = pygame.draw.rect(screen, self.gallow_color, pygame.Rect(128, 40, 10, 240)) hanger = pygame.draw.rect(screen, self.gallow_color, pygame.Rect(128, 40, 80, 10)) rope = pygame.draw.rect(screen, self.gallow_color, pygame.Rect(205, 40,10, 30))Each of these game configs will draw an image of a gallow andpygame.display.flip() pass it to the game window, which we'll put inside the while loop later. The reason is if we put multipleflip() orupdate() per function and call them all together, it will cause our whole game to over and over while running.
Now that we have a gallow, let's draw the man figure part by part which represents how many wrong guesses the player already has. Create a_man_pieces() function inside the class which we call every time we want to draw the man's body parts for every wrong guess. The man is supposed to be placed close to the gallow'srope so it will look like he is hanging in it:
def _man_pieces(self): head = pygame.draw.circle(screen, self.body_color, [210, 85], 20, 0) body = pygame.draw.rect(screen, self.body_color, pygame.Rect(206, 105, 8, 45)) r_arm = pygame.draw.line(screen, self.body_color, [183, 149], [200, 107], 6) l_arm = pygame.draw.line(screen, self.body_color, [231, 149], [218, 107], 6), r_leg = pygame.draw.line(screen, self.body_color, [189, 198], [208, 148], 6), l_leg = pygame.draw.line(screen, self.body_color, [224, 198], [210, 148], 6)Just like the gallow, each this piece will draw a figure of a human inside the game window after calling theflip().
Now we'll try to run the game to see what the gallow and the man looks like. Let's create another function in the class and name itmain(). This function controls running and/or stopping the game:
def main(self): # game's main components (no need to update) screen.fill(self.background_color) self._gallow() self._man_pieces() while self.running: for self.event in pygame.event.get(): if self.event.type == pygame.QUIT: self.running = False pygame.display.flip() self.FPS.tick(60) pygame.quit()Inside thewhile loop of themain() function, we put afor loop that catches all types of events so we can catch for every second all the events happening inside the game window (mouse movements, keys pressed, mouse clicks, etc.). We also put theflip() and the clock tick so it will refresh all the animation inside thewhile loop for every second at a normal speed.
We put thescreen.fill(), theself._gallow(), and theself._man_pieces() ahead and outside of thewhile loop because these animations do not need to refresh per frame so our code will run faster.
It is also important to putpygame.quit() after thewhile loop or else we'll catch some error.
Below the class and outside of it, let's call the game and then run thehangman.py in our terminal to see what it looks like:
if __name__ == "__main__": h = Hangman() h.main()
The man was properly placed in the gallow as we expected. Let's create the other parts of the game for handling guesses from keys pressed.
Create another function in the class and name it_guess_taker(). This function will be responsible for taking guesses the player submitted:
def _guess_taker(self, guess_letter): if guess_letter in ascii_letters: if guess_letter in self.secret_word and guess_letter not in self.guessed_word: self._right_guess(guess_letter) elif guess_letter not in self.secret_word and guess_letter not in self.wrong_guesses: self._wrong_guess(guess_letter)The_guess_taker() checks the value of the key pressed by the player as a letter and not a symbol or number by looking at theascii_letters and if it isn't, it will just ignore it. And if it is, it will proceed.
If theguess_letter is inself.secret_word and not yet guessed (guess_letter not in self.guessed_word), it will send the letter in theself._right_guess() function. And if not inself.secret_word, and vice versa, it will send the letter toself._wrong_guess().
We include theguess_letter, not in self.guessed_word andguess_letter not in self.wrong_guesses to not waste a move. This doesn't allow the guess to proceed inself._right_guess() orself._wrong_guess() in case the letter is already guessed, or the letter is already in the wrong guesses to prevent adding another body part just because of the mistake the player already committed in the earlier situation.
Let's update themain() function. Let's put game instructions below and inside thefor loop, we'll call theself._guess_taker() function.
def main(self): # game's main components (no need to update) screen.fill(self.background_color) self._gallow() instructions = self.font.render('Press any key to take Guess', True, (9,255,78)) screen.blit(instructions,(35,460)) while self.running: # shows the guessed word in the game window guessed_word = self.font.render(f"guessed word: {self.guessed_word}", True, (0,0,138)) screen.blit(guessed_word,(10,370)) # shows the wrong guesses in the game window wrong_guesses = self.font.render(f"wrong guesses: {' '.join(map(str, self.wrong_guesses))}", True, (125,0,0)) screen.blit(wrong_guesses,(10,420)) for self.event in pygame.event.get(): ... # manages keys pressed elif self.event.type == pygame.KEYDOWN: if self.taking_guess: self._guess_taker(self.event.unicode) pygame.display.flip() self.FPS.tick(60) pygame.quit()Below the screen fill and the_gallow() call, we removed theself._man_pieces and add some text instructions. We also added to the screen the guessed word and the wrong guesses which we put inside thewhile loop since they are updating for every guess the player.
Theunicode gets the value of the pressed key so if the type of theself.event is aKEYDOWN (a key was pressed) and the game is still taking guessesif self.taking_guess, we'll call theself._guess_taker() function and giveself.event.unicode as its argument.
If we check the game by running, you might see:
Now it has the game instructions saying "Press any key to take a guess" and if you take a guess you'll catch an error.
This is because of the two functions (theself._right_guess() and theself._wrong_guess()) we called but currently, we don't have. And those are the next things we're adding to the game.
Above the_guess_taker() function, create two other functions and name them_right_guess() and_wrong_guess(). These functions will save the guess whether it belongs to the wrong guesses lista or to guessed word (the right guess).
def _right_guess(self, guess_letter): index_positions = [index for index, item in enumerate(self.secret_word) if item == guess_letter] for i in index_positions: self.guessed_word = self.guessed_word[0:i] + guess_letter + self.guessed_word[i+1:] # stacks a layer of color on guessed word to hide multiple guessed_word stack screen.fill(pygame.Color(self.background_color), (10, 370, 390, 20))Theindex_positions collects all the indices where theguessed_letter is present. We put it in a list because some words had one letter two or more than times.Words like'dull' with two letters'l','bottom' with two'o's and't's, and'attention' with three't's.
Once we have them, we'll replace the asterisk"*" character with the letter guessed by the player using the indices we collect. So let's say our secret word is"attention" and the player's guess is the letter't', theindex_positions list contains 1, 2, and 5 since we start our count from 0. Theself.guessed_word will be"*tt**t***".
We also put another layer of color with the same color as the background, this will cover the rest of the stacked guesses behind it that were configured for every right guess the player did. If we didn't put a fill it would be messy and the guessed word is kind of hard to read.

Once an object is already uploaded to the game screen usingflip() orupdate(), it is impossible to remove and one of the fast fixes for this is adding a layer of color fill above the area we want to cover.
Next, let's modify the_wrong_guess() function:
def _wrong_guess(self, guess_letter): self.wrong_guesses.append(guess_letter) self.wrong_guess_count += 1 self._man_pieces()The_wrong_guess() appends the guessed letter toself.wrong_guesses, adds 1 to theself.wrong_guess_count, and calls the_man_pieces() to add a man's body part based on theself.wrong_guess_count.
Let's next modify ourself._man_pieces() since we made it earlier to draw the whole body in just a call. We need it to draw each of the parts for every mistake from the head, to the legs:
# draw man's body parts for every wrong guess def _man_pieces(self): if self.wrong_guess_count == 1: head = pygame.draw.circle(screen, self.body_color, [210, 85], 20, 0) elif self.wrong_guess_count == 2: body = pygame.draw.rect(screen, self.body_color, pygame.Rect(206, 105, 8, 45)) elif self.wrong_guess_count == 3: r_arm = pygame.draw.line(screen, self.body_color, [183, 149], [200, 107], 6) elif self.wrong_guess_count == 4: l_arm = pygame.draw.line(screen, self.body_color, [231, 149], [218, 107], 6), elif self.wrong_guess_count == 5: r_leg = pygame.draw.line(screen, self.body_color, [189, 198], [208, 148], 6), elif self.wrong_guess_count == 6: l_leg = pygame.draw.line(screen, self.body_color, [224, 198], [210, 148], 6)If we try our game, it will work now better, we can take guesses now and check them but it won't tell you whether you win or you lose. It doesn't even stop if the man is fully drawn or the world is already guessed.
In the next one, we're adding the game situation analyzer which also sends us the message for the game state.
Below the_guess_taker() create a function we call_message(). This will serve as the analyzer of the game situation and also the responsible for sending messages about whether the player wins or loses:
def _message(self): # win situation if self.guessed_word == self.secret_word: self.taking_guess = False screen.fill(pygame.Color(0,0,79), (40, 218, 320, 30)) message = self.font.render("YOU WIN!!", True, (255,235,0)) screen.blit(message,(152,224)) # lose situation elif self.wrong_guess_count == 6: self.taking_guess = False screen.fill(pygame.Color("grey"), (40, 218, 320, 30)) message = self.font.render("GAME OVER YOU LOSE!!", True, (150,0,10)) screen.blit(message,(78,224)) # shows the secret word if the player lose word = self.font.render(f"secret word: {self.secret_word}", True, (255,255,255)) screen.blit(word,(10,300)) # removes the instruction message if not taking guesses anymore if not self.taking_guess: screen.fill(pygame.Color(self.background_color), (35, 460, 390, 20))We also cover a color layer in the game instructions another indicator that the game is over.
Now let's call it inside themain(). We're placing it inside thewhile loop so it will review the game state for every second. Let's call it above thefor loop:
def main(self): ... while self.running: ... # checking game state self._message() for self.event in pygame.event.get(): ... pygame.display.flip() self.FPS.tick(60) pygame.quit()And now we're done! Here are some of the game snapshots.
The player won with minimal errors:

The player won with no errors:
The game while playing.
The player loses:
Now we know how to create a pretty cool and easy game with Pygame. Discover new words and have fun by playing Hangman.
In this tutorial, we learned about the basics of the Pygame module and make a Hangman game from scratch. I hope you learned something and you liked this tutorial. See you at the next one and have a nice day!
You can checkthe complete code here.
Here are some PyGame tutorials:
Happy coding ♥
Liked what you read? You'll love what you can learn from ourAI-powered Code Explainer. Check it out!
View Full Code Convert My CodeGot a coding query or need some guidance before you comment? Check out thisPython Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!
