Skip to content

Commit fb83db3

Browse files
committed
feat(guessing_game): implement main game logic and user input handling
1 parent c14895e commit fb83db3

4 files changed

Lines changed: 379 additions & 2 deletions

File tree

projects/guessing_game/Cargo.lock

Lines changed: 133 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

projects/guessing_game/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ version = "0.1.0"
44
edition = "2024"
55

66
[dependencies]
7+
rand = "0.8.5"

projects/guessing_game/README.md

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Guessing Game
2+
3+
A command-line number guessing game that demonstrates fundamental Rust concepts including user input, control flow, error handling, and type conversion.
4+
5+
## Application Summary
6+
7+
The guessing game is an interactive program where:
8+
1. The program generates a random secret number between 1 and 10
9+
2. The player is prompted to guess the number
10+
3. The program provides feedback ("Too low", "Too high", or "Correct")
11+
4. The game continues until the player guesses correctly
12+
13+
## Key Rust Concepts Demonstrated
14+
15+
### 1. External Crate and Standard Library Usage
16+
17+
The program uses:
18+
- `rand` crate for random number generation
19+
- `std::io` module from the standard library for input/output
20+
21+
```rust
22+
use rand::Rng;
23+
use std::io;
24+
```
25+
26+
**Note**: In Rust terminology:
27+
- **Crate**: A compilation unit (like a library or binary)
28+
- **Module**: A namespace within a crate (like `io` within the `std` crate)
29+
- **Package**: A Cargo concept containing one or more crates
30+
31+
The `rand` crate is an external dependency, while `std` is the standard library crate.
32+
33+
### 2. Variable Definitions
34+
35+
```rust
36+
let secret_number = rand::thread_rng().gen_range(1..=10); // Immutable random number
37+
let mut guess = String::new(); // Mutable variable (note the 'mut' keyword)
38+
```
39+
40+
- **Immutable by default**: `secret_number` cannot be changed once set
41+
- **Random number generation**: `thread_rng()` creates a random number generator, `gen_range(1..=10)` generates a number from 1 to 10 (inclusive)
42+
- **Mutable with `mut`**: `guess` can be modified to accept user input
43+
- **Shadowing**: The variable `guess` is shadowed when converted from `String` to `u32`
44+
45+
**Understanding Shadowing:**
46+
47+
Shadowing is different from type casting or reassignment in other languages. When you shadow a variable in Rust, you declare a *new* variable with the same name:
48+
49+
```rust
50+
let mut guess = String::new(); // guess is a String
51+
io::stdin().read_line(&mut guess);
52+
53+
let guess: u32 = guess.trim().parse() // NEW variable named guess (u32)
54+
.expect("..."); // shadows the String version
55+
```
56+
57+
The new `guess` (type `u32`) completely shadows the old `guess` (type `String`). The original String variable still exists in memory but is now inaccessible.
58+
59+
**Checking Variable Types:**
60+
61+
You can verify variable types in several ways:
62+
63+
1. **Using `std::any::type_name` (runtime inspection)**:
64+
```rust
65+
fn type_of<T>(_: &T) -> &'static str {
66+
std::any::type_name::<T>()
67+
}
68+
69+
let guess = String::new();
70+
println!("Type: {}", type_of(&guess)); // alloc::string::String
71+
72+
let guess: u32 = 42;
73+
println!("Type: {}", type_of(&guess)); // u32
74+
```
75+
76+
2. **Intentional compiler error (development trick)**:
77+
```rust
78+
let guess = String::new();
79+
let () = guess; // Compiler error shows actual type
80+
```
81+
82+
3. **Using the `dbg!` macro**:
83+
```rust
84+
dbg!(&guess); // Shows variable name, location, and value
85+
```
86+
87+
4. **IDE hover** - rust-analyzer shows type information on hover
88+
89+
### 3. Control Flow
90+
91+
**Infinite Loop with `loop`**:
92+
```rust
93+
loop {
94+
// Game logic repeats until 'break' is called
95+
}
96+
```
97+
98+
**Pattern Matching with `match`**:
99+
```rust
100+
let guess: u32 = match guess.trim().parse() {
101+
Ok(num) => num,
102+
Err(_) => {
103+
println!("Please enter a valid number!");
104+
continue;
105+
}
106+
};
107+
```
108+
- Handles the `Result` type from `parse()`
109+
- `Ok(num)` - successful parse, extract the number
110+
- `Err(_)` - parse failed, prompt again
111+
112+
**Conditional Logic with `if/else if/else`**:
113+
```rust
114+
if guess < secret_number {
115+
println!("Too low! Try again.");
116+
} else if guess > secret_number {
117+
println!("Too high! Try again.");
118+
} else {
119+
println!("Congratulations!");
120+
break; // Exit the loop
121+
}
122+
```
123+
124+
**Early Loop Continuation with `continue`**:
125+
- Used for input validation (invalid number or out of bounds)
126+
- Skips the rest of the loop iteration and starts over
127+
128+
### 4. Error Handling
129+
130+
**Method 1: `expect()` for critical errors**:
131+
```rust
132+
.expect("Failed to read line");
133+
```
134+
- Program panics (crashes) if reading from stdin fails
135+
136+
**Method 2: `match` for recoverable errors**:
137+
```rust
138+
match guess.trim().parse() {
139+
Ok(num) => num,
140+
Err(_) => continue,
141+
}
142+
```
143+
- Gracefully handles invalid input without crashing
144+
145+
### 5. Type Conversion
146+
147+
**String to integer conversion**:
148+
```rust
149+
let guess: u32 = guess.trim().parse()...
150+
```
151+
- `.trim()` removes whitespace (including newline)
152+
- `.parse()` converts string to the specified type (`u32`)
153+
- Type annotation (`: u32`) tells Rust what type to parse into
154+
155+
### 6. User Input
156+
157+
**Reading from standard input**:
158+
```rust
159+
io::stdin()
160+
.read_line(&mut guess)
161+
```
162+
- `stdin()` returns a handle to standard input
163+
- `read_line()` appends input to the mutable string reference `&mut guess`
164+
165+
## Building and Running
166+
167+
**Run the game**:
168+
```bash
169+
cargo run
170+
```
171+
172+
**Build without running**:
173+
```bash
174+
cargo build
175+
```
176+
177+
**Build optimized release version**:
178+
```bash
179+
cargo build --release
180+
```
181+
182+
## Example Gameplay
183+
184+
```
185+
Welcome to the Guessing Game!
186+
Please enter your guess (a number between 1 and 10):
187+
5
188+
Too high! Try again.
189+
3
190+
Too low! Try again.
191+
4
192+
Congratulations! You guessed the correct number: 4
193+
```
194+
195+
## Future Enhancements
196+
197+
- Add a guess counter to track attempts
198+
- Implement difficulty levels with different number ranges (easy: 1-10, medium: 1-50, hard: 1-100)
199+
- Add option to play multiple rounds
200+
- Display statistics (average guesses, best score, etc.)

projects/guessing_game/src/main.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
1-
fn main() {
2-
println!("Hello, world!");
1+
use rand::Rng;
2+
use std::io;
3+
use std::any::type_name;
4+
5+
fn type_of<T>(_: &T) -> &'static str {
6+
type_name::<T>()
37
}
8+
9+
fn main() {
10+
println!("Welcome to the Guessing Game!");
11+
println!("Please enter your guess (a number between 1 and 10):");
12+
13+
let secret_number = rand::thread_rng().gen_range(1..=10);
14+
15+
loop {
16+
let mut guess = String::new();
17+
18+
io::stdin()
19+
.read_line(&mut guess)
20+
.expect("Failed to read line");
21+
println!("Type before parse: {}", type_of(&guess));
22+
23+
let guess: u32 = match guess.trim().parse() {
24+
Ok(num) => num,
25+
Err(_) => {
26+
println!("Please enter a valid number!");
27+
continue;
28+
}
29+
};
30+
println!("Type after parse: {}", type_of(&guess));
31+
32+
if guess < 1 || guess > 10 {
33+
println!("Your guess is out of bounds! Please guess a number between 1 and 10.");
34+
continue;
35+
}
36+
37+
if guess < secret_number {
38+
println!("Too low! Try again.");
39+
} else if guess > secret_number {
40+
println!("Too high! Try again.");
41+
} else {
42+
println!("Congratulations! You guessed the correct number: {}", secret_number);
43+
break;
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)