Skip to main content

Overview

This project implements a classic Rock Paper Scissors game as an interactive command-line application. The game allows players to compete against the computer in unlimited rounds, with the computer making random choices using the rand crate.
This project demonstrates fundamental Rust concepts including user input handling, random number generation, pattern matching, and control flow.

What You’ll Learn

  • Reading and parsing user input with std::io
  • Generating random numbers with the rand crate
  • Using loops for continuous gameplay
  • Pattern matching with match expressions
  • Error handling with unwrap_or_else
  • String manipulation and comparison

Running the Game

1

Navigate to the project directory

cd 02-rock_paper_scissors
2

Run the game

cargo run
3

Play the game

Enter 1, 2, or 3 to make your choice, or type “exit” to quit.

Example Gameplay

Please Enter a number
1 for 🗿 Rock!
2 for 📄 Paper!
3 for ✂️  Scissors!
1
Victory — your move beats the computer. computer chose ✂️  Scissors
------------------------------------------------
Please Enter a number
1 for 🗿 Rock!
2 for 📄 Paper!
3 for ✂️  Scissors!
2
Defeat — the computer outplayed you. computer chose ✂️  Scissors
------------------------------------------------
Please Enter a number
1 for 🗿 Rock!
2 for 📄 Paper!
3 for ✂️  Scissors!
exit

Dependencies

The project uses the rand crate for generating random computer choices:
Cargo.toml
[dependencies]
rand = "0.9.2"
The rand crate is one of the most popular Rust crates for random number generation. Version 0.9.2 introduces the random_range function for cleaner syntax.

Code Architecture

Main Game Loop

The game runs in an infinite loop, continuously prompting for user input until the player types “exit”:
fn main() {
    loop {
        let mut user_input = String::new();
        let computer = rand::random_range(1..=3);
        println!("Please Enter a number");
        println!("1 for 🗿 Rock!");
        println!("2 for 📄 Paper!");
        println!("3 for ✂️  Scissors!");

        // read the input
        io::stdin().read_line(&mut user_input).unwrap();
        user_input = user_input.trim().to_lowercase();

        if user_input == String::from("exit") {
            break;
        }

        let user_input: i8 = user_input.parse().unwrap_or_else(|error| {
            println!("{error}");
            0
        });

        println!("{}", check_values(&computer, &user_input));

        println!("------------------------------------------------");
    }
}
Key components:
  • loop creates an infinite loop that continues until break is called
  • rand::random_range(1..=3) generates a random number between 1 and 3 (inclusive)
  • io::stdin().read_line() reads user input from the terminal
  • trim().to_lowercase() cleans the input by removing whitespace and converting to lowercase
  • parse().unwrap_or_else() attempts to parse the string as an integer, defaulting to 0 on error
  • The game checks for “exit” to break out of the loop

Checking Values and Displaying Results

The check_values function determines the game outcome and formats the result message:
fn check_values(computer: &i8, user: &i8) -> String {
    let emoji = match computer {
        1 => "🗿 Rock",
        2 => "📄 Paper",
        3 => "✂️  Scissors",
        _ => "Invalid Move by Computer",
    };
    if user == computer {
        format!("Draw — both players chose the {emoji}")
    } else if check_game(computer, user) {
        format!("Victory — your move beats the computer. computer chose {emoji}")
    } else {
        format!("Defeat — the computer outplayed you. computer chose {emoji}")
    }
}
The match expression is used to map numeric values to their corresponding emojis and names. The underscore _ pattern handles any unexpected values.

Game Logic

The check_game function implements the Rock Paper Scissors rules:
fn check_game(computer: &i8, user: &i8) -> bool {
    if (*computer == 3 && *user == 1)
        || (*computer == 2 && *user == 3)
        || (*computer == 1 && *user == 2)
    {
        true
    } else {
        false
    }
}
Win conditions:
  • Rock (1) beats Scissors (3)
  • Scissors (3) beats Paper (2)
  • Paper (2) beats Rock (1)
The function uses the dereference operator * to access the actual values from the references passed as parameters.

Rust Concepts Demonstrated

let mut user_input = String::new();
io::stdin().read_line(&mut user_input).unwrap();
user_input = user_input.trim().to_lowercase();

Key Takeaways

1

Infinite Loops with Exit Conditions

The loop keyword creates an infinite loop, and the break statement exits when the user types “exit”.
2

Input Validation

Using unwrap_or_else provides graceful error handling when parsing user input, defaulting to 0 for invalid entries.
3

Pattern Matching

The match expression provides a clean way to handle multiple conditions and map values to results.
4

Reference Borrowing

Functions use references (&i8) to borrow values without taking ownership, allowing the main function to retain access.
5

String Formatting

The format! macro creates formatted strings with variable interpolation using {variable} syntax.

Possible Enhancements

Add counters to track the number of wins, losses, and draws throughout the game session.
let mut wins = 0;
let mut losses = 0;
let mut draws = 0;
Improve error handling to re-prompt the user when invalid input is entered instead of defaulting to 0.
let user_input: i8 = match user_input.parse() {
    Ok(num) if num >= 1 && num <= 3 => num,
    _ => {
        println!("Invalid input! Please enter 1, 2, or 3.");
        continue;
    }
};
Allow players to type “rock”, “paper”, or “scissors” instead of just numbers.
let choice = match user_input.as_str() {
    "rock" | "1" => 1,
    "paper" | "2" => 2,
    "scissors" | "3" => 3,
    _ => 0,
};
Implement a tournament mode where the player competes in a best-of-3 or best-of-5 series.

Next Steps

After completing this project, you can:
  • Add more complex game modes (best-of-3, tournaments)
  • Implement score tracking and statistics
  • Create a Rock Paper Scissors Lizard Spock variant
  • Build a graphical interface using a crate like iced or egui
  • Add multiplayer support over a network
This project provides a solid foundation in Rust basics. The concepts learned here—input handling, random generation, and pattern matching—are essential for more complex applications.