Make a Back-End Number Guessing Game with Rust
Tags: rust, showdev
Published: 2021-02-28 18:30:59

---
title: Make a Back-End Number Guessing Game with Rust
published: true
description: A look into introductory concepts in Rust and learning them while developing an interesting guessing game.
tags: rust, showdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xd438rcsi5ypkg2p112u.jpg
canonical_url: https://jamesinkala.com/blog/make-a-back-end-number-guessing-game-with-rust/
---

I started learning Rust a while back and in good faith I've decided to share the experience with the community.

This is a game for Rust beginner and pros alike but more so to the beginners as it provides a good starting point to practice and understand some few Rust concepts. I'm a Rust beginner so if I get it then you can too.

## The Game ##
In this game the player gets to fill in a lower inclusive limit and and a higher exclusive limit of a range of numbers where the number to be guessed will be based on. The game should create a random ***secret_number*** which the player will be trying to guess afterwards.

Before creating the game you'll need to [install Rust](https://www.rust-lang.org/tools/install) into your system.

To simplify setting up Rust projects we'll need the support of Cargo.
So what's Cargo?

### Cargo ###
Cargo is the Rust package manager. It downloads and compiles Rust package's dependencies, compiles packages, makes distributable packages and uploads them to crates.io, the Rust community’s package registry.
As far as this game is concerned we'll use Cargo to manage our project, download and compile the project dependencies, in this case [rand](https://crates.io/crates/rand) a crate we'll need to generate the game's *secret_number*. Well, a 'crate' is simply a Rust package.

[Install Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) so that you can be able to use it in the game.
Read [The Cargo Book](https://doc.rust-lang.org/cargo/index.html) if you need to learn more about it.


### Start a New Cargo Package ###
After installation of both Rust and Cargo, open up your terminal and run the following command inside your projects folder:
```sh
# create new guessing-game package
$ cargo new guessing-game --bin

# then switch into the guessing-game directory
$ cd guessing-game
```

The above command will generate the Cargo configuration file, a src directory and a main.rs file inside it. The _--bin_ flag that we pass on the __cargo new__ command, tells Cargo that we intend to create a direct executable file as opposed to a library.

The resulting directory structure.
```sh
.
├── Cargo.toml
└── src
    └── main.rs
```

#### A breakdown of the project's template.####
**Cargo.toml**: This is a Cargo configuration file that contains all the metadata that Cargo needs to compile the package. All the dependencies go below [dependencies].
These are the details inside the file:
```toml
[package]
name = "guessing-game"
version = "0.1.0"
authors = ["Your Name <email@whoever.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
```
**src/main.rs** file: Cargo expects the Rust package's source files to be in the src directory leaving the package's root directory for README's, licence information and all other files not related to a package's code. The main.rs file is the package entry file containing the main() function:
```rs
fn main() {
    println!("Hello, world!");
}
```
__fn__: is used to declare functions in Rust, followed by the function's name. The main() function is the beginning of every Rust program.
__println!()__: is a Rust [macro](https://doc.rust-lang.org/book/ch19-06-macros.html) that's used to print things to the terminal.

To see what this code results to run the following code on the package's root:
```sh
$ cargo run

#results to - Hello, world!
```
If everything was set up correctly you should see the - "___Hello, world!___" output on the terminal

### Creating The Game ###
Let's proceed with creating our game.
Add the following lines of code to the __main.rs__ file:
```rs
use std::io;

fn main(){
    println!("'Guess The Number' Game!");
    println!("In this game you'll be guessing the secret number between a range of numbers, the lower being inclusive while the higher being exclusive. To start provide the lower and higher limits of the range of numbers.");
}
```
Since the game involves getting user input, we'll use the io [___prelude___](https://doc.rust-lang.org/std/io/prelude/index.html) from Rust's standard library to handle this. Next We'll print on the console information about the game and instructions on how to play it.

```rs
    let mut _lower_limit = String::new();
    
    println!("Choose the lower limit (number > 0): ");

    io::stdin().read_line(&mut _lower_limit)
    .expect("Failed to read value");

    let _lower_limit: u32 = match _lower_limit.trim().parse() {
        Ok(num) => num,
        Err(_) => 1,
    };
```
In the above code, we are binding the variable ***_lower_limit*** to an empty string on the first line. The **let** statement in Rust enables us to bind **patterns** (left side of the let statement) to a value (right side of a let statement) as opposed to most languages where we assign values to a variable name on the left side, this means we can have a let statement like 
```rs
let (a, b, c) = (19, 16, 31);
```
which will result to __a__ being _19_, __b__ being _16_ and __c__ being _31_.

In Rust variable bindings are immutable, that is after we have created a ___variable binding___ `let foo = bar;` we can not change the value of __foo__ unless we declare that the binding we are intending to create should be mutable by adding "___mut___" resulting to `let mut foo = bar;`.
The __String::new()__ above as provided by Rust's standard library provides an empty ___String___ type value to bind to ***_lower_limit***.

Then we proceed to taking input from the terminal by using the .read_line() method called on the Stdin() handle which we get from the io::stdin() function.
The __read_line()__ method puts what is typed into the ***&mut _lower_limit*** String that we pass into it and returns an __io::Result__.
Rust has a number of types name __Result__ types in it's standard library and their purpose is to encode error handling information, also the types resulting values have methods defined on them.

For our __io::Result__ type the __expect()__ method which takes the value it's called on crashes the program displaying the message that's passed into it in case the value is unsuccessful.
Without calling the expect() method the program will compile displaying a warning in case of an error and likely resulting in unwanted results.

In Rust we can shadow a previously assigned variable and this is helpful in a case where we probably need to change the type of a variable without the need to binding it into a new varible. This is demonstrated above where we initially binded `_lower_limit` as a String `let mut _lower_limit = String::new();` then proceeded to shadowing it as an unsigned 32 bit integer with `let _lower_limit: u32`.

To make sure we bind the varible to a number since we'll need numbers to make a range of numbers (who thought?) we call on the initially String bound variable _lower_limit `_lower_limit.trim().parse()` where the __trim()__ method trims the white spaces at the start and end of the String then call on it the __parse()__ method which parses Strings into some kind of number where in this case we hint to it the type of number we want the bunding to be, which is an unsigned 32 bit integer - `let _lower_limit: u32 = `.

To make sure we have a valid number and not crash our program on user input we'll bind a default number on ***_lower_limit***. And we can achieve that by with the help of a match statement.  
```rs
    let _lower_limit: u32 = match _lower_limit.trim().parse() {
        Ok(num) => num,
        Err(_) => 1,
    };
```
If a number is returned after `_lower_limit.trim().parse()` we return that with the `Ok(num) => num` and in case of anything else we bind ***_lower_limit*** to our default number 1 instead of crashing the program.

We replicate the same process above for the **_higher_limit**.

```rs
    let mut _higher_limit = String::new();
    
    println!("Choose the higher limit (number > _lower_limit + 1): ");

    io::stdin().read_line(&mut _higher_limit)
    .expect("Failed to read value");

    let _higher_limit: u32 = match _higher_limit.trim().parse() {
        Ok(num) => num,
        Err(_) => 101,
    };
```

Next we generate our random number that is between the two values provided above.
We do that by first adding the ___rand___ crate we talked about before in the Cargo.toml file under dependencies.
```toml
[dependencies]
rand = "0.8.4"
```

Run the ___Cargo check___ command.
```bash
cargo check
```
Cargo will download the needed dependencies and give us a status output.

We then intergrate the external crate by adding it at the start of our code and using it's __rand::Rng__ trait.
```rs
extern crate rand;

use rand::Rng;
```

```rs
    let secret_number = rand::thread_rng().gen_range(_lower_limit.._higher_limit);
```
We use the `rand::thread_rng()` method which copies the random number generator local to the thread of execution our program is in and we call the __gen_range()__ method provided by the __rand::Rng__ trait which takes a range of numbers (numeric range created as ___start..end___ in Rust) and returns a number that is lower bound inclusive but exclusive on the upper bound.

We then proceed to the part where the player guesses the secret number.
On this part the game loops the instructions until the player gets the correct number, that is in case the player doesn't guess the secret number we give them another opportunity to guess it again. On the process we bind the count of times they guessed the number to the variable ***_moves_made*** to be used later.

```rs
    let mut _moves_made: u32 = 0;

    loop {
        println!("Input guess: ");

        let mut _guess = String::new();

        io::stdin()
            .read_line(&mut _guess)
            .expect("Failed to read input!!!");

        let _guess: u32 = match _guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("You guessed the number: {}", _guess);

        _moves_made += 1;

        match _guess.cmp(&secret_number) {
            Ordering::Less  => println!("Too small!!"),
            Ordering::Greater  => println!("Too big!!"),
            Ordering::Equal  => {
                let total_moves = _higher_limit - _lower_limit;
                let total_moves_float = total_moves as f64;
                let moves_made_float = _moves_made as f64;
                let efficiency: f64 = ((total_moves_float - moves_made_float) / total_moves_float) * 100.00;
                println!("YOU HAVE WON!! \n Guesses Made: {}\n Possible Guesses: {}\n Efficiency: {}%" , _moves_made, total_moves, efficiency);
                break;
            }
        }
    }

```

To compare the numbers, in this case the player's input and the secret number we need to add another type into scope, the - `std::cmp::Ordering;`. We add the following at the start of our code.
```rs
use std::cmp::Ordering;
```
Since we need to compare two numbers, we call the __cmp()__ method which compares the thing it's called upon to the refference of the thing one wants to compared it to __var1.comp(&refferenceToVar2)__ returning the __Ordering__ type. We use the match statement to compare the player's guess to the secret number and check the type of Ordering that is returned. If it is __Ordering::Equal__ then we display the desired output and break the loop ending the game otherwise we notify the player whether the guess is above or below the secret number. This last part is specifically added to reduce the difficulty of the game, eliminating it kicks the game's difficulty up a notch.

Hopefully this is just the start on sharing what I learn and create on my Rust adventures, probably some of you will get to join in.
Go ahead and conquer the terminal with Rust.