Chapter 3. Working with Objects, Strings, and Variables

In the first two chapters of this book, you focused on learning about Ruby’s overall capabilities, how to experiment with Ruby using the irb, and how to create and execute Ruby scripts. Now it is time to begin digging into the language and seeing how things work. In this chapter, you will learn a number of fundamental object-oriented programming techniques, including how to define custom classes, which you can use as a template for instantiating new objects. You will also learn how to create and work with strings and to store and retrieve object data using variables. On top of all this, you will learn how to create a new Ruby script, the Ruby Virtual Crazy 8 Ball game.

Specifically, you will learn how to:

  • Create and work with text strings

  • Assign and retrieve object data using variables

  • Define custom classes and use them to instantiate objects

  • Define class properties and methods

Project Preview: The Ruby Virtual Crazy 8 Ball Game

In this chapter, you will learn how to create a new computer game called the Ruby Virtual Crazy 8 Ball game. In developing this Ruby script, you will learn how to work with text strings, objects, and variables. You will also learn how to generate random numbers, which will be used as the basis for creating a virtual crazy 8 ball game that provides randomly selected answers to player questions.

The game begins by displaying a welcome screen as shown in Figure 3.1. To begin the game, the player must press the Enter key.

The welcome screen for the Virtual Crazy 8 Ball game.

Figure 3.1. The welcome screen for the Virtual Crazy 8 Ball game.

Next, the game prompts the player for permission to play the game, instructing the player to respond with a y or an n, as shown in Figure 3.2.

The game requires that the player provide confirmation before play begins.

Figure 3.2. The game requires that the player provide confirmation before play begins.

If the player responds with anything other than y or n, the game will redisplay the screen shown in Figure 3.2. If the player responds by typing n, the game responds by displaying the screen shown in Figure 3.3.

The game terminates once the player presses the Enter key.

Figure 3.3. The game terminates once the player presses the Enter key.

If, on the other hand, the player responds with a y, the game prompts the player to ask it a question and then press the Enter key (see Figure 3.4).

All questions should be structured to accommodate yes/no-styled answers.

Figure 3.4. All questions should be structured to accommodate yes/no-styled answers.

The game then answers the player’s questions by displaying one of six randomly selected answers, as demonstrated in Figure 3.5.

The game pauses after displaying its answer to give the player time to reflect.

Figure 3.5. The game pauses after displaying its answer to give the player time to reflect.

Once the player dismisses the game’s answer by pressing the Enter key, the game responds by asking the player if she would like to ask a new question, as demonstrated in Figure 3.6.

The player can quit playing at any time by typing q and pressing the Enter key.

Figure 3.6. The player can quit playing at any time by typing q and pressing the Enter key.

The last screen displayed thanks the player for taking time to play the game, as shown in Figure 3.7.

The Virtual Crazy 8 Ball game has ended.

Figure 3.7. The Virtual Crazy 8 Ball game has ended.

Working with Text Strings

As you are no doubt aware by this point in the book, text plays a big role in most Ruby scripts, especially when communicating with users. Text can consist of letters, numbers, special characters, or even blank spaces. So far, all of the text strings that you have worked with in this book have been enclosed inside matching pairs of double quotation marks, as demonstrated here:

"Sample text string"

However, there are other ways of creating text strings within Ruby scripts. Strings can be created by enclosing them within matching single quotation marks, as demonstrated here:

'Sample test string'

Deciding whether to use matching double or single-quoted strings is more than a matter of personal preference. Ruby allows you to do things to text strings enclosed inside matching double quotes that it does not allow within matching single quotes. For example, when working with double-quoted strings, Ruby is able to perform escaping and variable substitution operations using #{}. Single-quoted strings do not support either of these operations.

Formatting Text Strings

When working with double-quoted strings, Ruby recognizes a number of escape characters that, when found, are automatically replaced with the appropriate corresponding operation. For example, Ruby supports escape characters that execute tab and new line operations. By embedding these escape characters within double-quoted strings, you can exercise detailed control over the manner in which Ruby renders the strings when it displays them. Table 3.1 provides a list of escape characters that you can use to exercise control over text formatting within strings.

Table 3.1. String Escape Substitution Characters

Option

Description



Backspace

f

Formfeed

New line

Return

s

Space

Tab

To get a good understanding of how to work with escape characters, it helps to see a few examples.

puts "1 2 3 4 5"

When executed, this statement results in the following output.

1 2 3 4 5

Now, let’s reformat this example by embedding a series of escape characters.

puts "	1 	2 	3 	4 	5"

This time when you execute the statement, the following output is displayed.

1   2   3   4   5

As you can see, by embedding the escape character, you can perform tab operations that affect how the string is displayed. Now, look at this next example.

puts "
1 
2 
3 
4 
5"

This time the escape character has been placed before each number in the text string. Since this escape character is the equivalent of a new line operation, when executed this statement produces the following output.

1
2
3
4
5

Hint

Hint

You will get the chance to work with escape characters again when you work on this chapter’s game project, the Ruby Virtual Crazy 8 Ball game.

Variable Interpolation

String interpolation, also referred to as variable substitution, is performed by embedding a text string inside other strings using the #{} characters. Interpolation is the process of substituting the value of an expression or variable inside a string. To see how this works, look at the following statements.

totalScore = 100
puts "Game over. Your score is #{totalScore}."

When executed, these two statements produce the following output.

Game over. Your score is 100.

As you can see, the value stored in the totalScore variable has been substituted into a pre-specified location in the text string. As has been stated, interpolation also works with expressions.

totalScore = 100
bonusPoints = 50
puts "Game over. Your score is #{totalScore + bonusPoints}."

When executed, these statements generate the following output.

Game over. Your score is 150.

As you can see, Ruby added together the values of totalScore and bonusPoints and then substituted the resulting value into the text string in place of the specified expression.

Hint

Hint

You will get the chance to work with variable substitution again when you work on this chapter’s game project, the Ruby Virtual Crazy 8 Ball game.

Other Options for Manipulating Strings

Ruby provides you with many different ways of working with strings. For starters, you can compare two different strings to see if they are the same, concatenate them together to create new strings, and multiply them together. You can also compare one string to another to see if they match or spread text strings out over multiple lines. In addition, you can use Ruby’s support for regular expressions to perform a host of other string-manipulation techniques.

Hint

Hint

Regular expressions are covered in Chapter 7, “Working with Regular Expressions.”

Concatenating Text Strings

Earlier in the chapter, you learned how to perform string interpolation. As an alternative to string interpolation, you could achieve the same results using string concatenation. Concatenation is the process of joining two strings together to form a new string. Concatenation is performed using the + string method. To see how concatenation works, consider the following example.

totalScore = 100
puts "Game over. Your score is " + totalScore.to_s + "."

Here, three strings have been concatenated together. The first string is "Game over. Your score is ". The second string was created by converting the numeric value of totalScore to a string using the to_s method, and the third string is ".". When executed, this statement generates the following output:

Game over. Your score is 100.

Here is another example of how to concatenate strings together. This time, in addition to concatenating the two strings, the escape character has been included to control the format of the resulting new string.

story = "Welcome to Ruby Programming for the Absolute Beginner"
author = "by Jerry Lee Ford, Jr."
puts story + "

" + author

When executed, these statements generate the following output.

Welcome to Ruby Programming for the Absolute Beginner

by Jerry Lee Ford, Jr.

Multiplying Text Strings

In addition to creating new strings by concatenating existing strings together, you can also create new strings by multiplying existing strings. This is accomplished using the String class’s * method.

x = "Happy birthday to you. " * 3
puts x

Here, the text string "Happy birthday to you. " is repeated three times as shown here:

Happy birthday to you. Happy birthday to you. Happy birthday to you.

Comparing Text Strings

Another commonly performed string operation is to compare two strings to see whether they are equal. This is accomplished using the equality operator (= =). You have already seen examples of string comparisons numerous times in this book. For example, look at the following statements.

puts "Would you like to hear a few funny jokes? (y/n) "
answer = STDIN.gets
answer.chop!

if answer = = "n"  #See if the player elected not to play

Here, the user is prompted to enter a value of y or n. The user’s input is then captured as answer, which is then used in the last statement to determine whether the value of answer is equal to the value of "n".

Trap

Trap

String comparisons are performed using the == operator and not the = operator. Because they are similar, it is easy to get them mixed up. To see what I mean, start up a new irb session, and execute the statements shown here:

irb(main):001:0> x = 1
=> 1
irb(main):002:0> y = 2
=> 2
irb(main):003:0> puts "x and y are equal" if x = y
x and y are equal
=> nil

Here, x has been set equal to 1 and y has been set equal to 2. The puts statement displays a text string message only in the event that the values of x and y are equal. Therefore, when the puts statement is executed, it would seem to make sense that nothing would be displayed. However, this is not the case. Instead, the test string "x and y are equal" is displayed. To find out why, execute the commands shown here:

irb(main):004:0> x
=> 2
irb(main):005:0> y
=> 2

As you can see, the value of x is set equal to 2 as expected. However, the value of y has also been set to 2 and not to 1. The reason is because there is an error in the puts statement that was previously executed. It used the equals assignment operator (=) and not the equals comparison operator (==). As a result, instead of comparing x and y, the puts statements assigned the value of y to x.

Now, to prove that the above explanation is accurate, execute the following statements using the irb.

irb(main):001:0> x = 1
=> 1
irb(main):002:0> y = 2
=> 2
irb(main):003:0> puts "x and y are equal" if x == y
=> nil

This time, things went as expected and the test string was not displayed because the values of x and y were different.

Creating Multiline Text Strings

In addition to creating text strings by embedding them inside matching quotation marks, you can also create text strings by embedding text inside the %q{ and } characters or the %Q { and } characters. Embedding characters inside the %q{ and } characters creates a string that is equivalent to a single-quoted string. Embedding characters inside the %Q{ and } characters creates a string that is equivalent to a double-quoted string.

The advantage of using the %q{ and } and %Q{ and } characters in place of quotation marks is that these characters allow you to create strings that span multiple lines, as demonstrated here:

story = %Q{Once upon a time there were
three children named Alexander, William,
and Molly.}

Using the %q{ and } and %Q{ and } characters to define strings, there is no practical limit as to the length of your text strings. In addition, strings created using the %Q{ and } characters provide the same support for escape characters and interpolation as do double-quoted strings.

Trick

Trick

When creating multiline text strings, you can replace the opening and closing brackets with any matching set of characters you want. For example, the following example uses the %q[ and ] characters to define a string.

story = %q[Once upon a time there were
three children named Alexander, William,
and Molly.]

The important thing to remember when deciding which characters you want to substitute when creating a string is to make sure that those characters do not also appear inside the string text; otherwise, Ruby will get confused and things will not work correctly.

Working with String Class Methods

In addition to the previously discussed options for manipulating text strings, Ruby’s String class provides you with access to a number of additional string manipulation methods, as shown in Table 3.2.

Table 3.2. A Listing of Some of the Methods Belonging to the String Class

Method

Description

capitalize

Capitalizes the first letter of a string

downcase

Converts a string to all lowercase letters

chop

Removes the last character from a string

length

Returns an integer representing the number of characters in a string

next

Replaces the next letter in a string with the next letter in the alphabet

reverse

Reverses the spelling of a string

swapcase

Reverses the case of each letter in a string

upcase

Converts a string to all uppercase letters

Let’s look at a few examples that demonstrate how to work with these String class methods. For starters, look at the following example.

irb(main):028:0> story = "Once upon a time"
=> "Once upon a time"
irb(main):029:0> puts story.length
16
=> nil
irb(main):030:0>

Here, the String class’s length method was used to display the number of characters that make up the string stored in the story variable. In this next example, the upcase method is used to convert the content of the string to all uppercase characters.

The irb(main):030:0> story = "Once upon a time"
=> "Once upon a time"
irb(main):031:0> puts story.upcase
ONCE UPON A TIME
=> nil
irb(main):032:0>

As this last example demonstrates, Ruby allows you to combine different string methods.

irb(main):001:0> story = "Once upon a time"
=> "Once upon a time"
irb(main):002:0> puts story.reverse.upcase
EMIT A NOPU ECNO
=> nil
irb(main):003:0>

Here, the reverse and upcase methods were chained together and executed. The reverse method reversed the order of all the characters in the string and the upcase method converted them to all uppercase.

Hint

Hint

Refer to RDoc to learn more about the methods associated with the String class.

Object-Oriented Programming

Ruby is an object-oriented programming language. It sees everything that it interacts with, including data, as an object. Objects are self-contained entities, meaning that they include information about themselves in the form of properties and provide program code stored as methods for interacting with and manipulating themselves.

Objects are defined in what is referred to as a class. Using the information defined within a given class, you can create or instantiate new object instances based on that class. So for an automobile class, you could create specific object instances, each of which would represent an individual automobile. You could then assign each automobile instance a different color by assigning a color to the appropriate object property and controlling the operation of the object by executing its methods.

In addition to its name, an object may be assigned any number of properties. Object properties are implemented as named variables. An object representing an automobile might have properties representing the model and color of the vehicle. To store data for each of these properties, object properties are stored in variables, which are pointers to locations in memory where data is stored. The code defined within an object definition that you use in your Ruby scripts to interact with and control an object is referred to as object methods. Returning to the automobile example, you might define methods for starting and stopping the car and for turning on and off different features like headlights.

Defining a New Class

Ruby scripts use this object-orientedness to define real-world concepts like files, folders, and network resources, or perhaps things like people, automobiles, and animals. Once they’re defined, you can interact with and control objects and define relationships between objects.

Objects are defined as classes using the syntax outlined here:

class ClassName
  statements
end

class is a keyword that tells Ruby that a new class is being defined. ClassName is the name that is being assigned to the new class. You must assign a capital letter as the first character of every class definition. statements is a placeholder for one or more script statements that define class attributes (properties) and methods. end is a keyword that identifies the end of the class definition. For example, the following statements begin the outline of a class named Automobile.

class Automobile

end

A Class definition represents a template that can be used to create or instantiate individual objects. In the case of the preceding example, the class represents the logical definition of an automobile. The end keyword marks the end of the class definition.

Defining Class Properties

Within the class definition, you can define one or more attributes that describe characteristics associated with the class. For example, you might want to define class attributes such as model and color to the Automobile class. Class attributes, also referred to as properties, are defined inside the class using the attr_accessor keyword using the syntax outlined here:

attr_accessor : attribute1, :attribute2, ...

attr_accessor is a keyword that identifies a list of one or more object properties. Each property is preceded by the colon character. Successive properties are separated from each other with a comma. There is no limit to the number or properties that you can define. The following example demonstrates how to assign two properties to the Automobile class.

class Automobile
 attr_accessor :model, :color
end

The first property specifies the model or type of automobile being defined and the second property will be used to store the color of the automobile.

Instantiating and Interacting with New Objects

At this point, the Automobile class represents a functional class definition that is ready to be used as the basis for instantiating scripts objects, which is accomplished using the syntax outlined here:

variableName = ClassName.new

variableName is the name of a variable that will be used to store and refer to the object, and ClassName is the name of the class being used to create the new object. new is a method that initiates the creation of the new object. Using the above syntax, you can create a new object as shown here:

superCar = Automobile.new

When executed, this statement creates a new object named superCar using the Automobile class as its template. Once instantiated, you can assign values to the object’s properties, as demonstrated here.

superCar.model = "Edsel"
superCar.color = "Red"

Once defined, you can reference object property values as shown here.

puts "Super car is the car of tomorrow. It is based on the " +
"original #{superCar.model} design."

When executed, this statement displays the text string shown here:

Super car is the car of tomorrow. It is based on the original Edsel design.

If you want, you can modify the value assigned to a property by reassigning another value as demonstrated here.

superCar.model = "Mustang"

Defining Class Methods

In order to control your objects, you need to define class methods. You can then use the methods to programmatically interact with any object you instantiate. Ruby methods are defined using the following syntax.

def methodname(arguments)
   Statements
end

methodname is the name to be assigned to the new method. arguments is a comma-separated list of parameters passed to the method for processing. Within the method, the parameters are treated as local variables. Statements is a placeholder representing one or more statements that will be executed whenever the method is called. To get a better feel for how to define a custom method, take a look at the following example.

class Automobile

 attr_accessor :model, :color

  def honk
     puts "Honk!!!"
  end
end

As with properties, you can interact with object methods by specifying the name of the class, followed by a period and then the name of the method.

The following statements demonstrate how to create a new object based on the Automobile class and how to execute the class’s honk method.

myCar = Automobile.new
myCar.honk

Inheritance

One of the primary benefits of object-oriented programming is its ability to allow programmers to model the creation of classes (and therefore objects) based on real-life concepts like automobiles, tools, people, or anything else you can imagine. In real life, objects often have relationships with one another. For example, you have a father from whom you inherited certain qualities. You may have children to whom you will pass on certain attributes. In addition, you may have brothers or sisters with whom you share common attributes.

By supporting an object-oriented process known as inheritance, Ruby allows you to use one class definition as the basis for creating another class definition. In this case, the new or child class is created as a copy of the original or parent class. You can create as many child instances as you want. If necessary, you can modify the characteristics of the child class to suit your particular needs.

Thanks to inheritance, you can define classes that model real-life concepts. For example, you might define a generic automobile class that defines all of the basic properties associated with a car and which includes all of the methods required to control the car. And then you can use this class as a template for creating a whole series of child subclasses, each of which might represent an individual make and model of a car. For example, the class definition for a simple car is outlined here:

class Automobile

 attr_accessor :model, :color

  def honk
     puts "Honk!!!"
  end
end

Here, an Automobile class has been defined that contains two property definitions, model and color, and a method named honk. If you want, you can use the Automobile class as the basis for defining a new class. Once way of doing this would be to copy and paste the Automobile class and then to rename it as shown here:

class Edsel

 attr_accessor :model, :color

  def honk
     puts "Honk!!!"
  end
end

Creating a new class in this manner is highly inefficient because it results in two nearly identical sets of code that must be maintained. Should you later want to make a change in the class by adding another property, you have to make the modification twice, once for the Automobile class and again for the Edsel class. Instead of doing things this way, a much better way of creating another, related class of cars would be to base the new Edsel class on the Automobile class, taking advantage of object inheritance as demonstrated here:

class Edsel < Automobile

end

Here the superclass operator was used to create a new class named Edsel, which is modeled on the Automobile class. The Edsel class inherits all of the properties and methods in the Automobile class. Not only does this require less code, but this approach also reduces the chance of making typing errors since there is less to type in. In addition, if you should later decide to make a fundamental change that would affect all the car-related classes, all you have to do is make that change in the Automobile class and any child classes (or subclasses) will automatically inherit the change as well. There is no limit to the number of child classes that you can create from a parent class. Therefore, if you need to expand your product line to include a second type of car, you could easily do so as demonstrated here:

class Mustang < Automobile

end

Here, a new Mustang class has been created. It automatically inherits all the properties and methods of the Automobile class. Ruby allows you to modify child classes as necessary to customize them to match up to whatever changes need to be made to differentiate the child class from it parent class. For example, take a look at the following statements.

class Explorer < Automobile

 attr_accessor :transmission

 def breaks
    puts "... screech!"
  end

end

Here, a new Explorer class has been defined based on the Automobile class. In addition to inheriting all of the Automobile's properties and methods, the Explorer class has been modified to include a new property and a new method that is unique unto itself.

Hint

Hint

This book has shown you how to use a number of different methods. In most cases, you’ve been told which class the method is associated with. However, for methods like puts, chop!, and print, you’ve simply been shown how to use them without any explanation of where they come from. These methods are just a few of the methods stored in the Kernel module. A module is a container used to group classes, methods, and constants. This module is a component of the Object class. Methods belonging to the Object class are made available to every Ruby object.

Normally when you work with a method, you do so by specifying the name of the class where the method resides followed by a period and then the method name, as demonstrated here:

pause = STDIN.gets

When working with methods belonging to the Kernel module, you can simply specify the method’s name, as demonstrated here:

puts "Well, hello there."

Alternatively, if you want to be specific, you could rewrite the preceding statement as shown here:

Kernel.puts "Well, hello there."

Converting from One Class to Another

As has already been stated, in Ruby, numbers and strings are really just different types of objects. Ruby supports a number of different types of numeric classes, including Fixnum, Integer, Bignum, and float. Ruby automatically handles numeric class assignments. For the most part, you do not need to concern yourself with the class that Ruby has assigned to a given number. However, you may come across situations where you need to convert an object from one type to another. In some situations, Ruby will implicitly handle object conversion for you. Consider the following example:

irb(main):001:0> x = 10

Here, a variable named x has been assigned a value of 10. In response, Ruby will create a new object based on the Fixnum class. To verify this, you can execute the class method as demonstrated here:

irb(main):002:0> x.class
=> Fixnum

Implicit Class Conversion

As you can see, x is assigned to the Fixnum class. However, if you reassign a value too large to fit into that class, Ruby will automatically, or implicitly, convert x to Bignum, as demonstrated here:

irb(main):007:0> x = 1000000000000000
=> 1000000000000000
irb(main):008:0> x.class
=> Bignum

If you then assign a string as the value of x, Ruby will again change the object’s class, as demonstrated here:

irb(main):009:0> x = "Hello"
=> "Hello"
irb(main):010:0> x.class
=> String

Explicit Class Conversion

In addition to implicitly changing or coercing an object from one class to another, Ruby also provides you with the ability to explicitly coerce objects from one class to another. You might need to do this if you have written a script that collects and processes user input. By default, any input provided by the user will be treated by Ruby as a string, even if the input that was provided was a number. For example, consider the following example.

irb(main):001:0> answer = STDIN.gets
10
=> "10
"

Hint

Hint

The previous example collects user input by executing the STDIN class’s gets method. This method pauses the console session and waits for the user to provide input, which is then assigned the specified variable.

As you can see, the value entered by the user was the number 10. Ruby appended the characters to the end of the user’s input when the Enter key was pressed. Executing the following command, remove the trailing /n characters, ensuring that the value assigned to answer is exactly what the user entered.

irb(main):002:0> answer.chop!
=> "10"

Trick

Trick

When the user presses the Enter key to submit her input, Ruby automatically appends an end of line marker to the end of the input. In this example, the presence of the end of line marker has no impact on the example.

The end of line marker can easily be removed from the answer variable using the chop! method. When executed, this method removes the last character from a specified string. If the string ends with the characters, the chop! method will remove both characters.

Using this method, you can easily remove the /n characters from any input provided by the user, as demonstrated here:

answer = STDIN.gets
answer.chop

If you now try to perform addition using the input, you will run into an error because Ruby is unable to explicitly convert a value of “10” to 10.

irb(main):003:0> x = 5 + answer
TypeError: String can't be coerced into Fixnum
        from (irb):3:in '+'
        from (irb):3
irb(main):004:0>

To prevent this type of error from occurring and to get the results you expect, you can explicitly force the conversion of an object’s type using different conversion methods. For example, you could use the to_i method to return the value stored in answer to an integer.

irb(main):005:0> x = 5 + answer.to_i
=> 15
irb(main):006:0>

As you can see, once explicitly converted, the error no longer occurs and the expected result is attached. If you prefer, you can use the tp_f method to convert a string to a floating point number. Going in the opposite direction, you can use the to_s method to convert any numeric value to a string, as demonstrated here:

irb(main):001:0> x = 5
=> 5
irb(main):002:0> y = 4
=> 4
irb(main):003:0> z = x + y
=> 9

In this example, two variables are defined and assigned values of 5 and 4, which are then added together and assigned to a variable named z. Using the to_s method, you can instruct Ruby to treat the values assigned to x and y as strings instead of numeric values, as demonstrated here:

irb(main):004:0> z = x.to_s + y.to_s
=> "54"
irb(main):005:0>

This time, instead of adding two numeric values together, Ruby coerces x and y into strings and concatenates both strings together to form a new string that is then assigned to a variable named z. If you use the class method to check on the z variable’s class type assignment, you will see that it has been set to String.

irb(main):006:0> z.class
=> String
irb(main):007:0>

Storing and Retrieving Data

When working with numbers, strings, and other types of objects, it often helps to be able to store their values in order to later be able to reference and modify them. This is accomplished through the use of variables. A variable is a pointer to a location in memory where the objects that are created in your scripts are stored. These objects may include numbers, text, or any custom objects that you have defined. For example, the following statement defines a variable named x and assigns it an integer value of 10.

x = 10

Likewise, the following statement assigns a text string to a variable named y.

y = "Well, hello there."

Variables are an essential part of any Ruby script, which is why you have already seen them used many times in this book. Now it is time to learn more about how to create and work with them.

Naming Variables

In Ruby, variable names are case sensitive. This means that to Ruby, totalcount and TotalCount are two separate variables. Ruby has a few rules that you need to be familiar with regarding the naming of variables. These rules are listed here:

  • Variable names must begin with a letter or an underscore character

  • Variable names can only contain letters, numbers, and underscore characters

  • Variable names cannot include blank spaces

Following these rules, each of the following variable names would be regarded by Ruby as valid.

  • Totalscore

  • totalScore

  • total_score

  • Total_Score

  • x

  • TotalTimes2

The variable names shown in Table 3.3, on the other hand, are not considered by Ruby to be valid.

Table 3.3. Invalid Variable Names

Option

Description

total score

Variable names cannot include blank spaces

@totalScore

Special characters are not allowed

total-score

Only letters, numbers, and the underscore characters are permitted

2TimesLucky

Variable names cannot begin with a number

Variable Assignments

As you have already seen many times, variable value assignments in Ruby are made using the equals assignment operator (=), as demonstrated here:

x = 10

Here an integer value of 10 has been assigned to a variable named x. Use the equals assignment operator to also modify a variable’s value by assigning it the results of an expression, as demonstrated here:

x = 1
x = x + 1

Here, a variable named x is assigned an initial value of 1. Then the value assigned to x is modified by assigning the values returned from the expression x + 1 to x. As a result the value of x was incremented by 1.

Incrementing a variable’s value is a common task. To help make it easier to perform, you can use the += operator, as demonstrated here:

x += 1

The += operator provides a shorthand way of incrementing a variable’s value of a specified amount. In the case of the previous example, the value of x is incremented by 1.

Variable Scope

In Ruby, variable access depends on the scope that has been set for that variable. Scope is a term that describes the areas within a script where a variable can be seen and accessed. Ruby supports three different scopes, as outlined in Table 3.4.

Table 3.4. Variable Scopes

Type

Opening Character(s)

Description

Local

a-z and _

Scope is limited to each iteration loop, module, class, and method in which it is defined or to the entire script if the variable is defined outside of one of the structures.

Instance

@

Scope is limited to the scope associated with the object itself.

Class

@@

Scope is limited to objects of class.

Global

$

Scope has no limit, allowing the variable to be accessed throughout the script.

In Ruby, variable scope is indicated by the characters you use at the beginning of the variable name. As Table 3.4 shows, a variable whose name begins with the $ character is a global variable, and a variable that begins with a lowercase letter or the underscore character is a local variable. For this book, you will only need to worry about working with local and global variables.

Local variables are variables that have a limited scope. For example, as Table 3.4 shows, any variable whose name begins with a lowercase letter and that is defined inside a method is accessible only within that method. For example, the following statements show a method named Add_Stuff. This method accepts two arguments, x and y, which are local variables within the method and thus not accessible outside of the method.

def Add_Stuff(x, y)
   puts x + y
end

If you key this example into the irb and then execute it by passing the method arguments of 3 and 4, a result of 7 will be displayed.

irb(main):004:0> Add_Stuff(3, 4)
7

However, if you attempt to access either the x or the y variable from outside of the method, as demonstrated next, an error will occur.

irb(main):005:0> puts x
NameError: undefined local variable or method 'x' for main:Object
        from (irb):5

Global variables can be accessed from anywhere within a Ruby script and are created by making the first character of the variable name a $, as demonstrated here:

$x = 1000

The $x variable will be accessible from anywhere within the Ruby script that it is defined in. You will see an example of how to use global variables later in this chapter’s game project, the Ruby Virtual Crazy 8 Ball game.

Storing Data That Does Not Change

Any time you are creating a Ruby script that will use a value that is known at design time and not subject to change, you should define that value as a constant. A constant is very much like a variable, the differences being that constant names begin with a capital letter and will generate warning messages in the event you change their values during script execution.

Trap

Trap

If you change a constant’s value, Ruby will complain, displaying a warning message, allowing the scripts to continue running. This makes Ruby different from most other programming languages that generate an error message and halt script and program execution.

The following expression demonstrates how to define a constant and assign it a value.

irb(main):001:0> Pi = 3.14
=> 3.14

Once defined, you can reference the value assigned to the constant as needed. If you forget that you are working with a constant and change the value that is assigned to it, Ruby will generate a working message while allowing the script to continue running.

irb(main):002:0> Pi = 3.1415
(irb):2: warning: already initialized constant Pi
=> 3.1415

Back to the Ruby Virtual Crazy 8 Ball Game

Okay, now it is time to turn your attention back to the development of this chapter’s game project, the Ruby Virtual Crazy 8 Ball game. As you follow along with the development of this script file, be sure to focus on the usage of text strings, variables, classes, and objects. In particular, pay close attention to the manner in which the script interacts with and controls objects once they have been instantiated.

Designing the Game

The development of the Ruby Virtual Crazy 8 Ball game will be completed in 10 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 game’s virtual 8 ball window.

  5. Instantiate custom script objects.

  6. Display a greeting message.

  7. Get confirmation before continuing game play.

  8. Analyze the player’s reply.

  9. Manage early game termination.

  10. Process and respond to player questions.

Remember to follow along carefully and not to skip any steps or parts of steps as you work your way through this exercise. Specifically, look out for typos and make sure that you do things in the correct order.

Step 1: Creating a New Ruby File

The first step in creating the Ruby Virtual Crazy 8 Ball game is to start up your preferred text or code editor and then to create a new Ruby script file. Save this file with a file name of Crazy8Ball.rb and store it in whatever folder you have decided to keep your Ruby scripts.

Step 2: Documenting the Script and Its Purpose

Once you have created your new script file, the next step is to add the following comment statements to it. These statements provide a high-level description of the game and its purpose.

#--------------------------------------------------------------------------
#
# Script Name: Crazy8Ball.rb
# Version:     1.0
# Author:      Jerry Lee Ford, Jr.
# Date:        October 2007
#
# Description: This Ruby script demonstrates how to work with variables
#              and to generate random numbers in order to create a fortune
#              telling game that provides randomly selected answers to
#              player questions.
#
#--------------------------------------------------------------------------

Hint

Hint

To get as much value as possible out of the script’s opening documentation statements, modify them to suit your own needs. For example, you might want to consider including a place for your URL if you have a website. In addition, you might want to add a space for recording changes, game instructions, or anything else that you think would be useful.

Step 3: Defining a Screen Class

Now it is time to define the first of two custom classes used by the script. The first class is named Screen. It closely resembles the Screen class used in Chapter 2. However, this version of the Screen class includes a new method definition.

# 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 first method defined within this class is the cls method. It contains two statements. The first statement writes 25 blank lines to the console window, clearing the screen. The second statement processes a string containing the a escape character sequence, which makes an audible beep sound, thus notifying the player each time the terminal screen is cleared.

Step 4: Defining a Ball Class

The script’s second class is named Ball. It serves as a template that the script will use to instantiate an object that represents a virtual 8 ball. As such, the class defines a number of properties and methods required to operate and interact with the 8 ball.

#Define a class representing the 8 ball
class Ball

  #Define class properties for the 8 ball
 attr_accessor :randomNo, :greeting, :question, :goodbye

 #Define a method to be used to generate random answers
 def get_fortune
    randomNo = 1 + rand(6)

    #Assign an answer based on the randomly generated number
    case randomNo
        when 1
         $prediction = "yes"
        when 2
         $prediction = "no"
        when 3
         $prediction = "maybe"
        when 4
         $prediction = "hard to tell. Try again"
        when 5
         $prediction = "unlikely"
        when 6
         $prediction = "unknown"
      end

    end

   #This method displays the 8 ball greeting message
   def say_greeting
      greeting = "		 Welcome to the Virtual Crazy 8 Ball game!" +
      "












Press Enter to " +
                "continue. 

: "
     print greeting
    end

   #This method displays the 8 ball's primary query
   def get_question
      question = "Type your question and press the Enter key. 

: "
     print question
    end

   #This method displays the 8 ball answers
   def tell_fortune(randomAnswer)
     print "The answer is " + randomAnswer + ". 

: "
    end

   #This method displays the 8 ball's closing message
   def say_goodbye
     goodbye = "Thanks for playing the Virtual Crazy 8 Ball game!

"
    puts goodbye
  end

end

The class definition begins by specifying four class properties. The randomNo property will be used to store a random number between 1 and 6. The greeting property will be used to store a text string containing the game’s welcome message. The question property will be used to store a text string that will be used to notify the player when it is time to ask a question. The goodbye property will be used to store a text string that holds the game’s closing message.

In addition to the four class properties, the class also defines five methods. The first method is named get_fortune and is responsible for randomly selecting one of six possible answers to the player’s questions. It accomplishes this task using the rand method, which retrieves a random number in the form of an integer.

Hint

Hint

The rand method returns a random number between 1 and the specified upper limit. Therefore rand(6) returns a number that is greater than 0 and less than 6. Adding 1 to this number results in a range of number from 1 to 6.

The randomly generated number, stored in randomNo, is used in a case code block that assigns a text string representing one of six 8 ball answers to the $prediction global variable.

Hint

Hint

A case code block is a structure for implementing conditional logic. It compares a single value, in this case randomNo, to a series of possible matching values, as specified by one or more when statements. You will learn more about how to work with the case code block in Chapter 4, “Implementing Conditional Logic.”

The next method defined is the say_greeting method, which assigns a text string to the class’s greeting property and then uses the print method to display that string. The get_question method comes next. It assigns a text string to the class’s question property and then uses the print method to display that string. The tell_fortune method is then defined. It accepts a single argument, which is assigned to a local variable named randomAnswer. This variable is then used to formulate a text statement that is displayed using the print method. The last method that is defined is the say_goodbye method, which assigns a text string to the class’s goodbye property and then uses the print method to display that string.

Step 5: Instantiating New Objects

The next step in creating the Ruby Virtual Crazy 8 Ball game is to instantiate both of the custom classes that you just defined by adding the following statements to the end of the script file.

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

Console_Screen = Screen.new  #Initialize a new Screen object
Eight_Ball = Ball.new  #Initialize a new Ball object

As you can see, a single object is being instantiated based on each class using the new method. A variable named Console_Screen is used to represent the Screen object and a variable named Eight_Ball is used to represent the Ball object.

Step 6: Greeting the Player

Now that the scripts’ objects have been instantiated, it is time to begin working with them to interact with and control both the screen and 8 ball. To begin, add the following statements to the end of the script file.

Console_Screen.cls  #Clear the display area

Eight_Ball.say_greeting  #Call method responsible for greeting the player

Console_Screen.pause  #Pause the game

The first statement executes the Screen class’s cls method to clear the display area. The second statement executes the Ball class’s say_greeting method to display a greeting message. The last statement uses the Screen class’s pause method to pause the game and give the player a chance to review the greeting message.

Step 7: Prompting for Confirmation to Continue

Once the player dismisses the 8 ball’s greeting message, the game sets up a loop that requires the player to provide confirmation of her intention to play. This will provide the player the chance to cancel game play in the event the game was started by accident. The script statements responsible for performing this task are shown next and should be added to the end of the script file.

answer = ""  #Initialize variable that is used to control the game's first
             #loop

#Loop until the player enters y or n and do not accept any other input.
until answer == "y" || answer == "n"

  Console_Screen.cls  #Clear the display area

 #Prompt the player for permission to begin the game
  print "Would you like to have your fortune predicted? (y/n)

: "

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

end

The first statement initializes a variable that will be used to control the execution of the loop that follows, in which a series of script statements have been embedded. The loop has been set up to execute until the player enters a value of y or n when prompted for confirmation. By using a loop to control the execution of the embedded statements, you are able to validate the player’s input. If the player responds by keying in anything other than a y or n, the loop will execute again, re-prompting the player to provide valid input.

Hint

Hint

A loop is a structure that permits a set of statements to be executed repeatedly a preset number of times or until a specified condition occurs. You will learn all about Ruby’s support for loops in Chapter 5, “Working with Loops.”

Step 8: Analyzing the Player’s Response

The next step in the development of the game is to further analyze the input that the player has provided. This is accomplished by adding the following conditional logic statements to the end of the script file.

#Analyze the player's response
if answer == "n"  #See if the player elected not to play

else  #The player has elected to play the game

end

Hint

Hint

The rest of the statements in the script will either be embedded within these statements or placed immediately after them. These statements control the high-level conditional logic for the script. The statements that you embed between the first two statements will execute when the player responds with a reply of n when asked for permission to play the game. The statements that you embed between the last two statements will execute when the player responds with a reply of y.

Step 9: Managing Early Termination of the Game

This next script statements are to be executed when the player responds with a value of n when prompted for confirmation to play the game and therefore should be placed between the opening if statement and the else statement that you added to the script file in the previous step.

Console_Screen.cls  #Clear the display area

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

"

As you can see, these statements clear the scripts and then display a text message that encourages the player to return and play again later.

Step 10: Responding to Player Questions

The rest of the statements that you will add to the script file need to be placed between the else and the end statements that are responsible for outlining the script’s overall controlling logic.

#Initialize variable that is used to control the game's primary loop
gameOver = "No"

#Loop until the player decides to quit
until gameOver == "Yes"

  Console_Screen.cls  #Clear the display area

  #Call upon the method responsible for prompting the player to ask a
  #question
  Eight_Ball.get_question

 #Call upon the method responsible for generating an answer
  Eight_Ball.get_fortune

  Console_Screen.pause  #Pause the game

  Console_Screen.cls  #Clear the display area

  #Call upon the method responsible for telling the player the 8 ball's
  #answer
  Eight_Ball.tell_fortune $prediction

  Console_Screen.pause  #Pause the game

  Console_Screen.cls  #Clear the display area
 #Find out if the player wants to ask another question
 print "Press Enter to ask another question or type q to quit. 

: "

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

  #Analyze the player's response
  if answer == "q"  #See if the player elected not to play
    gameOver = "Yes"  #The player wants to quit
  end

end

Console_Screen.cls  #Clear the display area

#call upon the method responsible for saying goodbye to the player
Eight_Ball.say_goodbye

This final set of statements is responsible for managing the overall play of the game. For starters, a variable named gameOver is defined and assigned an initial value of "No". This variable is used to control the execution of the loop that follows. This loop contains the script statements that prompt the player to ask the 8 ball a question and then call upon the various object methods as required to execute Screen and Ball methods that control Screen and Ball interaction.

Upon each execution of the loop, the Ball class’s get_question method is executed. This method displays a message that is used to prompt the player to enter a question. Next, the get_fortune method is called. This method is responsible for randomly selecting the 8 ball’s answer. The 8 ball’s answer is then displayed by calling on the tell_fortune method. Finally, a message is displayed that instructs the player to either press the Enter key to ask another question or to type q and press Enter to end the game. The player’s response is then collected and assigned to the answer variable. The String class’s chop! method is executed to remove the end of line character from the end of the variable’s value, after which the value is analyzed to determine whether it is equal to q. If it is, the value of gameOver is set equal to "Yes", resulting in the end of the loop. Otherwise the value of gameOver remains unchanged and the loop executes again.

Once the loop finishes executing, it is time to bring the game to a close. This is accomplished with the last three statements shown above—which clear the screen and display the 8 ball closing message, thanking the player for taking time to play.

Running Your New Ruby Script Game

Okay, that’s it. Go ahead and save your Ruby script. Assuming that you have followed along carefully and that you did not make any typing mistakes when keying in the code statements that make up the script file, everything should work as expected. If you run into any errors, read the resulting error messages carefully to ascertain what went wrong. If necessary, go back and review the script and look for mistyped or missing scripts statements.

Hint

Hint

If you really run into trouble when creating your version of the Ruby Virtual Crazy 8 Ball game, you can go to this book’s companion website at www.courseptr.com/ downloads and download the source code for this game and then compare it to your script file to see where things went wrong.

Summary

In this chapter, you learned how to create and work with text strings. This includes learning different ways of formulating strings as well as how to manipulate strings using different string methods. You learned how to define custom classes and then to instantiate new objects based on these classes. You learned how to define object properties and methods and to reference these properties and methods. You also learned how to store and retrieve object data using variables and the rules for formulating variables names.

Now, before you move on to Chapter 4, “Implementing Conditional Logic,” I suggest you set aside a few more minutes to work on the Ruby Virtual Crazy 8 Ball 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