Chapter 7. Working with Regular Expressions

In previous chapters, you learned how to create an assortment of different computer games. In most of these games, you more or less accepted whatever input the player provided, with little or no validation. In this chapter, this changes because you will learn how to use regular expressions. Using regular expressions, you can perform a detailed analysis of user input to determine if it meets criteria you specify. Using regular expressions, you will also be able to pick apart and process the contents of data regardless of its source, including text files and databases. You will also learn how to perform text-substitution operations and to deal with differences in case. On top of all this, this chapter will show how to create a new Ruby script, the Word Guessing game.

Specifically, you will learn how to:

  • Set up basic regular expression patterns

  • Set up regular expression patterns to match groups of characters

  • Set up regular expression patterns to match individual or multiple instances of characters

  • Use metacharacters when setting up regular expression patterns

  • Perform text substitution and to ignore differences in case

Project Preview: The Word Guessing Game

In this chapter, you will learn how to create a new computer script called the Word Guessing game. This game challenges the player to try to guess a secret word in three or fewer guesses. Before making any guesses, the player is allowed to specify five consonants and one vowel, which, if present in the word, are revealed, providing the player with a clue as to what the word might be.

The Word Guessing game begins by displaying the message shown in Figure 7.1, welcoming the player to the game.

The Word Guessing game’s welcome message.

Figure 7.1. The Word Guessing game’s welcome message.

The player is then prompted for permission to start a round of play, as demonstrated in Figure 7.2.

The player is prompted for permission to start a new game.

Figure 7.2. The player is prompted for permission to start a new game.

Before the first round of play begins, instructions for playing the game are presented as shown in Figure 7.3.

The player is told how to play the game.

Figure 7.3. The player is told how to play the game.

Next, the player is asked to specify five consonants, as demonstrated in Figure 7.4.

To make things easier, the player gets to provide a list of consonants that will be revealed if they are part of the secret word.

Figure 7.4. To make things easier, the player gets to provide a list of consonants that will be revealed if they are part of the secret word.

Consonants are collected one at a time, as shown in Figure 7.5. If the player attempts to enter a vowel, number, special character, or more than one character at a time, her input is rejected, and she will be prompted to try again.

Using regular expressions, the player’s input is carefully evaluated before being accepted.

Figure 7.5. Using regular expressions, the player’s input is carefully evaluated before being accepted.

Once all five consonants have been collected, the player is prompted to specify a vowel. The game then displays a clue to its secret word and challenges the player to try to guess it, as demonstrated in Figure 7.6.

The player is given three tries to guess the secret word.

Figure 7.6. The player is given three tries to guess the secret word.

Figure 7.7 shows the message that is displayed if the player guesses the secret word.

The player has won the game by figuring out the secret word.

Figure 7.7. The player has won the game by figuring out the secret word.

If the player’s first guess is incorrect, two more chances are given to guess the secret word. Figure 7.8 shows the message that is presented before the player is permitted to make a final guess.

The game is ready to let the player make one last guess.

Figure 7.8. The game is ready to let the player make one last guess.

If the player is unable to guess the game’s secret word, the screen shown in Figure 7.9 is displayed letting the user know that the current round of play is over and revealing the secret word to the player.

The player has failed to guess the game’s secret word.

Figure 7.9. The player has failed to guess the game’s secret word.

At the end of each round of play, the game prompts the player for permission to start another round. Depending on the player’s response, the game is either terminated or a new secret word is selected, and play begins again.

The Basics of Working with Regular Expressions

Data processed in computer scripts can come from many different sources, including databases, files, as input passed from other scripts and programs, and from the users, as is the case with computer games. While you can certainly trust in the source of the data to provide your scripts with valid data, you often do so at your own peril. In most cases, you will want to build data-validation logic into your own scripts, rejecting any data that is not in the proper format.

You can perform a certain amount of data validation using conditional logic and methods belonging to Ruby classes. However, to create really granular data-validation routines, you will need to take things a step further and use regular expressions. A regular expression is a pattern used to identify matching character data.

In previous chapter projects, you performed limited amounts of data validation. In these scripts, you were able to simplify data validation by restricting valid data to very limited and specific sets of characters, rejecting any data provided by the player that did not match the script’s precise requirements. For example, in the Superman Movie Trivia Quiz, you rejected as invalid any player input that was not an a, b, c, or d when processing quiz questions, as shown here:

#Analyze the player to determine if it was valid
if reply == "a" or reply == "b" or reply == "c" or reply == "d" then
  break  #Terminate the execution of the loop
end

When processing this type of restricted input, you can use basic conditional logic to process the player input. The following statements, taken from the Ruby Number Guessing game, provide another example of how input data validation was implemented.

#Validate the player's input only allowing guesses from 1 to 100
if reply < 1 or reply > 100 then
  redo  #Redo the current iteration of the loop
end

Here, numeric data is validated using conditional logic, and any value that is below 1 or greater than 100 is rejected. This type of data validation worked well in this example because the input allowed by the script was restricted to a very specific range (1 to 100). Things become a lot trickier when you begin writing scripts that can accept and process many different types of input. For example, this chapter’s game project is the Word Guessing game. This game will accept any alphabetic characters as input. Given the number of letters in the alphabet, it is not practical to use either of the two previous data-validation examples as models for solving this problem. Instead, as you will see, this script will validate incoming data using several different validation techniques, including regular expressions.

Regular expressions are not just limited to data validation. Regular expressions can also be used to search for data within strings and to perform string substitutions. For example, using regular expressions, you can define patterns that:

  • Perform search and replace operations on strings

  • Keep counts of instances of patterns

  • Extract a substring from a larger string

Learning How to Match Basic Patterns

In most cases, regular expression patterns are processed from left to right. Matches occur when patterns are found anywhere within a source string. By default, matches are case sensitive, although you can override this behavior. Also, every character inside a pattern is taken literally, with the exception of metacharacters, to which regular expressions assign a special meaning.

Matching Basic Patterns

The most basic type of regular expression is one that matches up against a specific pattern or a set of characters. This type of pattern can be set up using the // match operator, which has the following syntax.

/pattern/

For example, take a look at the following pattern.

/USA/

This pattern will match the occurrence of the characters USA if found anywhere in a string. If found, a value of true is returned. Otherwise, a value of false is returned. To see how this pattern might be used, take a look at the following set of statements.

if "Welcome to New York Harbor, USA." =~ /USA/ then
  puts "Welcome to America!"
end

Here, the source string “Welcome to New York Harbor, USA.” is searched using a pattern of /USA/. Since the string contains the words USA, a match occurs and the following output is displayed when the statements are executed.

"Welcome to America!"

Take note of the use of the =~ operator, which is the equivalent of the equals operation in a regular expression.

Hint

Hint

In addition to the =~ operator, you should also be familiar with the !~ operator, which is the regular expression equivalent of the not equals operator.

Matching Alternate Patterns

In addition to matching basic patterns as discussed in the previous section, you can set up regular expression patterns that can look for any of a possible set of matches. To set up this type of pattern, you need to use the | character to separate each possible match, as demonstrated here:

if "Welcome the USA!." =~ /USA|America/ then
  puts "We have a match!"
end

Here, a search pattern has been set up that looks for either the string USA or the string America. If either string is found in the source string, the match is successful.

Using this alternate pattern-matching approach, you can set up a pattern that searches for as many different alternate patterns as you want, as demonstrated here:

if "Remember to call your mother." =~ /tall|call|wall|ball|fall|mall/ then
  puts "We have a match!"
end

As you can see, alternate pattern matches can be very convenient. However, they can quickly grow a little long-winded. To make things easier, you can shorten things up using parentheses to separate unique parts of the search pattern from common parts, as demonstrated here:

if "Remember to call your mother." =~ /(t|c|w|b|f|m)all/ then
  puts "We have a match!"
end

Here, /(t|c|w|b|f|m)all/ is just a shorthand way of writing /tall|call|wall|ball|fall|mall/.

Understanding How to Work with Metacharacters

Normally, any character that you include in a regular expression pattern will match the same character if found in the source string. However, metacharacters are an exception to this rule. A metacharacter is a character that alters the way a pattern match occurs, as demonstrated in the following example.

if "My name is Jerry. My father's name is Mr. Ford." =~ /Mr./ then
  print "We have a match!"
end

Here, the source string is searched using the pattern /Mr./. The result is a match. However, the match has occurred for a reason that may not be immediately obvious. On the surface, it appears that the pattern matched because the string Mr. is equal to the string Mr. in the source string. However, something different is actually happening here. Specifically, when used in a regular expression, the . character is viewed as a metacharacter. Metacharacters have a special meaning. In particular, the . metacharacter is used to match up against any individual character. As a result, a pattern of /Mr./ results in a match with Mr., Mrs, and Mrx, etc., which is clearly not what was intended in the previous example.

While metacharacters can be extremely useful, sometimes they can get in the way. In these situations, you can escape the metacharacter by preceding it with a character. When escaped, the metacharacter is taken literally. Therefore, using the escape character, you can force a regular expression to treat the . character like a period and not like a metacharacter, as demonstrated here:

if "My name is Jerry. My father's name is Mr. Ford." =~ /Mr./ then
  print "We have a match!"
end

Table 7.1 lists a number of metacharacters that you will want to become familiar with.

Table 7.1. Regular Expression Metacharacters

Character

Description

.

Matches any character

^

Looks for a match at the beginning of a line

$

Looks for a match at the end of a line

A

Looks for a match at the beginning of a string



Looks for a match at the end of a string

d

Matches any numeric character

w

Matches any alphabetic, numeric, or underscore character

D

Matches any non-numeric character

W

Matches any non-alphabetic, non-numeric, or non-underscore character

s

Matches white space

S

Matches non-white space

You will learn more about how to work with metacharacters as you read through the rest of this chapter.

Matching Individual Characters

As you just learned, you can use the . metacharacter as part of a pattern to match any individual character (except for the new line character). By using multiple instances of the . metacharacter, you can create patterns that match more than one character, as demonstrated here:

if "My name is Jerry. My father's name is Mr. Ford." =~ /f...er/ then
  print "We have a match!"
end

Here, a pattern has been set up to match the lowercase letter f followed by any two characters and the letters er.

Matching a Pattern at the Beginning of a String

By default, a regular expression looks anywhere within a string for a match. However, using the ^ metacharacter, you can create regular expression patterns that only look at the beginning of a line for a match. For example, a pattern of /^My name/ will result in a match only if it is found at the beginning of the source, as demonstrated here:

if "My name is Jerry. My father's name is Mr. Ford." =~ /^My name/ then
  print "We have a match!"
end

Matching a Pattern at the End of a String

The $ metacharacter is the opposite of the ^ metacharacter, performing a match only if the pattern is found at the end of the line, as demonstrated here:

if "My name is Jerry. My father's name is Mr. Ford." =~ /Ford.$/ then
  print "We have a match!"
end

Hint

Hint

If necessary, you can combine the ^ and & metacharacters when creating regular expression patterns. For example, you could combine the ^, $, and . metacharacters to create a pattern that matches only if the source string is made up of a single character, as shown here:

/^.$/

Matching One or Not at All

There may be some situations in which you need to look for a character or a group of characters that occur either once or not at all, with either possibility resulting in a match. You can set this up using the ? metacharacter modifier, which matches zero or none of the preceding characters, as demonstrated here:

if "My name is Jerry. My father's name is Mr. Ford." =~ /Mrs?/ then
  print "We have a match!"
end

Here, a pattern of /Mrs?/ has been specified. This pattern will match a string of Mrs, and if the s is not present, it will also match Mr. You can use the ? metacharacter modifier on groups of characters just as easily, as shown here:

if "My father's name is Mr. Ford." =~ /father('s name)?/ then
  print "We have a match!"
end

As you can see, the trick here is to use parentheses to enclose the group of characters. In this example, a match will occur if either father or father's name is found in the source string.

Matching Zero or More Times

Another metacharacter modifier that you need to know about is the * character. This character is similar to the . metacharacter except that the * metacharacter matches zero or more instances of the preceding character. For example, a pattern of /f*r/ would match any of the following set of characters:

  • father

  • friendlier

  • for

  • r

Hint

Hint

Another useful metacharacter modifier is the + character, which matches one or more instances of the preceding character.

Matching Any of a Collection of Characters

Another type of pattern that you may need to work with is one that searches for a range of characters. This can be set up using character classes, which are enclosed inside a matching pair of square brackets ([]). Any characters placed inside the [] characters are regarded as a single character. Ruby’s support for regular expressions includes support for the character class patterns shown in Figure 7.2.

Table 7.2. Character Class Patterns

Pattern

Description

/[abc]/

Matches any specified lowercase letter (a, b, c)

/[abcdefghijklmnopqrstuvwxyz]/

Matches any lowercase letter

/[0123456789]/

Matches any number between 0 and 9

/[0–9]/

Shorthand option for matching any number between 0 and 9

/[a–z]/

Shorthand option for matching any of the lowercase letters

/[A–Z]/

Shorthand option for matching any of the uppercase letters

To see an example of how to work with character class patterns, look at the following example.

print "Please enter a vowel and press Enter: "
input = STDIN.gets
input.chop!

if input =~ /[aeiou]/ then
  puts "A vowel has been submitted."
end

Here, the user is prompted to enter a vowel (i.e., a, e, i, o, or u). A conditional check is then performed using a regular expression of /[aeiou]/. As a result, a match occurs if the user enters a response that includes at least one vowel.

Other Common Uses of Regular Expressions

Ruby’s support for regular expressions is quite extensive. As a result, there is a lot more that you can do with regular expressions than what has been discussed so far in this chapter. For example, using regular expressions, you can perform case-insensitive pattern searches. In addition, you can also perform complex string substitutions, where one or more instances of a search pattern are used to replace characters in a source string.

Hint

Hint

Regular expressions represent an enormous topic. So much so that entire books have been dedicated to this one topic. If you are interested in learning more about regular expressions, check out Mastering Regular Expressions, Second Edition (ISBN: 0596002890). You might also want to visit http://en.wikipedia.org/wiki/Regular_expression.

Overcoming Differences in Case

So far, all of the regular expression patterns that you have used have worked because the case used inside the pattern matched the case used in the source string. It won’t always work out this well. To eliminate case as an issue, you can disable case-sensitivity using the optional i modifier, as demonstrated here:

if "Welcome to New York Harbor, USA." =~ /usa/i then
  puts "Welcome to America!"
end

When used, the i modifier allows the pattern to match characters in the source string, regardless of the case that is in use. So this example results in a match, even though lowercase characters have been specified in the pattern and uppercase characters are used in the source string. If you were to remove the i modifier from the above example, a case-sensitive match would be attempted and the result would be no match.

String Substitution

In addition to finding and validating string contents, you can modify strings using character substitution with the String class’s sub method. This method accepts as an argument a search pattern, which can be any regular expression pattern or string, and a replacement pattern. The sub method has the following syntax.

string.sub(search, replace)

This method searches string and substitutes the first instance of search that it finds with replace. To get a feel for how this method works, take a look at the following example.

x = "Once upon a time there was a small boy who climbed a small tree."
puts x.sub("small", "big")

Here, a string is assigned to a variable named x. Next, the sub method is called and passed "small" as the search pattern and "big" as the replacement pattern. As a result, the following output is displayed when these statements are executed.

Once upon a time there was a big boy who climbed a small tree.

Here, the first instance of the word small has been replaced with the word big. However, the source string contained two instances of the word small and the second instance was not replaced. If you want to replace all instances of the search pattern, you need to work with the String class’s gsub method instead. Like the sub method, the gsub method accepts as an argument a search pattern, which can be any regular expression pattern or string, and a replacement pattern. Using gsub, you could rewrite the previous example as shown here:

x = "Once upon a time there was a small boy who climbed a small tree."
puts x.gsub("small", "big")

When executed, this example displays the following output.

Once upon a time there was a big boy who climbed a big tree.

Back to the Word Guessing Game

All right, that’s enough about regular expressions for now. It is time to turn your attention back to the creation of this chapter’s game project, the Word Guessing game. Remember to follow along carefully, watch for typos, and not omit any steps.

Designing the Game

The development of the Word Guessing game will be completed in 15 steps as outlined here:

  1. Open your text or script editor and create a new file.

  2. Add comment statements to the beginning of the script file to document the script and its purpose.

  3. Define a class representing the terminal window.

  4. Define a class representing the Number Guessing game.

  5. Add a display_greeting method to the Game class.

  6. Add a display_instructions method to the Game class.

  7. Add a select_word method to the Game class.

  8. Add a get_consonants method to the Game class.

  9. Add a get_vowel method to the Game class.

  10. Add a prompt_for_guess method to the Game class.

  11. Add a play_game method to the Game class.

  12. Add a display_credits method to the Game class.

  13. Instantiate script objects.

  14. Prompt the player for permission to begin the game.

  15. Set up the game’s controlling logic.

Step 1: Creating a New Ruby File

The first step in developing the Word Guessing game is to start your preferred text or code editor, create a new Ruby script file, and save the script file with a name of WordGuess.rb.

Step 2: Documenting the Script and Its Purpose

Now let’s add a few comment statements to the beginning of the script file to provide a high-level overview of the game and to explain what it does. To do this, add the following statements to the end of the script file.

#--------------------------------------------------------------------------
#
# Script Name: WordGuess.rb
# Version:     1.0
# Author:      Jerry Lee Ford, Jr.
# Date:        October 2007
#
# Description: This Ruby script demonstrates how to work with regular
#              expressions through the development of a computer game
#              that challenges the player to guess a mystery word after
#              being first allowed to guess 5 consonants and 1 vowel.
#
#--------------------------------------------------------------------------

Step 3: Creating the Screen Class

The Word Guessing game will utilize two custom classes that will provide it with a collection of methods required to control user interaction and the execution of the game. The code statements for the script’s first custom class, the Screen class, are shown next and should be added to the end of the script file.

# Define custom classes ---------------------------------------------------

#Define a class representing the console window
class Screen

  def cls  #Define a method that clears the display area
    puts ("
" * 25)  #Scroll the screen 25 times
    puts "a"  #Make a little noise to get the player's attention
  end

  def pause    #Define a method that pauses the display area
    STDIN.gets  #Execute the STDIN class's gets method to pause script
                #execution until the player presses the Enter key
  end

end

The Screen class contains two methods. The cls method writes 25 blank lines to the console window to clear the screen and then makes an audible beep sound. The pause method uses the STDIN class’s gets method to pause script execution until the player presses the Enter key.

Step 4: Creating the Game Class

The code statements that make up the Game class are shown next and should be added to the end of the script file. This class contains eight methods that are needed to control the game’s execution. To begin the creation of the Game class, add the following statements to the end of the script file.

#Define a class representing the Word Guessing Game
class Game

end

Step 5: Defining the display_greeting Method

The first of the methods defined in the Game class is display_greeting and as the name implies, it is responsible for displaying the game’s welcome message. The statements that make up this method are shown next and should be inserted between the game class’s opening and closing statements.

#This method displays the game's opening message
def display_greeting

  Console_Screen.cls  #Clear the display area

  #Display welcome message
  print "			Welcome to the Word Guessing Game!" +
  "













Press Enter to " +
             "continue."

Console_Screen.pause       #Pause the game
end

Step 6: Defining the display_instructions Method

The next method to be added to the Game class is the display_instructions method. The script statements for this method are shown next and should be added to the class definition, immediately after the previously defined method.

#Define a method to be used to present game instructions
def display_instructions

  Console_Screen.cls       #Clear the display area
  puts "INSTRUCTIONS:

"  #Display a heading

  #Display the game's instructions
  puts "At the start of each new round of play, the game will randomly"
  puts "select a word that is between 5 and 10 characters long"
  puts "and challenge you to guess it. Before submitting your guess, you"
  puts "are allowed to provide the game with 5 consonants and 1 vowel to"
  puts "determine if they are used in the secret word.


"


  puts "Good luck!








"
  print "Press Enter to continue."

  Console_Screen.pause       #Pause the game

end

This method displays a series of text strings that provide the player with instructions for playing the game.

Step 7: Defining the select_word Method

The next method to be added to the Game class is the select_word method. This method’s script statements are shown next and should be added to the end of the class definition, immediately after the display_instructions method.

#Define a method that generates the secret word
def select_word

  #Define an array of 20 words from which the game will randomly select
  words = ["W I N D O W", "S T A T I O N", "H A M B U R G E R",
           "E X P R E S S I O N", "W A L L E T", "C A M E R A",
           "A I R P L A N E", "C A N D L E", "C O M P U T E R",
           "P I C T U R E", "F R A M E", "S H E L F", "B O W L I N G",
           "P O L I T E", "S T A T E M E N T", "N E G A T I V E",
           "M E T H O D", "F I S H I N G", "C O M P E N S A T E",
           "H A P P Y"]

  #Generate and return a random number between 0 and 19
  randomNo = rand(19)

  #Return a randomly selected word to the calling statement
  return words[randomNo]

end

This method begins by defining an array named words and populates the array with 20 text strings representing game words. Spaces have been added between each letter in each word to facilitate the eventual splitting up of the characters that make up each word into an array later in the script file.

Next, a random number between 0 and 19 is generated. The final statement in the method returns a word extracted from the words array back to the statement that called upon the method to execute.

Step 8: Defining the get_consonants Method

The code statements for the get_consonants method are shown next and should be added to the end of the class definition, immediately after the select_word method. This method is responsible for prompting the player to provide a list of five consonants, which the game will use to disclose matching letters in its secret word prior to prompting the player to try to guess it.

#Define a method that collects the player's consonant guesses
def get_consonants

  list = Array.new  #define an array in which to store the consonants

  #Give the player an idea of what is coming
  puts "Before you try to guess the secret word, you must specify " +
       "5 consonants.

"
  print "Press Enter to continue."

  Console_Screen.pause      #Pause the game

  5.times do  #Iterate 5 times


     Console_Screen.cls      #Clear the display area

     #Prompt the player to enter a consonant
     print "
Please enter a consonant and press Enter: "

     input = STDIN.gets  #Collect the player's input
     input.chop!         #Remove the end of line marker

     #Only accept consonant characters
     if input !~ /[bcdfghjklmnpqrstvwxyz]/i then
       Console_Screen.cls       #Clear the display area
      print "Error: " + input + " is not a consonant. Press Enter to " +
      "continue."
      Console_Screen.pause      #Pause the game
      redo  #Repeat the current execution of the loop
    end

    #Only accept one character of input per guess
    if input.length > 1 then # !~ /./ then
      Console_Screen.cls       #Clear the display area
      print "Error: You may only enter one character at a time. Press " +
      "Enter to continue."
      Console_Screen.pause      #Pause the game
      redo  #Repeat the current execution of the loop
    end

    #Do not allow the player to submit the same guess twice
    if list.include?(input.upcase) == true then
      Console_Screen.cls       #Clear the display area
      print "Error: You have already guessed " + input + ". Press " +
      "Enter to continue."
      Console_Screen.pause      #Pause the game
      redo  #Repeat the current execution of the loop
    else
      list.push(input.upcase)  #Convert the consonant to uppercase and
    end                        #add it to the list of consonants

  end

  return list  #Return the list of consonants to the calling statement

end

This method begins by defining a new array named list, which will be used to store a list of consonants supplied by the player. Next, a message is displayed that explains to the player that she is about to be prompted to provide five consonants. A loop is then set up that executes five times (once for each consonant that is collected).

Within the loop, the player is prompted to enter a constant. The user’s input is then validated in a series of three conditional checks. The first conditional check sets up a regular expression that performs a case-insensitive comparison of the player’s input against a list of all the consonants in the alphabet. The player’s input is rejected if it does not match one of the consonants listed in the regular expression.

The second conditional check uses the String class’s length method to determine whether the player entered more than one character and rejects the input if she did. The third conditional check uses the String class’s include? method to determine if the player’s input (converted to uppercase) has already been submitted (e.g., has been added to the list array) and rejects it if this is the case. However, if the player’s input is valid at the end of the third conditional check, it is added to the end of the list array using the push method (after being converted to all uppercase).

Hint

Hint

There are two important points to note in the previous set of conditional checks. First, any time one of the conditional checks rejects the player’s input, the redo command is executed. As a result, the current execution of the loop runs again, preventing the loop from iterating. Second, only valid input that has been converted to uppercase is added to the list array. The contents of this array will be used later in the script to disclose any matching letters in the game’s secret word, thus helping the player to figure it out.

The last statement in the get_consonants method returns a copy of the contents of the list array to the statement that invoked the method.

Step 9: Defining the get_vowel Method

The code statements for the get_vowel method are shown next and should be added to the end of the class definition, immediately after the get_consonants method. This method is responsible for prompting the player to provide a vowel, which the game will use to disclose matching letters in its secret word prior to prompting the player to try to guess it.

#Define a method that collects the player's vowel guess
def get_vowel

  #Give the player an idea of what is coming
  puts "Before you try to guess the secret word, you must specify " +
  "1 vowel.

"

  1.times do  #Iterate 1 time

    Console_Screen.cls       #Clear the display area
    #Prompt the player to enter a vowel
    print "
Please enter a vowel and press Enter: "
    input = STDIN.gets  #Collect the player's input
    input.chop!         #Remove the end of line marker

    #Only accept vowel characters
    if input !~ /[aeiou]/i then
      Console_Screen.cls       #Clear the display area
      print "Error: " + input + " is not a vowel. Press Enter to " +
      "continue."
      Console_Screen.pause      #Pause the game
      redo  #Repeat the current execution of the loop
    end

    #Only accept one character of input per guess
    if input.length > 1 then # !~ /./ then
      Console_Screen.cls       #Clear the display area
      print "Error: You may only enter one character at a time. Press " +
      "Enter to continue."
      Console_Screen.pause      #Pause the game
      redo
    end

    input = input.upcase  #Convert the vowel to uppercase
    return input  #Return the vowel to the calling statement

  end

end

As you can see, the program statements that make up this method are very similar to those in the previous method, except only two conditional validation checks are performed on the user’s input and the user’s input is returned to the calling statement as a individual value and not as a list.

Step 10: Defining the prompt_for_guess Method

The code statements for the prompt_for_guess method are shown next and should be added to the end of the class definition, immediately after the get_vowel method. This method is responsible for formatting the display of the secret word and then managing the game’s interaction with the player as the player attempts to guess the word.

#Define a method that collects player guesses
def prompt_for_guess(shortWord, word, consonants, vowel)

   Console_Screen.cls       #Clear the display area

   consonants.push(vowel)  #To make things easy, add the vowel to the
                           #list of consonants

   wordArray = word.split(" ") #Split the secret word into an array

   i = 0  #Initial the variable with a starting value of zero

   #Loop once for each letter in the word (stored in an array)
   wordArray.each do |letter|

     match = false  #Initial the variable with a starting value of false

     #Loop once for each consonant stored in the consonants array
     consonants.each do |character|

       #Compare the current character from the consonants array to the
       #current letters in the wordArray array
       if character == letter then
         match = true  #Set variable value to indicate a match
         break  #Terminate loop execution when a match occurs
       end

end

#If there is no matching character in the consonants array for the
#current letter in the wordArray array, replace that letter in the
#wordArray with an underscore character
if match == false then
  wordArray[i] = "_"  #Replace the current character with an
end                   #underscore
  match = false  #Reset the value of the match variable

  i = i + 1  #Increment the variable's value by 1

end

#Once the contents of the array have been formatted with underscores,
#convert the contents of the array back into a word
word = wordArray.join(" ")

#Allow the player up to three guesses
3.times do |i|  #i equals 0 on the first iteration of the loop

  Console_Screen.cls       #Clear the display area

  #Prompt the player to try to guess the secret word
  puts "I am thinking of a word.





"
  print "Here is your clue: " + word + "







"
  print "What do you think this word is? "
  reply = STDIN.gets  #Collect the player's reply
  reply.chop!         #Remove the end of line marker
  reply = reply.upcase  #Convert the reply to all uppercase

  #Analyze the player's guess
  if reply == shortWord then  #The player guessed the secret word

    Console_Screen.cls       #Clear the display area
    print "Correct! Press Enter to continue."
    Console_Screen.pause       #Pause the game
    break  #Terminate the execution of the loop

  else  #The player did not guess the secret word

    Console_Screen.cls       #Clear the display area

    #Display a message based on how many turns remain
    if i == 1 then
      print "Wrong! You have one guess left. Press Enter to " +
        "try again."
      elsif i == 2
        print "Sorry, you lose.

"
        print "The word was " + shortWord + ". Press Enter to continue."
      else
        print "Wrong! Press Enter to try again."
      end

      Console_Screen.pause       #Pause the game

    end

  end

end

The prompt_for_guess method processes four arguments. shortWord is a copy of the game’s secret word with no spaces in it. word is a copy of the game’s secret word with spaces inserted between each letter. consonants is a list of five consonants previously supplied by the player, and vowel is a string representing the vowel specified by the player when the get_vowel method was executed.

The prompt_for_guess method begins by adding vowel to the consonants array. This is done to simplify things by grouping all of the player’s input into a single array, making the data easy to process. Next a new array named wordArray is created and assigned a list of letters extracted from word.

Hint

Hint

In order to extract each letter from the string stored in word and assign it as an item in the wordArray array, the String class’s split method was used. This method splits the contents of string into an array using a specified delimiter. In the case of the word variable, the delimiter was the blank space located between each letter.

Next, a value of zero is assigned to a variable named i. This variable will be used later in the method to keep track of which item (letter) is being examined in the wordArray array when it is being processed by a loop and compared to each of the six letters provided by the player (five consonants and a vowel).

A loop is then set up that loops though each item stored in the wordArray array. Within this loop a second loop has been set up that loops through each item stored in the consonants array. The inner loop compares the currently selected item (letter) from the wordArray array against each of the items (letters) in the consonants array. If a match is found, the inner loop is terminated using the break command and a value of true is assigned to a variable named match.

Next, a conditional statement is executed that replaces the current character in the wordArray array with an underscore if no match was found in the consonants array. The value of match is then set back to false and the value of i is incremented by one. Once every item in wordArray has been compared to every item in consonants, the contents of wordArray are converted back into a string again using the Array class’s join method.

Hint

Hint

The join method takes as an argument a delimiter that is then used to pad array items to create a string. In the previous statement, the join method is passed a single blank space.

Next, a loop is set up that provides the player with three chances to guess the game’s secret word. Each time the loop iterates, it displays a copy of the secret word. Any letters in the word that match up against the consonants and vowel that the player specified are revealed and all other letters are hidden (represented by underscore characters). The player is then prompted to guess the secret word.

The player’s guess is converted to all uppercase characters and compared against the value of shortWord to see if there is a match, in which case the player has successfully guessed the word. If this happens, the break command is executed, terminating the execution of the loop. Otherwise, the player is informed of her error and given another chance to make a guess. After using up all three chances without correctly guessing the secret word, the loop terminates.

Hint

Hint

Because Ruby is a case-sensitive programming language, it views upper-and lowercase letters as being different from one another. To keep things simple, the game converts all letters to uppercase, eliminating any concerns about comparing one string against another.

Step 11: Defining the play_game Method

The next method to be added to the Game class is the play_game method. The statements belonging to this method are shown next and should be added to the end of the class definition, immediately after the prompt_for_guess method.

#Define a method to control game play
def play_game
word = select_word  #Call on the method that retrieves a random word

Console_Screen.cls       #Clear the display area

consonants = get_consonants #Call on the method that prompts the player
                            #to enter a list of consonants

Console_Screen.cls       #Clear the display area

#Call on the method that prompts the player to enter a vowel
vowel = get_vowel

#Remove blank spaces from the word to create a short version of the word
shortWord = word.gsub(" ", "")

#Call the method that processes player guesses
prompt_for_guess(shortWord, word, consonants, vowel)

Console_Screen.cls       #Clear the display area

end

This method begins by calling on the select_word method to retrieve a word for the player to guess. Next, the get_consonants method is called to retrieve a list of five consonants, and the get_vowel method is called to prompt the player to identify a vowel. Next, the String class’s gsub method is used to generate a short version of the secret word (without spaces) and the prompt_for_guess method is executed. This method is passed shortWord, word, consonants, and vowel as arguments and uses these objects to prompt the player to try to guess the secret word.

Step 12: Defining the display_credits Method

The last method to be added to the Game class is the display_credits method. This method displays the game’s credits, including the author’s URL. The statements that make up this method are shown next and should be appended to the end of the Game class.

#This method displays the information about the Word Guessing game
def display_credits

  Console_Screen.cls  #Clear the display area
  #Thank the player and display game information
  puts "		     Thank you for playing the Word Guessing Game.



"
  puts "
			 Developed by Jerry Lee Ford, Jr.

"
  puts "				  Copyright 2008

"
  puts "			URL: http://www.tech-publishing.com









"

end

Step 13: Initializing Script Objects

Now it is time to initialize instances of the Screen and the Game classes. This is done by appending the following statements to the end of the script file.

# Main Script Logic -------------------------------------------------------

Console_Screen = Screen.new  #Instantiate a new Screen object
WordGuess = Game.new         #Instantiate a new Game object

#Execute the Game class's display_greeting method
WordGuess.display_greeting

answer = ""  #Initialize variable and assign it an empty string

In addition to instantiating the Console_Screen and SQ objects, these statements define a variable named answer, which will be used to control the execution of the loop that prompts the player for permission to begin a new round of play.

Step 14: Getting Permission to Start the Game

The script statements responsible for prompting the player for permission to start a new round of play are outlined here:

#Loop until the player enters y or n and do not accept any other input
loop do

  Console_Screen.cls  #Clear the display area

  #Prompt the player for permission to start the quiz
  print "Are you ready to play the Word Guessing Game? (y/n): "

  answer = STDIN.gets  #Collect the player's answer
  answer.chop!  #Remove any extra characters appended to the string
  #Terminate the loop if valid input was provided
  break if answer =~ /y|n/i

end

These statements, which should be added to the end of the script file, are controlled by a loop that has been set up to run forever. Each time the loop iterates, the player is prompted to enter a value of y or n, to tell the game whether a new round of play should be initiated or the game should be terminated. Any input other than a y or n is ignored. As soon as valid input has been provided, the break command is executed and the loop terminates, allowing the rest of the script to execute.

Step 15: Controlling Game Play

The rest of the statements that make up the Word Guessing game are shown next and should be added to the end of the script file. These statements are responsible for controlling the overall execution of the game.

#Analyze the player's input
if answer == "n"  #See if the player elected not to take the quiz

  Console_Screen.cls  #Clear the display area

  #Invite the player to return and take the quiz some other time
  puts "Okay, perhaps another time.

"

else  #The player wants to play the game

    #Execute the game class's display_instructions method
    WordGuess.display_instructions

  loop do  #Loop forever

    #Execute the Game class's play_game method
    WordGuess.play_game

    #Find out if the player wants to play another round
    print "Would you like to play again? (y/n): "
    playAgain = STDIN.gets  #Collect the player's response
    playAgain.chop!  #Remove any extra characters appended to the string

    #Terminate the loop if valid input was provided
    break if playAgain =~ /n/i

  end

  #Call upon the Game class's determine_credits method
  WordGuess.display_credits

end

As you can see, these statements are controlled by a large if code block. The script statements that it executes depend on whether the player decides to terminate the game or play another round. If the player elects not to play, a message is displayed that encourages her to return and play another time. If the player elects to play, the Game class’s display_instructions method is executed. Next, a loop executes the Game class’s play_game method, initiating a new round of play. Once the current round of play has finished, control returns to the loop, which prompts the player to play again. If the player decides to play again, the loop iterates. Otherwise the break command is executed, terminating the loop and allowing the display_credits method to execute.

Running Your New Ruby Script Game

Okay, you now have everything needed to create and execute the Word Guessing game. Assuming that you did not make any typos along the way and that you did not accidentally skip any steps, the script should run as described at the beginning of this chapter. If, however, you should run into an error or two, be sure to carefully read the text of the error messages to get an idea of what and where the problems reside. If all else fails, go back and review the script and look for typos and missing script statements.

Summary

In this chapter, you learned how to use regular expressions as a tool for analyzing data. You learned how to create regular expressions that can match simple character patterns and groups of characters. You learned how to incorporate metacharacters to create more streamlined and powerful regular expressions. You also learned how to use regular expressions to extract data from a string and to perform string substitution. Finally, you learned how to use regular expressions to negate differences in case.

Now, before you move on to Chapter 8, “Object-Oriented Programming,” I suggest you set aside a little extra time to make a few improvements to the Word Guessing game by implementing the following list of challenges.

..................Content has been hidden....................

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