Programming a Guessing Game
In this chapter, we'll explore build a hands-on project to learn mana fundamentals.
Programming a Guessing Game
Let's learn Mana by building a classic beginner project: a guessing game. Here's how it works: the program generates a random number between 1 and 100, then prompts the player to guess. After each guess, the program indicates whether the guess is too low, too high, or correct.
Setting Up a New Project
Create a new project:
mana new guessing_game
cd guessing_gameOpen src/main.mana and replace its contents with:
module main
fn main() -> void {
println("Guess the number!")
}Run it with mana run to verify everything works.
Processing a Guess
First, let's ask for user input:
module main
use std.io
fn main() -> void {
println("Guess the number!")
println("Please input your guess.")
let mut guess = String::new()
io.stdin()
.read_line(guess)
.expect("Failed to read line")
println("You guessed: ", guess)
}Let's break this down:
Importing with use
use std.ioThis brings the io module from the standard library into scope, giving us input/output functionality.
Storing Values with Variables
let mut guess = String::new()This creates a mutable variable named guess bound to a new, empty string. The mut keyword makes it mutable—without it, variables are immutable by default.
Receiving User Input
io.stdin()
.read_line(guess)
.expect("Failed to read line")We call stdin() to get a handle to standard input, then read_line() to read a line into our string. The expect() handles potential errors—if reading fails, it prints our message and exits.
Variadic Print
println("You guessed: ", guess)Mana's println accepts multiple arguments of any type. The values are printed in sequence.
Generating a Secret Number
Now let's generate a random number. Add the rand dependency to mana.toml:
[dependencies]
rand = "0.8"Update src/main.mana:
module main
use std.io
use rand
fn main() -> void {
println("Guess the number!")
let secret_number = rand.gen_range(1, 101)
println("The secret number is: ", secret_number)
println("Please input your guess.")
let mut guess = String::new()
io.stdin()
.read_line(guess)
.expect("Failed to read line")
println("You guessed: ", guess)
}We generate a random number between 1 and 100 (the range is exclusive on the upper bound, so we use 101).
Comparing the Guess to the Secret Number
Now let's compare the guess to the secret number:
module main
use std.io
use rand
fn main() -> void {
println("Guess the number!")
let secret_number = rand.gen_range(1, 101)
println("Please input your guess.")
let mut guess = String::new()
io.stdin()
.read_line(guess)
.expect("Failed to read line")
// Convert string to number
let guess: int = guess.trim().parse()
.expect("Please type a number!")
println("You guessed: ", guess)
match guess.cmp(secret_number) {
Ordering.Less => println("Too small!"),
Ordering.Greater => println("Too big!"),
Ordering.Equal => println("You win!"),
}
}Converting Types
let guess: int = guess.trim().parse()
.expect("Please type a number!")We shadow the original guess variable with a new one. The trim() removes whitespace (including the newline from pressing Enter), and parse() converts the string to a number.
Pattern Matching
match guess.cmp(secret_number) {
Ordering.Less => println("Too small!"),
Ordering.Greater => println("Too big!"),
Ordering.Equal => println("You win!"),
}The match expression compares values against patterns. Here, cmp returns an Ordering value, and we handle each possibility.
Allowing Multiple Guesses with Looping
Let's add a loop so players can guess multiple times:
module main
use std.io
use rand
fn main() -> void {
println("Guess the number!")
let secret_number = rand.gen_range(1, 101)
loop {
println("Please input your guess.")
let mut guess = String::new()
io.stdin()
.read_line(guess)
.expect("Failed to read line")
let guess: int = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue
}
println("You guessed: ", guess)
match guess.cmp(secret_number) {
Ordering.Less => println("Too small!"),
Ordering.Greater => println("Too big!"),
Ordering.Equal => {
println("You win!")
break
}
}
}
}Quitting After a Correct Guess
When the player guesses correctly, we print a congratulatory message and break out of the loop.
Handling Invalid Input
Instead of crashing on invalid input, we use match on the parse() result:
- On
Ok(num), we extract and return the number - On
Err(_), wecontinueto the next iteration, ignoring the invalid input
The Complete Game
Here's our finished guessing game:
module main
use std.io
use rand
fn main() -> void {
println("Guess the number!")
let secret_number = rand.gen_range(1, 101)
loop {
println("Please input your guess.")
let mut guess = String::new()
io.stdin()
.read_line(guess)
.expect("Failed to read line")
let guess: int = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println("Please enter a valid number!")
continue
}
}
println("You guessed: ", guess)
match guess.cmp(secret_number) {
Ordering.Less => println("Too small!"),
Ordering.Greater => println("Too big!"),
Ordering.Equal => {
println("You win!")
break
}
}
}
}Summary
This project introduced many Mana concepts:
moduledeclarationslet,mut, and variablesusestatements for importing- Functions like
println(),stdin(),read_line() - Variadic print with multiple arguments
- Type conversion with
parse() matchexpressions for pattern matchingloop,break, andcontinuefor iteration- Error handling with
Result(Ok/Err)
Congratulations! You've built your first Mana program. Now let's explore these concepts in more depth in the following chapters.