|
| 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.) |
0 commit comments