Chapter 9. File and Folder Administration

In this chapter, you will learn the ins and outs of how to use Ruby to develop scripts that interact with the computer’s file system. In doing so, you will learn how to develop scripts that can create, rename, and delete files and folders. You will learn how to determine a file’s size and to iterate through a folder’s contents. You will also learn how to write data to text files. This will include learning how to write data to new files as well as how to overwrite or append data to existing text files. Finally, you will learn how to read and process data stored in files. On top of all this, you will learn how to create a new Ruby script, the Ruby Blackjack game.

Specifically, you will learn how to:

  • Create and delete text files

  • Read from and write to text files

  • Create and delete folders

  • Examine and process folder contents

Project Preview: The Ruby Blackjack Game

In this chapter, you will learn how to create a new Ruby script called the Ruby Blackjack game. This game will create a virtualized casino blackjack dealer against whom the player will compete in an effort to build a hand that comes as close as possible to 21 without going over. As shown in Figure 9.1, the game begins by displaying a welcome message.

The Ruby Blackjack game’s opening welcome message.

Figure 9.1. The Ruby Blackjack game’s opening welcome message.

The player is then prompted for permission to begin play, as shown in Figure 9.2.

The player must respond with a y to begin the game.

Figure 9.2. The player must respond with a y to begin the game.

Once permission has been given, instructions for playing the game are displayed, as shown in Figure 9.3.

The object of the game is to get a hand that is as close as possible to 21 without going over.

Figure 9.3. The object of the game is to get a hand that is as close as possible to 21 without going over.

Both the player and the dealer are dealt an initial card, after which the player is prompted to play out the rest of her hand, as demonstrated in Figure 9.4.

Once initial cards are dealt, the player always goes first.

Figure 9.4. Once initial cards are dealt, the player always goes first.

The player may ask for as many additional cards as she wants, as long as the total value of her hand does not exceed 21. If this happens, the player busts and loses the hand, as demonstrated in Figure 9.5.

The player’s hand has exceeded a total of 21, resulting in a loss.

Figure 9.5. The player’s hand has exceeded a total of 21, resulting in a loss.

After each round of play, the player is prompted for permission to start a new hand, as shown in Figure 9.6.

The player may play as many hands as she wants.

Figure 9.6. The player may play as many hands as she wants.

Figure 9.7 shows an example of a game in which the player has won. Here the player’s hand is a perfect 21.

The player has won this hand.

Figure 9.7. The player has won this hand.

Once the player has decided to stop playing, the game ends after displaying the screen shown in Figure 9.8.

The game’s final screen displays information about the game and its author.

Figure 9.8. The game’s final screen displays information about the game and its author.

Understanding File Input and Output

The movement of data from one source to another on a computer is commonly referred to as data input and output. The actual movement of the data occurs as a stream between the sending and the receiving resources. By default, the computer looks to the keyboard for standard input or STDIN and sends standard output or STDOUT to the computer’s monitor.

You can redirect STDIN and STDOUT as necessary to pull data from a different input source and to reroute data to a different output destination. Redirection is accomplished using pipe operators. The > pipe operator allows you to pass the output from one source, such as a Ruby script, to another source, like a text file. For example, suppose you created a Ruby script named Hello.rb that contained the following statement.

puts "Hello World!"

When executed from the command line, the words Hello Word! are displayed on the computer screen. However, using the > pipe operator, you could instead redirect the script’s output to a text file, as demonstrated here:

ruby Hello.rb > Hello.txt

When the Hello.rb script is executed this time, its output is redirected to a file named Hello.txt located in the current working directory (i.e., the same directory where the script resides). If the Hello.txt file does not exist, it is created and then written to. If a file of the same name already exists, its contents are overwritten.

In a similar fashion, you can use the < pipe operator to redirect standard input from one resource to another. For example, suppose you had a Ruby script named DispMsg.rb that consisted of the following statement.

message = STDIN.gets

If you execute this script from the command prompt, the script would immediately pause and wait for input to be provided by the user. Once something is typed and the Enter key is pressed, the script assigns the input to the message variable and then terminates. However, using the < pipe operator, you can instead redirect STDIN from a file source, as demonstrated here:

ruby Dispmsg.rb < Hello.txt

Here, the Dispmsg.rb script reads the first line of text stored in a file named Hello.txt and then assigns it to the message variable.

Hint

Hint

If an empty file were used as input in the previous example, a value of nil (Ruby’s way of representing a value of nothing) would be assigned to the message variable.

Both the > and the < pipe operators are provided by the operating system and can be used as an elementary way of writing to and reading from text files. However, Ruby provides a number of more elegant and sophisticated ways of working with and administering files.

Administering Text Files and Folders

Ruby offers a number of different ways to perform file and folder administration. Ruby provides you with everything you need to create, rename, and delete files and folders. This functionality is provided through the Ruby File and Dir classes, which supply you with access to various file and folder administration methods.

Verifying Whether a File or Folder Exists

Before you begin any file or folder administration task, it is a good idea to first check and see if the file or folder with which you intend to work already exists. Files and folders can disappear from a computer for a host of different reasons. They may, for example, be accidentally deleted or moved to a different location. If after checking for the existence of a folder and finding out that it does not exist, you may want to terminate the script’s execution or you may instead want to create a new folder. The same logic can be applied to files.

To determine if a file or folder exists, you need to use the File class’s exist? method, which supports the following syntax.

File.exist?(Name)

Name represents the name and path of the file or directory being looked for. To get a better feel for how to use the exist? method, look at the following example.

puts "Found it!" if File.exist?("Hello.txt")

Here, a statement has been set up to look for a file named Hello.txt located in the current working directory. If the file is found, a message is displayed. A more realistic example might involve modifying this statement to display a message in the event the file is not found. For example, the following statements provide an example that does something similar to this, only this time the focus of attention is on a folder named TestDir instead of a file.

if File.exist?("TestDir") then

  puts "All is well."

else

  print "The TestDir folder is missing. Enter y to recreate it: "
  answer = STDIN.gets
  answer.chop!

  if answer =~ /y/i then
   Dir.mkdir("TestDir")
  end

end

Here, if the folder is not found, a message is displayed prompting the user for permission to create a new copy of the folder. If the user agrees, the Dir class’s mkdir method is used to create the folder.

Retrieving Information about a File or Folder

The File and Dir classes provide you with access to a number of methods that you can use to get information about files and folders. Using these methods, you can determine whether a specified resource is a file, folder, or something else. You can also determine whether a file is empty or how large it is, and you can retrieve a list of all the files and folders stored in a folder.

Determining if a Resource Is a File or a Folder

Using the File class’s directory? method, you can determine if a resource is a file or something else (file, socket, pipe, etc.). This method supports the following syntax.

File.directory?(Name)

Name represents the name and path of the resource to be checked. To get a better feel for how to work with the directory? method, look at the following example.

if File.directory?("TestDir") then
  puts "It's a folder."
else
  puts "It is something else."
end

Here, the current working directory? method is used to determine whether TestDir is a file or a folder. The directory? method returns a value of true if the specified resource is a folder and value of false if it is not. Based on the results of the analysis, one of two text messages is displayed.

The File class also contains a method named file?, which can be used to determine whether a resource is a file or something else. As you can see from the following syntax, the file? method is very similar to the directory? method.

File.file?(Name)

Name represents the name and path of the resource to be checked. To get a better feel for how to work with the file? method, look at the following example.

if File.file?("Hello.txt") then
  puts "It's a file."
else
  puts "It is something else."
end

Here, the example looks for a file named Hello.txt located in the current working directory.

Checking a File’s Size

Before reading from a file or overwriting an existing file with a new file, you may want to first check to see if the file has anything in it. Based on this analysis, you could avoid trying to read from an empty file or decide to write new data to the end of a file in append mode instead of overwriting any existing data.

To determine if a file has any data in it, you can use the File class’s size method, which has the following syntax. This method returns a count of the specified file’s size in bytes.

File.size(Name)

Name represents the name and path of the resource to be checked. To get a better feel for how to work with the size method, look at the following example.

puts "File Hello.txt is " + File.size("Hello.txt").to_s + " bytes in size."

Here, a text string is displayed that shows the size of a file named Hello.txt located in the current working directory.

In addition to the size method, you can also use the size? method to determine the size of a file. The syntax for this method is shown next. This method returns the size of the file, in bytes. However, if the file is empty, a value of nil is returned instead.

File.size?(Name)

Name represents the name and path of the resource to be checked. To get a better feel for how to work with the size method, look at the following example.

if File.size?("Hello.txt") > 0 then
  puts "Processing file"
else
  puts "File processing skipped"
end

Here, the size? method is used to determine whether a file should be processed. If the size of the file is greater than zero bytes, it is processed. Otherwise it is not processed.

Examining Folder Contents

Ruby gives you the ability to list the contents in a folder using the Dir class’s entries method, which returns the contents of the list as an array. You can then iterate through the array and work with each individual file or folder as necessary. The entries method has the following syntax.

Dir.entries(Name)

Name represents the name and path of the directory to be processed. To get a better feel for how to work with the entries method, look at the following example.

puts Dir.entries(".")

Here, the puts method is used to display the contents of the current working directory, as provided by the entries method. When executed, this statement will display output similar to that shown here.

c:Ruby_scripts>test
.
..
BlackJack.rb
Crazy8Ball.rb
NumberGuess.rb
RPS.rb
RubyJoke.rb
SupermanQuiz.rb
TallTale.rb
Test.rb
TypingChallenge.rb
WordGuess.rb

Hint

Hint

In Ruby, the . character can be used as a shortcut for representing the current working directory.

You can also produce a list of all the files stored in a folder using the Dir class’s foreach method, as demonstrated here:

Dir.foreach(".") do |resource|
  puts resource
end

Here, a loop has been set up that iterates through every file and folder stored in the current working directory. The advantage of using the foreach method in this manner is that it provides you with the ability to take multiple actions against folder contents by allowing you to place as many statements as you want inside the loop.

Creating New Folders

As you will see in a few minutes, Ruby allows you to create new files and write any amount of data to them. Ruby also provides you with the ability to create new folders using the Dir class’s mkdir method, which has the following syntax.

Dir.mkdir(Name)

Name is used to specify the name and path of the folder that you want to create. To get a better feel for how to work with the mkdir method, look at the following example.

Dir.mkdir("TestDir")

When executed, this statement creates a new folder named TestDir. However, if a folder of the same name already exists, an error will occur, so you may want to check to see if a folder of the same name already exists, as shown here:

if File.exist?("TestDir") then
  Dir.mkdir("TestDir")
end

Deleting Files and Folders

Ruby also offers you the ability to delete both files and folders. To do so, you will need to work with the File and Dir class’s delete method. The File class’s delete method has the following syntax.

File.delete(Name,... Name)

The Dir class’s delete method’s syntax is identical, as shown here:

Dir.delete(Name,... Name)

Name represents any number of files or folders that you want to delete. To get a better feel for how to work with the delete method, look at the following example.

Dir.delete("TestDir")

Here, a folder named TestDir is deleted if it exists.

Trap

Trap

If you attempt to delete a file or folder that does not exist, or if you try to delete a folder that is not empty, an error will occur.

Renaming Files

Ruby also provides you with the ability to rename a file or folder using the File class’s rename method, which has the following syntax.

File.rename(OldName, NewName)

OldName represents the current name of the file to be renamed and NewName represents the new file name that is to be assigned to the file. To get a better feel of how to work with the rename method, look at the following example.

File.rename("Hello.txt", "Greeting.txt")

This example renames a file named Hello.txt to Greeting.txt. If the specified file or folder does not exist, an error will occur.

Working with Files and Folders on Different Operating Systems

All of the examples that you have looked at in this chapter have operated based on the assumption that the example scripts were being executed from the same folder where target files and folders resided (e.g., the current working directory). Of course, this will not always be the case. As a result, you need to be able to specify the path to the files and directories you want to work with.

On Microsoft Windows, you can include paths in your script statements, as demonstrated here:

puts File.exists?('C:Test_FilesHello.txt')

Trap

Trap

The reason that single quotes were used in this example is because they prevent any character interpolation from occurring. As a result, the characters are taken literally and everything works just fine. If you wanted to work with double quotes instead, you would have to escape each instance of the character to get expected results, as demonstrated here:

puts File.exists?("c:\Ruby_Scripts\xxx.txt")

Here, the File class’s exists? method has been instructed to look in the C:Test_Files folder for a file named Hello.txt.

If instead of Microsoft Windows, you were working on a computer running UNIX or Linux, you could rewrite the previous example as shown here:

puts File.exists?('/Test_Files/Hello.txt')

Here, the File class’s exists? method has been instructed to look in a folder named Test_Files, which is located at the root of the computer file systems, for a file named Hello.txt.

Trap

Trap

Note that while Microsoft Windows uses backslashes when specifying path information, UNIX and Linux use forward slashes.

If you need to develop scripts that will run on different operating systems, then you will need a way of determining which type of operating system your script is executing on. One way of addressing this challenge is to take advantage of Ruby’s RUBY_PLATFORM special variable, as demonstrated here:

if RUBY_PLATFORM =~ /win32/ then
  puts File.exists?('C:Test_FilesHello.txt')
else
  puts File.exists?('/Test_Files/Hello.txt')
end

Here, the value assigned to RUBY_PLATFORM is checked to see if it contains the characters win32. If it does, the script is executing on a Windows computer. Otherwise, it is assumed that the script is executing on a computer running some flavor of UNIX or Linux.

Hint

Hint

RUBY_PLATFORM is a special Ruby variable. A special variable is a variable that is automatically created and maintained by Ruby and which can be referenced by any Ruby scripts. RUBY_PLATFORM contains a string that identifies the name of the operating system on which a script is executing. Using the regular expression shown in the previous example, you can easily distinguish between a Windows and a non-Windows computer.

Reading from and Writing to Text Files

In addition to taking advantage of the operating system’s ability to pipe data to and from text files using the < and > pipe operators, Ruby offers a number of different options for writing data to and reading it from files using methods belonging to the File class.

One way of interacting with files is to use the File class’s new method to set up a reference to it. Once this reference has been established, you can refer to the file as necessary to perform read and write operations. One way to set up a file reference is to use the syntax outlined here:

Reference = File.new(Name, Mode)

Reference is a placeholder for a variable that will be used to refer back to the target file. Name represents the file that you want to interact with and Mode represents one of the options listed in Table 9.1 that specify the mode in which you want the file opened.

Table 9.1. File Class Mode Specifications

Mode

Description

r

Opens the file in read-only mode, placing the location pointer at the beginning of the file.

r+

Opens the file for both reading and writing, placing the location pointer at the beginning of the file.

w

Opens the file in write-only mode, overwriting any existing text by placing the pointer at the beginning of the file. If the specified file does not exist, it is created.

w+

Opens the file for both reading and writing, overwriting any existing text by placing the pointer at the beginning of the file. If the specified file does not exist, it is created.

a

Opens the file in append mode, placing the pointer at the end of the file to preserve any pre-existing text.

a+

Opens the file in append mode, allowing for both reading and writing, placing the pointer at the end of the file to preserve any pre-existing text.

Writing Data to Text Files

One way of writing data to a text file is to use the File class’s new method and to specify a write mode operation, as demonstrated here:

outFile = File.new("Demo.txt", "w")
  outFile.puts "Ho Ho Ho"
  outFile.puts "Merry Christmas!"
outFile.close

In this example, a file reference has been set up to open a file named Demo.txt using write mode. Once established, the file reference (outFile) can be used to write data to the file using the puts method. In this example, two lines of text were written to the file. If the target file does not exist, it is created and then written to. If, however, it does exist, it is opened and then overwritten. Take special note of the last statement in the example. This is a requirement for any file that is opened by creating a file reference. Failure to explicitly close any open file reference may result in the corruption of the file.

Hint

Hint

Up to this point in the book, you have used the puts method exclusively for the purpose of displaying text on the computer screen (e.g., default STDOUT). However, in this example, by pre-appending the file reference to the puts method using dot notation, you have redirected the puts method’s output to the specified file.

Figure 9.9 shows the contents of the text file that is created when the example is executed.

A text file programmatically created and written to by a Ruby script.

Figure 9.9. A text file programmatically created and written to by a Ruby script.

Appending Data to the Ends of Text Files

Appending data to the end of a file is very similar to writing it except that in append mode, any data already written to the file is preserved. This makes append mode the appropriate option to use when adding to the end of text files, as demonstrated here:

outFile = File.new("Demo.txt", "a")
  outFile.puts "And a happy new year!"
outFile.close

In this example, the Demo.txt file is reopened and an additional line of text is written to it before it is again closed. Figure 9.10 shows how the contents of the text file have been modified once the example has executed.

Examining the contents of the text file once additional data has been appended to it.

Figure 9.10. Examining the contents of the text file once additional data has been appended to it.

Reading Data from Text Files

Reading data stored in text files is no more difficult than writing to text files. For starters, the file must be opened in read mode. You can then read data from the text file, as demonstrated here:

File.new("Demo.txt", "r").each do |line|
 puts line
end

In this example, the Demo.txt file has been opened for reading using the File class’s new method. Next, the each method is used to iterate through and display each line of text that is in the file. When executed, the following output is displayed.

Ho Ho Ho
Merry Christmas!
And a happy new year!

In addition to processing the contents of a text file using a loop, you can also use the gets method to retrieve data from the file a line at a time, as demonstrated here:

inputFile = File.new("Demo.txt", "r")
puts inputFile.gets
inputFile.close

Here, the Demo.txt file has been opened in read mode. Next, the gets method is used to read the first line of the file, which is then displayed by the puts method. The last statement closes the open file. When executed, these statements generate the following output.

Ho Ho Ho

As you can see, working with a file reference when reading a file is pretty straightforward. However, every time you open a file to read it, you must remember to close the file to prevent the file from becoming corrupt. To help make things even easier on you, Ruby provides a couple of quick and easy shortcut methods that you can use to read file contents without having to worry about closing the files when you are done. These methods include the read and readlines methods.

To use the read method, all you have to do is pass the method the name of the file that you want read, as demonstrated here:

inputFile = File.read("Demo.txt")

In this example, the Demo.txt file is opened and read and all of its contents are stored in a variable named inputFile. You can then process the data stored in inputFile as you see fit. For example, you might manipulate it using regular expression or simply display it as demonstrated here:

puts inputFile

When executed, this statement displays the data stored in the inputFile variable, as shown here:

Ho Ho Ho
Merry Christmas!
And a happy new year!

The readlines method is similar to the read method, only instead of reading the contents of a file into a single variable, the file’s contents are read line by line into an array, allowing you to reference and manipulate them using any of the Array class’s methods. For example, the following statement uses the readlines method to read the Demo.txt file and store its contents in an array named inputArray.

inputArray = File.readlines("Demo.txt")

Once loaded into the array, you can process the array’s contents as you see fit. For example, the following statements could be used to loop through the array and print out its content, one item at a time.

inputArray.each do |line|
  puts line
end

Trick

Trick

In addition to opening a file for reading using the File class’s new method, you can also use the File class’s open method. The advantage of using the open method over the new method is that the open method does not require you to close the file once it has been read. You can use the open method in conjunction with other methods, including the each, read, and readlines methods. For example, the following statement uses the open method in conjunction with the readlines method to read all of the lines stored in Demo.txt and store them in an array named inputArray.

inputArray = File.open("Demo.txt").readlines

The File class’s open method can also be used for write operations, as demonstrated here:

File.open("Demo.txt", "a") do |output|
  output.puts "
The End"
end

Here, the open method is used to open the Demo.txt file in append mode and then to write a single line of text to the end of the file. Once the write operation is completed, the file is automatically closed. The contents of the text file are modified, as shown here:

Ho Ho Ho
Merry Christmas!
And a happy new year!


The End

Back to the Ruby Blackjack Game

Okay, now it is time to turn your attention back to the development of this chapter’s game project, the Ruby Blackjack game. As with all previous games, this script will be developed in a modular fashion. As you work your way through this script, take particular note of the manner in which script variables are kept localized and how programming logic is kept separate and organized into distinct methods.

Designing the Game

The development of the Ruby Blackjack 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 Blackjack game.

  5. Define the display_greeting method.

  6. Define the display_instructions method.

  7. Define the play_game method.

  8. Define the get_new_card method.

  9. Define the complete_player_hand method.

  10. Define the play_dealer_hand method.

  11. Define the determine_winner method.

  12. Define the display_credits method.

  13. Instantiate custom script objects.

  14. Get confirmation before continuing game play.

  15. Control high-level game play.

Remember to follow along carefully and make sure that you do not skip any steps as you work your way through this script.

Step 1: Creating a New Ruby File

Let’s begin the creation of the Ruby Blackjack game by starting up your favorite text or script editor and creating a new Ruby script file. Save this file with a file name of Blackjack.rb and store it in the same folder as the rest of your Ruby script files.

Step 2: Documenting the Script and Its Purpose

Okay, now let’s add the usual list of comment statements to the beginning of the script file to document the script file and explain what it does. To do this, add the following statements to the end of the script file.

#--------------------------------------------------------------------------
#
# Script Name: BlackJack.rb
# Version:     1.0
# Author:      Jerry Lee Ford, Jr.
# Date:        October 2007
#
# Description: This Ruby game is a virtualized casino card game in which
#              the player competes against the dealer (computer) in an
#              effort to build a hand that comes as close as possible to 21
#              without going over.
#
#--------------------------------------------------------------------------

Step 3: Defining a Screen Class

The Ruby Blackjack game will make use of two custom classes, which will provide you with access to collections of methods required to control user interaction and the overall execution of the game. The code statements for the script’s first class, the Screen class, are shown next and should be added to the end of the script file.

#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 defines two methods. The cls method writes 25 blank lines to the console window and then plays a beep sound. The pause method pauses script execution whenever it is called and waits for the player to press the Enter key.

Step 4: Creating the Game Class

The Game class contains eight methods, which provide you with control over the game’s execution. To begin the creation of the Game class, append the following statements to the end of the script file.

#Define a class representing the Ruby Blackjack game
class Game

end

Step 5: Defining the display_greeting Method

The first method defined in the Game class is display_greeting. It is responsible for displaying the game’s welcome message. The statements that make up this method are shown next and should be inserted inside 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 a welcome message
  print "			Welcome to the Ruby Blackjack Game!" +
  "













Press Enter to " +
             "continue. "

  Console_Screen.pause     #Pause the game

end

Step 6: Defining the display_instructions Method

The next method defined in the Game class is the display_instructions method. This method displays the game instructions using a series of text strings. The statements that make up this method are shown next and should be added to the Game class definition, immediately after the display_greeting method.

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

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

"  #Display a heading

  #Display the game's instructions
  puts "This game is based on the Blackjack card game. In this"
  puts "game the player and dealer are each dealt an initial card. The"
  puts "player is then prompted to draw additional cards. The player"
  puts "may draw as many additional cards as desired, as long as the"
  puts "player's hand remains less than 21. If the player's hand goes"
  puts "over 21, the player busts and the dealer automatically"
  puts "wins. Once the player decides to hold, it becomes the dealer's"
  puts "turn. The dealer will continue to add new cards to its hand"
  puts "until the value exceeds 17 or the dealer busts. Once the"
  puts "dealer's hand is complete, the game analyzes the player's hand"
  puts "and the dealer's hand to determine the results of the game."
  puts "






"
  print "Press Enter to continue. "
  Console_Screen.pause      #Pause the game

end

Step 7: Defining the play_game Method

The next method defined in the Game class is the play_game method, which is responsible for managing an individual round of play. The statements that make up this method are shown next and should be added to the end of the class definition, immediately after the display_instructions method.

#Define a method to control game play
def play_game

  Console_Screen.cls       #Clear the display area

  #Assist the player and dealer with an initial starting card
  playerHand = get_new_card
  dealerHand = get_new_card

  #Call the method responsible for dealing new cards to the player
  playerHand = complete_player_hand(playerHand, dealerHand)

  #If the player has not busted, call the method responsible for managing
  #dealer's hand
  if playerHand <= 21 then
    dealerHand = play_dealer_hand(dealerHand)
  end

  #call the method responsible for determining the results of the game
  determine_winner(playerHand, dealerHand)

end

This method begins by calling on the get_new_card method two times to assign an initial card to both the player’s and dealer’s opening hand. Since the player always goes before the dealer, the complete_player_hand method is called next and is passed the value of the player’s and dealer’s hand as an argument. The complete_player_hand method is responsible for adding new cards to the player’s hand until the player busts or decides to stick with the cards currently in her hand, after which it returns a value representing the current value of the player’s hand. This value is then examined to see if it exceeds 21, in which case the player has gone bust. If the player has not gone bust, the play_dealer_hand method is called and passed the current value of the dealer’s hand as an argument. The play_dealer_hand method is responsible for playing out the dealer’s hand and then returning the result of that hand. The last statement in the player_game method calls up on the determine_winner method, passing it the current value of the player’s and dealer’s hands. The determine_winner method analyzes these two arguments to determine the result of the game.

Step 8: Defining the get_new_card Method

The next method to be added to the Game class is the get_new_card method, whose statements are shown here:

#Define a method responsible for dealing a new card def
get_new_card

  #Assign a random number from 1 to 13 as the value of the card being
  #created
  card = 1 + rand(13)

  #A value of 1 is an ace, so reassign the card a value of 11
  return 11 if card == 1

  #A value of 10 or more equals a face card so reassign the card a value
  #of 10
  return 10 if card >= 10

  return card  #Return the value assigned to the new card

end

When called, this method generates a random number from 1 to 13, which it assigns to a variable named card. If the value of card is set equal to 1, the card is considered to be an ace. As such, the value of card is reassigned a value of 11. On the other hand, if the value of card is greater than or equal to 10, it is assumed that the card is either a 10 or a face card (Jack, Queen, or King) and as such the value of card is set equal to 10. Once the value assigned to card is finally established, it is returned back to the statement that called upon the method to execute.

Step 9: Defining the complete_player_hand Method

The next method defined in the Game class is the complete_player_hand method, which is responsible for assisting the player in completing her hand. The statements that make up this method are shown next and should be added to the end of the class definition, immediately after the get_new_card method.

#Define a method responsible for dealing the rest of the player's hand
def complete_player_hand(playerHand, dealerHand)

  loop do  #Loop forever

    Console_Screen.cls  #Clear the display area

    #Show the current state of the player's and dealer's hands
    puts "Player's hand: " + playerHand.to_s + "

"
    puts "Dealer's hand: " + dealerHand.to_s + "





"
    print "Would you like another card? (Y/N) "

    reply = STDIN.gets  #Collect the player's answer
    reply.chop!  #Remove any extra characters appended to the string

    #See if the player decided to ask for another card
    if reply =~ /y/i then
      #Call method responsible for getting a new card and add it to the
      #player's hand
      playerHand = playerHand + get_new_card
    end

    #See if the player has decided to stick with the current hand
    if reply =~ /n/i then
      break  #Terminate the execution of the loop
    end

    if playerHand > 21 then
      break  #Terminate the execution of the loop
    end

  end

  #Return the value of the player's hand
  return playerHand

end

This method is passed two arguments, playerHand and dealerHand, which represent the current value of the player’s and dealer’s hands. The value of both hands is displayed and the player is then asked if she would like another card. If the player elects to add another card to her hand, the value assigned to playerHand is incremented, adding the result returned by the get_new_card method to the value of playerHand. The player may add as many cards as desired to her hand, provided that the total value of her hand does not exceed 21. Once the player busts or decides not to draw more cards, the method ends by returning the current value of the player’s hand back to the statement that called upon the method to execute.

Step 10: Defining the play_dealer_hand Method

The next method defined in the Game class is the play_dealer_hand method, which is responsible for completing the dealer’s hand. The statements that make up this method are shown next and should be added to the end of the class definition, immediately after the complete_player_hand method.

#Define a method responsible for managing the dealer's hand
def play_dealer_hand(dealerHand)

  loop do  #Loop forever

    #If the value of the dealer's hand is less than 17 then give the
    #dealer another card
    if dealerHand < 17 then
      #Call method responsible for getting a new card and add it to the
      #dealer's hand
      dealerHand = dealerHand + get_new_card
    else
      break  #Terminate the execution of the loop
    end

  end

  #Return the value of the dealer's hand
  return dealerHand

end

This method takes as an argument the current value of the dealer’s hand. The method then repeatedly calls upon the get_new_card method, adding new cards to the dealer’s hand until the total value of the dealer’s hands exceeds 17, at which time the method returns the current value of the dealer’s hand to the calling statement and then ends.

Step 11: Defining the determine_winner Method

The next method defined in the Game class is the determine_winner method, which is responsible for determining the results of the game. The statements that make up this method are shown next and should be added to the end of the class definition, immediately after the play_dealer_hand method.

#Define a method responsible for analyzing the player's and dealer's
#hands and determining who won
def determine_winner(playerHand, dealerHand)

  Console_Screen.cls  #Clear the display area

  #Show the value of the player's and dealer's hands
  puts "Player's hand: " + playerHand.to_s + "

"
  puts "Dealer's hand: " + dealerHand.to_s + "





"

  if playerHand > 21 then  #See if the player has busted
    puts "The Player busts!

"
    print "Press Enter to continue."
  else  #See if the player and dealer have tied
    if playerHand == dealerHand then
      puts "Tie!

"
      print "Press Enter to continue."
    end
    #See if the dealer has busted
    if dealerHand > 21 then
        puts "The Dealer busts!

"
        print "Press Enter to continue."
    else
      #See if the player's hand beats the dealer's hand
      if playerHand > dealerHand then
        puts "The Player wins!

"
        print "Press Enter to continue."
      end
      #See if the dealer's hand beats the player's hand
      if playerHand < dealerHand then
        puts "The Dealer wins!

"
        print "Press Enter to continue."
      end
    end
  end

  Console_Screen.pause        #Pause the game

end

This method is passed two arguments, representing the current value of the player’s and dealer’s hands. It then displays both of these values and, using a series of nested if statements, determines the overall result of the game. Based on the analysis, the method displays an appropriate message.

Step 12: Defining the display_credits Method

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

#This method displays information about the Ruby Blackjack game
def display_credits

  Console_Screen.cls  #Clear the display area

  #Thank the player and display game information
  puts "		     Thank you for playing the Ruby Blackjack game.



"
  puts "
			 Developed by Jerry Lee Ford, Jr.

"
  puts "				  Copyright 2007

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









"

end

Step 13: Initializing Script Objects

Now that both of the script’s custom classes have been defined, it is time to initialize instances of both 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
BJ = Game.new  #Instantiate a new Game object

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

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

In addition to instantiating the Console_Screen and BJ objects, these statements call upon the Game class’s display_greeting method, which is responsible for prompting the player for permission to start the game, and then define a variable named answer, which will be used to manage the execution of a loop.

Step 14: Getting Permission to Start the Game

The following script statements are responsible for prompting the player for permission to begin the game and should be added to the end of the script file.

#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 game
  print "Are you ready to play Ruby Blackjack? (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 are controlled by a loop that runs forever. Upon each iteration of the loop, the player is prompted for permission to start a new round of play. Any input other than a y or n is ignored. Once valid input is provided, a break command is executed, terminating the loop and allowing the rest of the script to execute.

Step 15: Controlling Game Play

The rest of the statements that make up the Ruby Blackjack game are shown next and should be appended to the end of the script file. These statements are responsible for managing the overall execution of the game.

#Analyze the player's answer
if answer == "n"  #See if the player wants to quit

  Console_Screen.cls  #Clear the display area

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

"

else  #The player wants to play the game

  #Execute the Game class's display_instructions method
  BJ.display_instructions

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

  loop do  #Loop forever

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

    loop do  #Loop forever

      Console_Screen.cls  #Clear the display area
      #Find out if the player wants to play another round
      print "Would you like to play another hand? (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|y/i

end

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

end

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

end

These statements are controlled by a large if code block. The script statements that are executed depend on the player’s input. If the player decides not to play the game, a message is displayed encouraging the player to return and play another time. If, on the other hand, the player decides to play, the Game class’s display_instructions method is executed. Next, a loop executes that repeatedly calls upon the Game class’s play_game method. Each time the play_game method finishes executing, control returns to the loop, which prompts the player to play again. The player may play as many times as she wants. Once she decides to stop playing, a 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 you need to create and execute the Ruby Blackjack game. As long as you followed along carefully and kept an eye on your typing, everything should work exactly as expected. If you run into any errors, carefully examine the resulting error message to determine where the problem may reside. If necessary, go back and review the script and look for typos or missing scripts statements.

Summary

In this chapter, you learned how to interact with the computer’s file system. This included learning how to read from and write to text files. In doing so, you learned how to overwrite existing files or append data to the end of them when writing text. You also learned how to read data from text files a line at a time or all at once. This chapter showed you how to rename and delete files and folders as well as how to determine the size of a file and how to iterate through all the items in a folder.

Now, before you move on to Chapter 10, “Debugging,” I suggest you set aside a little extra time to make a few improvements to the Ruby Blackjack 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