Thirty Days of Rust: Day Four
Sammy Shear
Posted on January 15, 2022
Today was more of a relaxed day for me, instead of putting together a whole new project I just added the ability to lose into my hangman game. This was way simpler than I thought it would be (I'm not really sure why, but I expected this to take a while), so I don't really have that much to share, but I will share it.
Final Code
// /src/main.rs
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::io;
fn main() {
let mut rng = thread_rng();
let possible_words = [
"Hangman",
"Challenge",
"Hello, World!",
"Programming",
"Rust",
"Thirty Days",
"Day Three",
];
let word_to_guess = possible_words.choose(&mut rng).unwrap().to_string();
let mut guessed_letters: Vec<char> = vec![];
let mut current_guess = String::new();
for i in 0..word_to_guess.chars().count() {
if &word_to_guess.chars().nth(i).unwrap() == &' ' {
current_guess.push_str(" ");
} else {
current_guess.push_str("_");
}
}
let mut guesses_left: i32 = 8;
while current_guess != word_to_guess && guesses_left > 0 {
println!("{}", ¤t_guess);
println!("Make a guess");
println!("Guessed Letters: {:?}", guessed_letters);
let mut guess = String::new();
io::stdin().read_line(&mut guess).unwrap();
if !guessed_letters.contains(&guess.chars().nth(0).unwrap()) {
guessed_letters.push(guess.chars().nth(0).unwrap());
let mut new_guess = current_guess.chars().collect::<Vec<_>>();
for i in 0..word_to_guess.len() {
if word_to_guess.chars().collect::<Vec<_>>()[i]
.eq_ignore_ascii_case(&guess.chars().nth(0).unwrap())
{
new_guess[i] = word_to_guess.chars().collect::<Vec<_>>()[i];
} else {
continue;
}
}
if new_guess.iter().collect::<String>() == current_guess {
guesses_left -= 1;
}
current_guess = new_guess.iter().collect::<String>();
println!("Guesses left: {:?}", guesses_left);
} else {
println!("You already guessed that");
continue;
}
}
if current_guess == word_to_guess {
println!("You won! Guesses: {:?}", guessed_letters.len());
println!("{}", current_guess);
} else {
println!("You lose! Guesses: {:?}", guessed_letters.len())
}
}
It's basically the same as yesterday, but with a few things added. Notably, a variable to keep track of how many guesses are left, an extra condition for the while loop, and a way to check if the guess was wrong or not:
...
let mut guesses_left: i32 = 8;
...
while current_guess != word_to_guess && guesses_left > 0
...
if new_guess.iter().collect::<String>() == current_guess {
guesses_left -= 1;
}
current_guess = new_guess.iter().collect::<String>();
...
The last part is also a slight change to what I was doing before, which was to immediately assign the current_guess
to the new_guess
instead of waiting until I checked the validity of the guess.
Problem Solving
The first way I went about this was just by getting rid of the continue;
in that else statement and replacing it with the guesses_left -= 1;
line. The problem with this is that it runs each for loop iteration, so that was a no go. Not thinking too hard about it, I changed guesses_left
to be dependant on the length of word_to_guess
, which sort of solved one issue but raised another one. The line was still being called every iteration, so instead of only using up "one" guess if the guess was wrong, it used up one guess every time a letter wasn't every letter in the string, which was all the time. That's why I had to bring the logic out of the for loop, and instead of relying on that else statement, I just waited to reassign current_guess
to the value of new_guess
until after I had made a check to see if anything was correct.
That's about it for me today, and thanks for reading.
Posted on January 15, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.