Hangman game in Ruby

Do not miss this exclusive book on Binary Tree Problems. Get it now for free.

Content

  1. Introduction
  2. Summary
  3. Ruby2d
  4. Hangman
    • initialize
    • key press events
    • guesses/ win or lose
    • compare word and letters
    • display
    • stick figure
    • guesses remaining/gameover/message
    • call game and key events
  5. Conclusion

Introduction

In this article at OpenGenus, we will be building a traditional hangman game with the use of ruby2d as the GUI, the game will choose a random word from an array and the user will have to guess what that word is. The user will have 6 chances to guess the word, before it is GAME OVER!!!.




Summary

The Game is built using ruby2d. The main window has a white background, the game starts by introducing a blue line where your correct guess will appear, below that is an empty array to store your incorrect guesses , then the remaining number of guesses and a hint to help you guess the correct word. When you fill the blue line with the correct word a 'won' message appears, if you dont get the correct word before you run out of guesses you would see the fully drawn hangman on the game window then 'game over' appears. This game was implemented using one class with a number of methods to handle specific tasks that were needed in the game, an initialize method with default variables for when the game starts, a method to randomly select a word for the user to guess, another to deal with key that is pressed to enter the letter, a couple of methods that deal with comparing if that letter is correct or not and a another set of methods, that deal with the overall display of the game we see, when it is in progress and when it has been completed or game over.

Ruby2d

Ruby2D is a simple and lightweight 2D game development framework for the Ruby programming language. It provides an easy-to-use interface for creating graphics, handling user input, playing audio, and managing windowing and event-driven programming.

Hangman

Firstly we start by loading the required gem "ruby2d", we do this by adding it to the GEM file gem'ruby2d' and then we run bundle install in our terminal after this we create a new file and add our first line require 'ruby2d', we then use the set method of ruby2d to add the title and background of our GUI

require 'ruby2d'

set title: "Hangman"
set background: 'white'

initialize

Next we create our Hangman class and add our initialize method, this will have five variables @word to store our randomly selected word which is chosen from our select_word method, @incorrect_guesses to store our wrong guesses, likewise correct_guesses to store our right guesses, @max_guesses to compare when we reached our guess limit and @game_over to let us know when the game has ended.

class Hangman
  def initialize
    @word = select_word
    @incorrect_guesses = []
    @correct_guesses = []
    @max_guesses = 6
    @game_over = false
  end
  
  def select_word
    words = ["ruby", "hangman", "games", "programming", "openai"]
    words.sample
  end

key press events

We continue with our next method handle_key_press which takes one argument event, now we are going to add a few methods here which we will create later so dont be alarmed. First we have a guess variable that is equal to the lowercase of whatever key we press, then we make our condition and say if we have a valid guess and our game is not over and our word includes our guess then we add our guess to our @correct_guesses array and if we have won our @game_over become true else if the guess is incorrect we add our guess to our @incorrect_guesses array and if it is a loss our game_over will still be true.

---previous code---

def handle_key_press(event)
    guess = event.key.downcase

    if valid_guess?(guess) && !@game_over
      if @word.include?(guess)
        @correct_guesses << guess
        if won?
          @game_over = true
        end
      else
        @incorrect_guesses << guess
        if loss?
          @game_over = true
        end
      end
    end
  end

guesses/ win or lose

Here we will add the mothods that were mentioned previously, valid_guess?, won?, loss?. Our valid_guess method takes one argument guess and says if our guess length is the same as 1 and our guess matches any lowercase letter from a to z and our guess is included in our @correct_guesses or @incorrect_guesses then this is what makes a valid guess. Our won? method just checks to see if visible_word(a method we will add next) matches our @word(chosen word), and finally our loss method check the length of our @incorrect_guesses variable to see if it is greater or equal to our @max_guesses variable.

----previous code----

def valid_guess?(guess)
     guess.length == 1 && guess.match?(/[a-z]/) && !(@correct_guesses.include?(guess) || @incorrect_guesses.include?(guess))
end

def won?
  visible_word == @word
end

def loss?
  @incorrect_guesses.length >= @max_guesses
end

compare word and letters

The next method we have is visible_word, this compare our completed input to @word so we would know if we have won. we declare visible as and empty string then we check to see if each letter of @word is in @correct_guesses we then add those letter to visible to form our word.

----previous code----

def visible_word
  visible = ""
  @word.chars.each do |char|
    visible << (@correct_guesses.include?(char) ? char : "_")
  end
  visible
end

display

We will now create 2 methods to display the game when it is in play and when it is game over. The first one is display_board this includes the hints for the user, we simply say if @word is a particular word then the hint should be a particular hint. Then we use the Text method of ruby2d to display the text we want in the game, Text.new(visible_word, ....) displays the letters for the word when we input the correct letter,Text.new("Incorrect Guesses:...) displays the incorrect letters the user has inputted,Text.new("Guesses Remaining:...) displays the amount of guesses remaining and Text.new("A hint: #{hint}",...) displays the hint for the correct word.

----previous code----

def display_board
    hint = ""
    if @word == "ruby"
      hint = "computer language"
      elsif
        @word == "hangman"
        hint = "swinging male"
      elsif
        @word == "games"
        hint = "what we play"
      elsif
        @word == "programming"
        hint = "make the computer speak"
      else
        @word == 'openai'
        hint = "artificial intelligence"
    end
    Text.new(visible_word, color: 'blue', x: 10, y: 10, size: 50)
    Text.new("Incorrect Guesses: #{@incorrect_guesses}", color:'black', x: 10, y: 100)
    Text.new("Guesses Remaining: #{guesses_remaining}", color:'black', x: 10, y: 130)
    Text.new("A hint: #{hint}", color: 'blue', x: 10, y: 160)
    stick_figure
end

This next method shows text for when you win or lose. Our method display_game_over is just a condition that says if loss? display these set of text else(if won?) display these text instead.

----previous code----

def display_game_over
    if lost?
      Text.new("GAME OVER", color:'red', x: 200, y: 100, size: 60)
      Text.new("The word was: #{@word}", color:'black', x: 220, y: 200, size: 30)
      Text.new(message, color:'black', x: 220, y: 300, size: 30)
      stick_figure
    else
      Text.new("YOU WON, CONGRATS", color: 'blue', x: 80, y: 100, size: 60)
      Text.new(message, color:'black', x: 250, y: 300, size: 30)
    end
  end

stick figure

This method uses different aspects of ruby2d to draw the hangman on the window. We begin with setting the base with a Square and a few Lines, these will be shown by default from the start of the game. We then proceed to create the body by using Circle.new and Line.new, these objects are put into variables to be used withing the coming if statement. In this if statement we use the length of the @incorrect_guesses array to say which part of the hangman body we will shown, if the length is zero then no part is shown, we do this by using the remove method of ruby2d and if the length is one then we show the head by using the add method on the head variable but leave the rest of the body parts as remove. This trend continues as the length of the @incorrect_guesses increases until all the body parts are added to the window, which will mean "GAME OVER".

----previous code----
def stick_figure
    Square.new(
      x: 300, y: 500,
      size: 50,
      color: 'blue',
      z: 10
    )
    Line.new(
      x1: 325, y1: 300,
      x2: 325, y2: 500,
      width: 10,
      color: 'lime',
      z: 20
    )
    Line.new(
      x1: 325, y1: 300,
      x2: 450, y2: 300,
      width: 10,
      color: 'lime',
      z: 20
    )
    Line.new(
      x1: 450, y1: 300,
      x2: 450, y2: 350,
      width: 10,
      color: 'lime',
      z: 20
    )
    head = Circle.new(
      x: 450, y: 370,
      radius: 20,
      sectors: 32,
      color: 'fuchsia',
      z: 10
    )
    body = Line.new(
      x1: 450, y1: 390,
      x2: 450, y2: 500,
      width: 10,
      color: 'fuchsia',
      z: 20
    )
    arm_one = Line.new(
      x1: 450, y1: 410,
      x2: 375, y2: 410,
      width: 10,
      color: 'fuchsia',
      z: 20
    )
    arm_two = Line.new(
      x1: 450, y1: 410,
      x2: 525, y2: 410,
      width: 10,
      color: 'fuchsia',
      z: 20
    )
    leg_one = Line.new(
      x1: 450, y1: 500,
      x2: 400, y2: 575,
      width: 10,
      color: 'fuchsia',
      z: 20
    )
    leg_two = Line.new(
      x1: 450, y1: 500,
      x2: 500, y2: 575,
      width: 10,
      color: 'fuchsia',
      z: 20
    )
    
    if @incorrect_guesses.length == 0
      head.remove
      body.remove
      arm_one.remove
      arm_two.remove
      leg_one.remove
      leg_two.remove
      elsif 
        @incorrect_guesses.length == 1
        head.add
        body.remove
        arm_one.remove
        arm_two.remove
        leg_one.remove
        leg_two.remove
      elsif
        @incorrect_guesses.length == 2
        head.add
        body.add
        arm_one.remove
        arm_two.remove
        leg_one.remove
        leg_two.remove
      elsif
        @incorrect_guesses.length == 3
        head.add
        body.add
        arm_one.add
        arm_two.remove
        leg_one.remove
        leg_two.remove
      elsif
        @incorrect_guesses.length == 4
        head.add
        body.add
        arm_one.add
        arm_two.add
        leg_one.remove
        leg_two.remove
      elsif
        @incorrect_guesses.length == 5
        head.add
        body.add
        arm_one.add
        arm_two.add
        leg_one.add
        leg_two.remove
      else
        @incorrect_guesses.length == 6
        head.add
        body.add
        arm_one.add
        arm_two.add
        leg_one.add
        leg_two.add
    end
  end

guesses remaining/gameover/message

The next few methods are pretty self explanatory, guesses_remaining minus the length of @incorrect_guesses from the @max_guesses, game_over simply calls the @game_over variable and message has a string letting the user know which button to press to restart the game. We then just set the width and height of our display screen/ game area, this is done outside of our Hangman class

----previous code----

    def guesses_remaining
       @max_guesses - @incorrect_guesses.length
      end

    def game_over
      @game_over
    end

    def message
      "Press 'spacebar' to restart"
    end
end

set width: 800, height: 600

call game and key events

This Last part is done outside of our Hangman class, we first call a new game game = Hangman.new, then we use 2 of the ruby2d methods on :key_down and update. With on :key_down we call the handle_key_press method this lets the program know which keys have been pressed and we also say if the key is space(spacebar) then we call a new hangman game(restart the game). Our update method is saying if the game is over we clear the window and call our display_game_over method else we just call our display_board method, we then wrap it all up with our ruby2d show method at the end.

----previous code----

game = Hangman.new

on :key_down do |event|
  game.handle_key_press(event)
  if event.key == 'space'
    game = Hangman.new
  end
end

update do
  clear
  if game.game_over
    game.display_game_over
  else
    game.display_board
  end
end

show

complete code below

require 'ruby2d'

set title: "Hangman"
set background: 'white'


class Hangman
  def initialize
    @word = select_word
    @incorrect_guesses = []
    @correct_guesses = []
    @max_guesses = 6
    @game_over = false
  end

  def select_word
    words = ["ruby", "hangman", "games", "programming", "openai"]
    words.sample
  end

  def handle_key_press(event)
    guess = event.key.downcase

    if valid_guess?(guess) && !@game_over
      if @word.include?(guess)
        @correct_guesses << guess
        if won?
          @game_over = true
        end
      else
        @incorrect_guesses << guess
        if loss?
          @game_over = true
        end
      end
    end
  end

  def valid_guess?(guess)
    guess.length == 1 && guess.match?(/[a-z]/) && !(@correct_guesses.include?(guess) || @incorrect_guesses.include?(guess))
  end

  def won?
    visible_word == @word
  end

  def loss?
    @incorrect_guesses.length >= @max_guesses
  end

  def visible_word
    visible = ""
    @word.chars.each do |char|
      visible << (@correct_guesses.include?(char) ? char : "_")
    end
    visible
  end

  def display_board
    hint = ""
    if @word == "ruby"
      hint = "computer language"
      elsif
        @word == "hangman"
        hint = "swinging male"
      elsif
        @word == "games"
        hint = "what we play"
      elsif
        @word == "programming"
        hint = "make the computer speak"
      else
        @word == 'openai'
        hint = "artificial intelligence"
    end
    Text.new(visible_word, color: 'blue', x: 10, y: 10, size: 50)
    Text.new("Incorrect Guesses: #{@incorrect_guesses}", color:'black', x: 10, y: 100)
    Text.new("Guesses Remaining: #{guesses_remaining}", color:'black', x: 10, y: 130)
    Text.new("A hint: #{hint}", color: 'blue', x: 10, y: 160)
  end

  def display_game_over
    if loss?
      Text.new("GAME OVER", color:'red', x: 200, y: 100, size: 60)
      Text.new("The word was: #{@word}", color:'black', x: 220, y: 200, size: 30)
      Text.new(message, color:'black', x: 220, y: 300, size: 30)
    else
      Text.new("YOU WON, CONGRATS", color: 'blue', x: 80, y: 100, size: 60)
      Text.new(message, color:'black', x: 250, y: 300, size: 30)
    end
  end


  def guesses_remaining
    @max_guesses - @incorrect_guesses.length
  end

  def game_over
    @game_over
  end

  def message
    "Press 'spacebar' to restart"
  end
end

set width: 800, height: 600

game = Hangman.new

on :key_down do |event|
  game.handle_key_press(event)
  if event.key == 'space'
    game = Hangman.new
  end
end

update do
  clear
  if game.game_over
    game.display_game_over
  else
    game.display_board
  end
end

show

Conclusion

With this article at OpenGenus, you have just created a hangman game using ruby CONGRATULATIONS, now you can explore more with ruby and ruby2d to make even more games.

Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.