Getting Started with Ruby, III: Numbers and Logic

This entry is part 3 of 6 in the series Getting Started with Ruby

Getting Started with Ruby

The last part of this series looked at strings and their methods, along with storing information as variables. We also wrote a Greeter program and Madlibs program, making web versions of them both using Sinatra. In this post, the focus is how to use numbers in Ruby and create logical statements.

Remember, if you want to follow along and do the examples, fire up irb from your command prompt.

Time for Some Sums!

4 + 5
=> 9

4 and 5 are both objects and + is actually a method. We saw in the previous post how to call a method using the dot syntax and you can do the same with the + method:

4.+(5)

This is an example of adding arguments to a method. 4 is the object calling the + method and 5 is the argument. Ruby lets us write this in a more natural way (like writing out arithmetic). This is known as syntactic sugar, so 4 + 5 is seen as being equivalent to 4.+(5).

We can do lots of different arithmetic in a similar way:

10 - 4
  => 6
4 * 8
  => 32
9 / 2
  => 4
1

<p>... Hang on! That last sum was incorrect. This is because 9 and 2 are both Integers. This means that the solution to 9/2 will only give you the integer part of the answer. To get the correct answer, we need to use a different type of number called a Float (because they use floating point decimals). To use Floats instead of Integers, just write 9 as 9.0 instead (this is known as a Float literal):</p>

1
> 9.0/2
=> 4.5

That’s better!

Ruby gives us methods to find out if a number is odd or even:

> 4.odd?
=> false
> 5.odd?
=> true

In the last post we learned about ‘bang methods’. The odd? and even? methods are examples of boolean methods. These are methods that only return the result of true or false. They usually have a question mark (‘?’) character on the end of the method.

Ruby can also do some quite cool mathematical stuff, like finding the greatest commond divisor of two numbers using the gcd method. One number calls the method (12 in the example below) and the other number is the argument of the method (20 in the example below):

> 12.gcd(20)
=> 20

There is also a lcm method that finds the lowest common multiple of two numbers:

> 15.lcm(20)
=> 60

One of my favorite methods is the times method, shown in the example below:

> 5.times do
>  puts "Hello!"
> end
Hello!
Hello!
Hello!
Hello!
Hello!

I love how it encapsulates the readability of the Ruby programming language. It really does do what it says on the tin and reads almost like an English sentence. By the way, the bit of code between the do and end statements is called a block and we’re going to be covering much more about them in future posts.

One last Integer method that we’re going to look at is the to_s method, which stands for ‘to string’. It takes an integer and converts it into a string representation of that integer, for example:

> 7.to_s
=> "7"

This allows you to treat the number like a string. There is also an equivalent method for strings called to_i which changes a string into an integer:

"42".to_i
=> 42

You have to be careful with this as the string has to have some numbers at the beginning, otherwise it will return 0 (You can see more here).

Generating Random Numbers

It’s often useful to generate a random number when programming. This is easily done in Ruby using the rand method. This will generate a floating point number between 0 and 1.0, like so:

> rand
=> 0.29635454177765397

If you provide an integer argument, then it will generate a random integer between 0 and up to, but not including the argument. For example:

> rand(6)
=> 2

The above code will generate a random number from 0 – 5. We can use this (and a bit of the arithmetic we learnt earlier to mimic the rolling of a die:

> rand(5) + 1
=> 4

Check out this very in-depth post from Robert Qualls on randoms and Ruby for more info.

Logic

We can create logical statements in Ruby using the if and then statement. For example, we could play a game of trying to roll a 6 on a die:

number = rand(5) + 1
if  number == 6 then puts "Congratulations, you rolled a six!" end

Most of this is quite easy to figure out, but there are a few gotchas. First of all, notice the difference between = and ==. In the first case, we use = for assignment, that is setting a variable, ‘number’ in this case, equal to a value, a random number from 1 to 6. In the second case, we are using == to test equality. In the example, we are testing if the value of number is equal to 6. Be careful not to confuse these because writing this:

if  number = 6 then puts "Congratulations, you rolled a six!" end

actually sets the value of number to 6 and therefore will always be true!

There is also an else statement:

number = rand(5) + 1
if number.odd?
  puts "You rolled an odd number"
else
  puts "You rolled an even number"
end

Notice that you don’t actually have to use the then statement. We can also nest lots of statements using the elsif statement (not the spelling!):

number = rand(5) + 1
if number == 1
  puts "You rolled a 1"
elsif number == 2
  puts "You rolled a 2"
elsif number == 3
  puts "You rolled a 3"
elsif number == 4
  puts "You rolled a 4"
elsif number == 5
  puts "You rolled a 5"
else
  puts "You rolled a 6"
end

Guess My Number Program

Now that we’ve learned a little bit about how numbers work, we’re going to write a short program that invites the user to guess a randomly chosen integer from 1 to 100. Save the following code in a file called ‘guessmynumber.rb’:

number = rand(100) + 1
puts "I'm thinking of a number between 1 and 100, try and guess what it is"
guess = gets.to_i
attempts = 1
until guess == number do
  if guess < number then puts "Too small, try again"
  elsif guess > number then puts "Too big, try again"
  end
  guess = gets.to_i
  attempts += 1
end
puts "Well done, you guessed my number in #{attempts} attempt#{'s' if attempts > 1}!"

First we set a variable called number, which is the random number between 1 and 100 that is to be guessed. We use the rand method with an argument of 100 and then add 1 to set this.

After this, the puts method is used to print a string explaining what the player has to do.
Then the gets method gets a guess from the player, which is stored in a variable called guess. Any input from the user is stored as a string (even if you enter a number), so we need to use the to_i method to ensure that the value is treated as an integer.
Next, set the number of attempts to 1 using the variable attempts. This will be used to keep track of how many attempts the user has before guessing correctly.
Then, there is the statement until guess == number do. This is a nice bit of Ruby that says: Until the guess is equal to the number, do the following. It’s an example of a loop that we’ll be looking at in more detail in the next part of the series.

After the do statement, we have another example of a block of code. This is what is carried out until the user guesses the right number.

We use the if and elsif statements to check if the guess is lower or higher than the number and then give some appropriate feedback. After which, take another guess and increase the number of attempts by 1. This is done using the statement attempts += 1, which is shorthand for writing attempts = attempts + 1.

Once the number is guessed correctly, the program breaks out of the loop and displays a message. The interpolation trick we learned in the last post is used to insert the attempts variable into the string. Notice that we have a neat way of using the if statement to pluralize the number of guesses if it’s more than one.

Guess My Number on the Web

As usual in this series, we’re going to finish off by putting our program on the web using Sinatra. For this program we will need to keep note of the number after each request. The HTTP protocol that the web uses is stateless, which means that information isn’t stored between requests.

One way to get around this is to use sessions. This is easy to do in Sinatra – you just need to enable them. Create a file called ‘webguessthe_number.rb’ and add the following lines:

require 'sinatra'

enable :sessions

This requires the sinatra gem and also enables sessions. Sessions use cookies to store the information. Be aware that these are not secure and can be intercepted and decrypted. It’s not so much of a problem in the case of this game, but keep this in mind if you ever plan to store sensitive information.

Next we want to set the game up, we’ll start by creating a route handler for the root URL (found at http://localhost:4567) when a user request the web page.

get '/' do
  @message = "I'm thinking of a number between 1 and 100, try and guess what it is"
  session[:number] = rand(100) + 1
  session[:attempts] = 0
  erb :guess
end

First of all, set an instance variable called @message. Instance variables always start with a @ symbol and they are available in the view associated with the route handler. At the end of the route handler we call the view guess using ERB. In the view, we will be able to refer to the @message variable. Also, we set the number and attempts variables in this route handler.

As mentioned earlier, we use sessions for this, otherwise the information will be lost after each request. This is done by using a hash to store the information, so session[:number] holds the value of the number and session[:attempts] holds the value of the number of attempts.

We need some HTML code that we’ll write using ERB, the following code goes at the end of the file:

__END__

@@guess
<!doctype html>
<html>
  <header>
    <title>Guess the Number</title>
  </header>
  <body>

    <p><%= @message %></p>
    <form method="POST" action="/">
      <input name="number">
      <input type="submit" value="Guess">
    </form>
  </body>
</html>

This is a basic web page that contains a form for submitting the guess. The form will be posted using the HTTP POST method and we also need to provide the route where it will be posted. In this case, we are doing what is called a postback when we use the same URL that the form is located on (the root of the site ‘/’ in this case). Notice that the name attribute is number as we’ll need this to grab the value of it when the form is posted.

We need to create a route handler for when this happens. Because the form is POSTed, we will use the post method for our route handler with the route that was specified in the form’s action attribute. Place the following code after the other route handler (but before the ERB code):

post '/' do
  number = session[:number]
  guess = params[:number].to_i
  session[:attempts] += 1
  redirect to('/success') if guess == number
  if guess < number then @message = "Too small, try again"
  elsif guess > number then @message = "Too big, try again"
  end
  erb :guess
end

Here we retrieve the number that was saved in the session hash, then grab the guess from the form. All values submitted by a form are strings, so we need to use the to_i method to convert it to an integer. We also increase the number of guesses (also stored in the session hash) by one.

Next, we use some if logic to redirect to a success page if the guess is equal to the number. Notice that this is written the other way round to the examples given above (the if statement comes first), which has the effect of making it more readable.

If the user isn’t redirected, then their guess must have been wrong, so we use some if and elsif logic to test if the guess is too big or too small and update the @message instance variable with an appropriate message. We finsih by displaying the guess view in ERB again so the user can provide another guess.

The paragraph that contains the @message variable will now display a different message depending on the guess, even though we are using the same view.

All that is left to do is create a success route handler. This goes after the last route handler:

get '/success' do
  attempts = session[:attempts]
  "Well done, you guessed my number in #{attempts} attempt#{'s' if attempts > 1}!"
end

This simply retrieves the number of guesses from the session hash then displays a congratulatory message that informs the user of how many attempts it took.

Have a go at playing the game by entering ruby web_guess_the_number in a terminal to start the server and then visiting http://localhost:4567 in your browser.

That’s All Folks

In this post, we’ve introduced Integers and Floats and looked at some of their methods. We’ve also looked at how to construct a logical statement using if, then, else and elsif statements. We put all this together to create a “Guess the Number” program that we put on the web using sessions to store the information. In the next part of the series, we’re going to look at containers such as arrays and hashes and how to perform loops. In the meantime, have a play around with what we’ve covered so far and be sure to leave any questions or comments below.

Getting Started with Ruby

<< Getting Started with Ruby, II: Strings and ThingsGSwR IV: Going Loopy Over Arrays and Hashes >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.