diff --git a/74_Rock_Scissors_Paper/rust/Cargo.toml b/74_Rock_Scissors_Paper/rust/Cargo.toml new file mode 100644 index 00000000..cfdc7abe --- /dev/null +++ b/74_Rock_Scissors_Paper/rust/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rock_scissors_paper" +version = "1.0.0" +edition = "2021" + +[dependencies] +text_io = "0.1.10" +strum = "0.24" +strum_macros = "0.24" +nanorand = "0.6.1" \ No newline at end of file diff --git a/74_Rock_Scissors_Paper/rust/README.md b/74_Rock_Scissors_Paper/rust/README.md new file mode 100644 index 00000000..50e88090 --- /dev/null +++ b/74_Rock_Scissors_Paper/rust/README.md @@ -0,0 +1,4 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Rust](https://www.rust-lang.org) + diff --git a/74_Rock_Scissors_Paper/rust/src/main.rs b/74_Rock_Scissors_Paper/rust/src/main.rs new file mode 100644 index 00000000..98511bff --- /dev/null +++ b/74_Rock_Scissors_Paper/rust/src/main.rs @@ -0,0 +1,147 @@ +/* + * Rock paper scissors + * Originally from the wonderful book: _Basic Computer Games_ + * Port to Rust By David Lotts +*/ +use nanorand::{tls::TlsWyRand, Rng}; +use std::io::{self, Write}; +use strum::EnumCount; +use strum_macros::{Display, EnumCount, FromRepr}; +use text_io::try_read; +fn main() { + let mut computer_wins = 0; + let mut human_wins = 0; + let mut rng = nanorand::tls_rng(); + println!("{:>21}", "GAME OF ROCK, SCISSORS, PAPER"); + println!("{:>15}", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); + print!("\n\n\n"); + // pass by reference in rust! input() modifies this variable. + let mut qty_games = 0; + loop { + input_int("HOW MANY GAMES", &mut qty_games); + if qty_games < 11 { + break; + } + println!("SORRY, BUT WE AREN'T ALLOWED TO PLAY THAT MANY."); + } + for game_number in 1..=qty_games { + println!(); + println!("GAME NUMBER {}", game_number); + let mut your_choice = 0; + loop { + println!("3=ROCK...2=SCISSORS...1=PAPER"); + input_int("1...2...3...WHAT'S YOUR CHOICE", &mut your_choice); + // interesting validation in original BASIC: IF (K-1)*(K-2)*(K-3)==0 + if (1..=3).contains(&your_choice) { + break; + } + println!("INVALID."); + } + // Convert number to enum. Note the type change. Really it is a new variable. + let your_choice = Choice::from_repr((your_choice - 1) as usize).unwrap(); + let my_choice = Choice::new_random(&mut rng); + + println!("THIS IS MY CHOICE..."); + println!("...{}", my_choice.to_string()); + let winner = Winner::decide_winner(my_choice, your_choice); + println!( + "{}", + match winner { + Winner::Tie => { + "TIE GAME. NO WINNER." + } + Winner::Computer => { + computer_wins = computer_wins + 1; + "WOW! I WIN!!!" + } + Winner::Human => { + human_wins = human_wins + 1; + "YOU WIN!!!" + } + } + ) + } + println!(); + println!("HERE IS THE FINAL GAME SCORE:"); + println!("I HAVE WON {} GAME(S).", computer_wins); + println!("YOU HAVE WON {} GAME(S).", human_wins); + println!( + "AND {} GAME(S) ENDED IN A TIE.", + qty_games - (computer_wins + human_wins) + ); + println!(); + println!("THANKS FOR PLAYING!!"); +} + +#[derive(FromRepr, Debug, PartialEq, EnumCount, Display)] +pub enum Choice { + PAPER, + SCISSORS, + ROCK, +} + +impl Choice { + /// Returns randomly selected paper..rock. + fn new_random(rng: &mut TlsWyRand) -> Choice { + Choice::from_repr(rng.generate_range(0..Choice::COUNT)).unwrap() + } +} + +#[derive(FromRepr, Debug, PartialEq, EnumCount)] +pub enum Winner { + Human, + Computer, + Tie, +} + +impl Winner { + /// take opponent's choices and decide the winner + // I really learned alot about enums here, and now you can too! + // Originally I broke this out for auto testing. + pub fn decide_winner(my_choice: Choice, your_choice: Choice) -> Winner { + let my_choice = my_choice as u8; + let your_choice = your_choice as u8; + if my_choice == your_choice { + return Winner::Tie; + } + // wordy but clear way: + // if (my_choice == 1 && your_choice == 3) || (my_choice > your_choice) + // consice but opaque way: + if 1 == (3 + my_choice - your_choice) % 3 { + return Winner::Computer; + } + return Winner::Human; + } +} + +/// print the prompt, wait for a number and newline. Loop if invalid. +fn input_int(prompt: &str, number: &mut i32) { + loop { + print!("{} ? ", prompt); + io::stdout().flush().unwrap(); + if let Ok(n) = try_read!() { + *number = n; + return; + } + } +} + +/// Test winner decider for every case. +#[cfg(test)] +mod tests { + #[test] + fn winner_test() { + use super::*; + use Choice::*; + use Winner::*; //decide_winner(my_choice=computer, your_choice=human) + assert_eq!(Winner::decide_winner(PAPER, PAPER), Tie); + assert_eq!(Winner::decide_winner(PAPER, SCISSORS), Human); + assert_eq!(Winner::decide_winner(PAPER, ROCK), Computer); + assert_eq!(Winner::decide_winner(SCISSORS, PAPER), Computer); + assert_eq!(Winner::decide_winner(SCISSORS, SCISSORS), Tie); + assert_eq!(Winner::decide_winner(SCISSORS, ROCK), Human); + assert_eq!(Winner::decide_winner(ROCK, PAPER), Human); + assert_eq!(Winner::decide_winner(ROCK, SCISSORS), Computer); + assert_eq!(Winner::decide_winner(ROCK, ROCK), Tie); + } +}