use console::Term;
use rand::Rng;
use std::io::{self, Write};
fn main() {
title("Number Guessing Game");
let max_number: i32 = 10;
let random_number: i32 = rand::rng().random_range(1..=max_number);
let player_input: String =
input("Guess my number between 0 and {}!: ".replace("{}", &max_number.to_string()));
let player_input_new: i32 = player_input.parse().unwrap_or(-1);
if player_input_new == -1 || player_input_new > max_number || player_input_new < 0 {
println!("Pick a actual number between 0 and {}.", max_number);
exit();
} else {
println!(
"Your guess was {}. My number was {}. {} win!",
player_input_new,
random_number,
if player_input_new == random_number {
"You"
} else {
"I"
}
);
exit();
}
}
pub fn input<T: std::fmt::Display>(prompt: T) -> String {
print!("{}", prompt);
io::stdout().flush().unwrap();
let mut line: String = String::new();
io::stdin().read_line(&mut line).unwrap();
line.trim().to_string()
}
fn exit() {
let stdout: Term = Term::buffered_stdout();
println!("Press any key to exit...");
stdout.read_char().expect("");
}
fn title(name: &str) {
let stdout: Term = Term::buffered_stdout();
stdout.set_title(name);
}
I assume you want feedback, so maybe specify a bit what exactly you want feedback on, e. g. “is this code idiomatic?“.
This way people don‘t need to guess what you want feedback on and thus can give you more concise (and less general) answers.
Don't make input generic. It's great if you know now how trait bounds work, but you just don't need this here. prompt can be just a &str.
You define a max_number, but have the min_number hard coded. As a result, you ask your user to enter a number between 0 and max_number, but the randomly generated number is between 1 and max_number - that a little bit cheating
You can define a range for the allowed numbers let allowed_numbers = 0..10; and then use the range...
When calling input, you can use the format! macro instead of replacing.
... = input(&format!(
"Guess my number between {} and {}",
allowed_numbers.start(),
allowed_numbers.end())
)
You compare player_input_new == -1 and player_input_new < 0 - that is redundant
That one is bad in my opinion: You are assigning some magic error code (-1). That is not required in Rust. You can do the whole parsing and comparison / validation much nicer:
if let Ok(player_input) = player_input.parse()
&& allowed_numbers.contains(player_input) {
... input is valid - this is the former else branch
} else {
... input is not valid
exit() is called at the end of both branches. It might be just me, but things like this always confuse me. It could come after the if-statement, but I always assume I might overlook something, hence I'd call this also bad.
Your current code is densely packed. Give it some room to breathe and make it more readable.
use console::Term;
use rand::Rng;
use std::io::{self, Write};
fn main() {
title("Number Guessing Game");
let max_number: i32 = 10;
let random_number: i32 = rand::rng().random_range(1..=max_number);
let player_input: String =
input("Guess my number between 0 and {}!: ".replace("{}", &max_number.to_string()));
let player_input_new: i32 = player_input.parse().unwrap_or(-1);
if player_input_new == -1 || player_input_new > max_number || player_input_new < 0 {
println!("Pick a actual number between 0 and {}.", max_number);
exit();
} else {
println!(
"Your guess was {}. My number was {}. {} win!",
player_input_new,
random_number,
if player_input_new == random_number {
"You"
} else {
"I"
}
);
exit();
}
}
pub fn input<T: std::fmt::Display>(prompt: T) -> String {
print!("{}", prompt);
io::stdout().flush().unwrap();
let mut line: String = String::new();
io::stdin().read_line(&mut line).unwrap();
line.trim().to_string()
}
fn exit() {
let stdout: Term = Term::buffered_stdout();
println!("Press any key to exit...");
stdout.read_char().expect("");
}
fn title(name: &str) {
let stdout: Term = Term::buffered_stdout();
stdout.set_title(name);
}
Avoid unnecessary duplications.
In the control flow of
if player_input_new == -1 || player_input_new > max_number || player_input_new < 0 {
println!("Pick a actual number between 0 and {}.", max_number);
exit();
} else {
println!(
"Your guess was {}. My number was {}. {} win!",
player_input_new,
random_number,
if player_input_new == random_number {
"You"
} else {
"I"
}
);
exit();
}
both branches end with exit();. So you can move this out of the branches:
if player_input_new == -1 || player_input_new > max_number || player_input_new < 0 {
println!("Pick a actual number between 0 and {}.", max_number);
} else {
println!(
"Your guess was {}. My number was {}. {} win!",
player_input_new,
random_number,
if player_input_new == random_number {
"You"
} else {
"I"
}
);
}
exit();
Remove code that serves no real purpose
Your function exit() serves no real purpose. We don't need user interaction to terminate the program. Remove it and the refactored call of it from the code base.
let Ok(player_input) = input("Guess my number between 0 and {}!: ".replace("{}", &max_number.to_string())) else {
println!("Pick a actual number between 0 and {}.", max_number);
return;
}
println!(
"Your guess was {}. My number was {}. {} win!",
player_input,
random_number,
if player_input_new == random_number {
"You"
} else {
"I"
}
);
Bonus: You can also read a generic type in a loop until the player inputs a valid value:
Choose your types wisely.
You want numbers from 1 to 10, so you neither need a signed integer, nor 32 bits. Use a u8 instead of a i32.
Bonus: Implement a custom integer type that upholds the invariant of being within the range 1..=10.