Programming a Guessing Game

Let’s jump into DBL by working through a hands-on project together! This chapter introduces you to a few common concepts by showing you how to use them in a real program. You’ll learn about variables, main functions, terminal I/O, and more. In the following chapters, we’ll explore these ideas in more detail. In this chapter, you’ll just practice the fundamentals.

We’ll implement a classic beginner programming problem, a guessing game. Here’s how it works: the program will generate a random integer between 1 and 100. It will then prompt the player to enter a guess. After a guess is entered, the program will indicate whether the guess is too low or too high. If the guess is correct, the game will print a congratulatory message and exit.

Setting up a new project

To set up a new .NET project, go to the projects directory that you created in Chapter 1 and make a new console app project by using the .NET CLI, like so:

dotnet new SynNETApp -n GuessingGame
cd GuessingGame

The first command, dotnet new, takes the template name (SynNetApp) as the first argument and the name of the project (GuessingGame) as the second argument. The second command changes to the new project’s directory.

As you saw in the Getting Started section, dotnet new generates a “Hello World” program for you. Check out the Program.dbl file:

import System

main
proc
    Console.WriteLine("Hello World")
endmain

Now let’s compile this “Hello World” program and run it in the same step using the dotnet run command:

dotnet run Program.dbl

The run command comes in handy when you need to rapidly iterate on a project, as we’ll do in this game, quickly testing each iteration before moving on to the next one.

Open the Program.dbl file. You’ll be writing all the code for your program in this file.

Processing a guess

The first part of the guessing game program will ask for user input, process that input, and check whether the input is in the expected form. To start, we’ll allow the player to input a guess. Replace the contents of Program.dbl with the following:

import System

main
proc
    stty(0) ; Enable .NET console input
    Console.WriteLine("Guess the number!")

    Console.WriteLine("Please input your guess.")

    data guess = Console.ReadLine()

    Console.WriteLine("You guessed: " + guess)
endmain

Code breakdown

Let’s break down each part of the code, starting with the first line:

import System

import System tells the compiler to make the contents of the System namespace implicitly available in this source file. For our use case, System provides access to fundamental classes for managing input and output (I/O), basic data types, and other essential services. This access is necessary to use the Console class in the program.

main
proc

main indicates the starting point of the program. In DBL, main is a special keyword used to define the entry point of the application. As you’ve seen in the Hello World example, you can skip the main keyword and just start with proc. In this example we’re using a fully declared main. proc signals the transition from the data division to the procedure division. The procedure division contains the statements that perform the tasks of the program.

stty(0) ; Enable .NET console input

DBL has multiple ways to read input from the console. The stty statement is used to enable the .NET console input. This is necessary for using the Console.ReadLine() method in the program, and it’s mutually exclusive with other console input methods.

Console.WriteLine("Guess the number!")

Console.WriteLine is a method from the .NET Console class that writes a line of text to the standard output stream, which is the console in this case.

The following line outputs the text to prompt the user for a guess:

Console.WriteLine("Please input your guess.")

Similar to the previous line, this line outputs “Please input your guess.” to the console. It’s a prompt for the user to enter their guess.

The next line declares a variable named guess:

data guess = Console.ReadLine()

data guess declares a variable named guess. Because it does not specify the type of the variable, the type will be inferred from the value assigned to it. In this case, it will be a string. The = followed by a call to Console.ReadLine() reads the next line of characters from the standard input stream (the console input in this case) and then stores the result in the variable guess.

Console.WriteLine("You guessed: " + guess)

This combines “You guessed: “ with the value stored in guess, displaying the user’s input on the console.

Finally, the endmain, which is optional, marks the end of the main procedure:

endmain

The endmain is optional and marks the end of the main procedure.

Testing the first part

Let’s test the first part of the guessing game. Run it using dotnet run:

dotnet run
Guess the number!
Please input your guess.
5
You guessed: 5

At this point, the first part of the game is complete. We’re getting input from the keyboard and then printing it.

Generating a secret number

Next, we need to generate a secret number that the user will try to guess. To make the game fun to play more than once, the number should be different every time. We’ll use a random number between 1 and 100 so the game isn’t too difficult. DBL has a built-in random number facility, the RANDM routine, but since it’s not a super ergonomic function, we’re going to use the Random class from the System namespace instead. Let’s start using Random to generate a random number between 1 and 100. Replace the contents of Program.dbl with the following:

import System

main
proc
    stty(0) ; Enable .NET console input
    Console.WriteLine("Guess the number!")

    data random = new Random()
    data randomNumber = random.Next(1, 101) ; Generates a random number between 1 and 100

    Console.WriteLine("Please input your guess.")

    data guess = Console.ReadLine()

    Console.WriteLine("You guessed: " + guess)
    Console.WriteLine("The secret number was " + %string(randomNumber))
endmain

First, we’ve added a variable named random and assigned it a new instance of the Random class. This is a class that provides a convenient way to generate random numbers. Next we’ve added a variable named randomNumber and assigned it the result of calling the Next method on the random variable. The Next method takes two arguments; the first is the inclusive lower bound of the random number, and the second is the exclusive upper bound. In this case, we’re passing 1 and 101, so the random number will be between 1 and 100. Finally, we’ve added a line to convert our secret number to be a string, then output it to the console.

Try running the program a few times:

dotnet run
Guess the number!
Please input your guess.
You guessed: 50
The secret number was 37

You should get different random numbers, and they should all be numbers between 1 and 100. Great job!

Comparing the guess to the secret number

Now that we have user input and a random number, we can compare them. Replace the contents of Program.dbl with the following:

import System

main
proc
    stty(0)
    Console.WriteLine("Guess the number!")

    data random = new Random()
    data randomNumber = random.Next(1, 101) ; Generates a random number between 1 and 100

    Console.WriteLine("Please input your guess.")

    data guess = Console.ReadLine()

    data guessNumber, int, %integer(guess)
    if(guessNumber > randomNumber) then
        Console.WriteLine("Too big!")
    else if(guessNumber < randomNumber) then
        Console.WriteLine("Too small!")
    else
        Console.WriteLine("Correct!")
endmain

First, we’re calling integer and passing it the string we read off the console in order to convert it from a string to an int. In that same line, we’ve added a new variable, guessNumber, declared that it’s an int, and assigned its initial value. Next, we have a block of IF-ELSE statements. The if statement checks if guessNumber is greater than randomNumber. If it is, it prints “Too big!”. The else if statement checks if guessNumber is less than randomNumber. If it is, it prints “Too small!”. Finally, the else statement is a catch-all that prints “Correct!” if guessNumber is neither greater than nor less than randomNumber. If we hadn’t converted guess to an int, the compiler wouldn’t allow us to compare with randomNumber because they would be different types.

If you run the program now, you’ll see something like the following:

dotnet run
Guess the number!
Please input your guess.
55
Too small!

You might be tempted to experiment with inputting something other than a number to see what happens. Go ahead and try it. You’ll see something like this:

dotnet run
Guess the number!
Please input your guess.
dd
Unhandled exception. Synergex.SynergyDE.BadDigitException: Bad digit encountered
   at Synergex.SynergyDE.VariantDesc.ToLong()
   at Synergex.SynergyDE.SysRoutines.f_integer(VariantDesc parm1, NumericParam parm2)
   at _NS_GuessingGame._CL.MAIN$PROGRAM(String[] args)

Not very user-friendly! Let’s add some error handling to make this a better experience for the user. Replace the contents of Program.dbl with the following:

import System

main
proc
    stty(0)
    Console.WriteLine("Guess the number!")

    data random = new Random()
    data randomNumber = random.Next(1, 101) ; Generates a random number between 1 and 100

    Console.WriteLine("Please input your guess.")

    data guess = Console.ReadLine()

    try
    begin
        data guessNumber, i4, %integer(guess)
        if(guessNumber > randomNumber) then
            Console.WriteLine("Too big!")
        else if(guessNumber < randomNumber) then
            Console.WriteLine("Too small!")
        else
            Console.WriteLine("Correct!")
    end
    catch(ex, @Synergex.SynergyDE.BadDigitException)
    begin
        Console.WriteLine("Please type a number!")
    end
    endtry
endmain

We’ve added a try block around the code that converts the guess to an int and compares it to the randomNumber. We’ve also added a catch block that catches the BadDigitException and prints a more user friendly message. We could also have used Int.TryParse from .NET, but this way you can see some explicit error handling. If you run the program now and enter something other than a number, you’ll see something like this:

dotnet run
Guess the number!
Please input your guess.
dd
Please type a number!

Allowing multiple guesses with looping

Now that we have the basic game working, we can make it more interesting by allowing multiple guesses. To do this, we’ll use a repeat loop. The repeat loop continues until exitloop is executed.

Replace the contents of Program.dbl with the following:

import System

main
proc
    stty(0)
    Console.WriteLine("Guess the number!")

    data random = new Random()
    data randomNumber = random.Next(1, 101) ; Generates a random number between 1 and 100

    repeat
    begin
        Console.WriteLine("Please input your guess.")

        data guess = Console.ReadLine()

        try
        begin
            data guessNumber, i4, %integer(guess)
            if(guessNumber > randomNumber) then
                Console.WriteLine("Too big!")
            else if(guessNumber < randomNumber) then
                Console.WriteLine("Too small!")
            else
            begin
                Console.WriteLine("Correct!")
                exitloop
            end
        end
        catch(ex, @Synergex.SynergyDE.BadDigitException)
        begin
            Console.WriteLine("Please type a number!")
        end
        endtry
    end
endmain

We’ve added a repeat block around the entire chunk of code that gets the user’s guess and compares it to randomNumber. We’ve also made the else block into a compound statement so we can execute both Console.WriteLine and exitloop. Now if you run the program you’ll see something like the following:

dotnet run
Guess the number!
Please input your guess.
55
Too big!
Please input your guess.
25
Too big!
Please input your guess.
13
Too big!
Please input your guess.
6
Too big!
Please input your guess.
3
Correct!

And there you have it! A fully functioning guessing game. You can play it as many times as you want, and it will generate a new random number each time. You can also try to guess the number as many times as you’d like.

Summary

This project was a hands-on way to introduce you to some of the fundamentals of DBL. You learned about variables, looping, error handling, and conditional statements. In the next chapter, we’ll dive deeper into common programming concepts.