From ba5ee8b915babb428a7a8008b1bf11a8ef4e83ae Mon Sep 17 00:00:00 2001 From: David Date: Mon, 28 Feb 2022 01:26:27 -0500 Subject: [PATCH 1/3] completed rust code for 74 Rock paper scissors --- 74_Rock_Scissors_Paper/rust/Cargo.toml | 9 +++ 74_Rock_Scissors_Paper/rust/README.md | 4 ++ 74_Rock_Scissors_Paper/rust/src/main.rs | 96 +++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 74_Rock_Scissors_Paper/rust/Cargo.toml create mode 100644 74_Rock_Scissors_Paper/rust/README.md create mode 100644 74_Rock_Scissors_Paper/rust/src/main.rs diff --git a/74_Rock_Scissors_Paper/rust/Cargo.toml b/74_Rock_Scissors_Paper/rust/Cargo.toml new file mode 100644 index 00000000..5f4b4b85 --- /dev/null +++ b/74_Rock_Scissors_Paper/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rock_scissors_paper" +version = "1.0.0" +edition = "2021" + +[dependencies] +text_io = "0.1.10" +#num-integer = "0.1" +rand = "0.8.5" \ 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..e32f373e --- /dev/null +++ b/74_Rock_Scissors_Paper/rust/src/main.rs @@ -0,0 +1,96 @@ +/* + * Rock paper scissors + * Port from book _Basic Computer Games_ + * By David Lotts +*/ +use rand::Rng; +use std::io::{self, Write}; +use text_io::{try_read}; +fn main() { + let mut computer_wins = 0; + let mut human_wins = 0; + let mut rng = rand::thread_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 num_games_q. + 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 my_choice: i32 = rng.gen_range(1..=3); // basic: X=INT(RND(1)*3+1) + 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."); + } + println!("THIS IS MY CHOICE..."); + println!( + "...{}", + match my_choice { + 1 => "PAPER", + 2 => "SCISSORS", + 3 => "ROCK", + _ => "um, what?", + } + ); + if my_choice == your_choice { + //THEN 250 + println!("TIE GAME. NO WINNER."); + } else { + //if (my_choice == 1 && your_choice == 3) + // || (my_choice > your_choice) + if 1 == (3 + my_choice - your_choice) % 3 + { + println!("WOW! I WIN!!!"); + computer_wins = computer_wins + 1; + } else { + println!("YOU WIN!!!"); + human_wins = human_wins + 1; + } + } + } + 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!!"); +} + +fn input_int(prompt: &str, number: &mut i32) { + loop { + print!("{} ? ", prompt); + io::stdout().flush().unwrap(); + match try_read!() { + Ok(n) => { + *number = n; + return; + } + Err(_) => println!("{}", prompt), + } + } +} + + +// TODO break out the winner decider and test it. +#[cfg(test)] +mod tests { + #[test] + fn winner_test() { + assert_eq!(true,true); + } +} \ No newline at end of file From bbfe11764a47a745e06e90601ec641c898064f1b Mon Sep 17 00:00:00 2001 From: David Lotts Date: Wed, 2 Mar 2022 03:28:42 -0500 Subject: [PATCH 2/3] Added test, and enums -- slick, but kind of gratuitous --- 74_Rock_Scissors_Paper/rust/Cargo.toml | 4 +- 74_Rock_Scissors_Paper/rust/src/main.rs | 119 +++++++++++++++++------- 2 files changed, 86 insertions(+), 37 deletions(-) diff --git a/74_Rock_Scissors_Paper/rust/Cargo.toml b/74_Rock_Scissors_Paper/rust/Cargo.toml index 5f4b4b85..c07af4e5 100644 --- a/74_Rock_Scissors_Paper/rust/Cargo.toml +++ b/74_Rock_Scissors_Paper/rust/Cargo.toml @@ -6,4 +6,6 @@ edition = "2021" [dependencies] text_io = "0.1.10" #num-integer = "0.1" -rand = "0.8.5" \ No newline at end of file +rand = "0.8.5" +strum = "0.24" +strum_macros = "0.24" \ No newline at end of file diff --git a/74_Rock_Scissors_Paper/rust/src/main.rs b/74_Rock_Scissors_Paper/rust/src/main.rs index e32f373e..899c235f 100644 --- a/74_Rock_Scissors_Paper/rust/src/main.rs +++ b/74_Rock_Scissors_Paper/rust/src/main.rs @@ -1,11 +1,13 @@ /* * Rock paper scissors - * Port from book _Basic Computer Games_ - * By David Lotts + * Originally from the wonderful book: _Basic Computer Games_ + * Port to Rust By David Lotts */ -use rand::Rng; +use rand::{prelude::ThreadRng, Rng}; //Large code. Switch to https://crates.io/crates/oorandom use std::io::{self, Write}; -use text_io::{try_read}; +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; @@ -25,10 +27,10 @@ fn main() { for game_number in 1..=qty_games { println!(); println!("GAME NUMBER {}", game_number); - let my_choice: i32 = rng.gen_range(1..=3); // basic: X=INT(RND(1)*3+1) + //let my_choice: i32 = rng.gen_range(1..=3); // basic: X=INT(RND(1)*3+1) let mut your_choice = 0; loop { - println!("3=ROCK...2=SCISSORS...1=PAPER"); + 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) { @@ -36,61 +38,106 @@ fn main() { } 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!( - "...{}", - match my_choice { - 1 => "PAPER", - 2 => "SCISSORS", - 3 => "ROCK", - _ => "um, what?", + println!("...{}", my_choice.to_string()); + let winner = Winner::decide_winner(my_choice, your_choice); + println!("{}", match winner { + Winner::Tie => { + "TIE GAME. NO WINNER." } - ); - if my_choice == your_choice { - //THEN 250 - println!("TIE GAME. NO WINNER."); - } else { - //if (my_choice == 1 && your_choice == 3) - // || (my_choice > your_choice) - if 1 == (3 + my_choice - your_choice) % 3 - { - println!("WOW! I WIN!!!"); - computer_wins = computer_wins + 1; - } else { - println!("YOU WIN!!!"); - human_wins = human_wins + 1; + 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!( + "AND {} GAME(S) ENDED IN A TIE.", + qty_games - (computer_wins + human_wins) + ); println!(); println!("THANKS FOR PLAYING!!"); } +#[derive(FromRepr, Debug, PartialEq, EnumCount, Display)] +enum Choice { + PAPER, + SCISSORS, + ROCK, +} + +impl Choice { + /// Returns randomly selected paper..rock. + fn new_random(rng: &mut ThreadRng) -> Choice { + Choice::from_repr((rng).gen_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. + 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(); - match try_read!() { - Ok(n) => { + if let Ok(n) = try_read!() { *number = n; return; - } - Err(_) => println!("{}", prompt), } } } - // TODO break out the winner decider and test it. #[cfg(test)] mod tests { #[test] fn winner_test() { - assert_eq!(true,true); + use super::*; + use Winner::*; + use Choice::*; //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); } -} \ No newline at end of file +} From 143e4428e40f028245138880f88f0cf4b5317353 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 3 Mar 2022 02:02:37 -0500 Subject: [PATCH 3/3] Switch to nanorandom because small and includes random seed. Ready to Pull request. --- 74_Rock_Scissors_Paper/rust/Cargo.toml | 5 +- 74_Rock_Scissors_Paper/rust/src/main.rs | 78 +++++++++++++------------ 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/74_Rock_Scissors_Paper/rust/Cargo.toml b/74_Rock_Scissors_Paper/rust/Cargo.toml index c07af4e5..cfdc7abe 100644 --- a/74_Rock_Scissors_Paper/rust/Cargo.toml +++ b/74_Rock_Scissors_Paper/rust/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] text_io = "0.1.10" -#num-integer = "0.1" -rand = "0.8.5" strum = "0.24" -strum_macros = "0.24" \ No newline at end of file +strum_macros = "0.24" +nanorand = "0.6.1" \ No newline at end of file diff --git a/74_Rock_Scissors_Paper/rust/src/main.rs b/74_Rock_Scissors_Paper/rust/src/main.rs index 899c235f..98511bff 100644 --- a/74_Rock_Scissors_Paper/rust/src/main.rs +++ b/74_Rock_Scissors_Paper/rust/src/main.rs @@ -3,7 +3,7 @@ * Originally from the wonderful book: _Basic Computer Games_ * Port to Rust By David Lotts */ -use rand::{prelude::ThreadRng, Rng}; //Large code. Switch to https://crates.io/crates/oorandom +use nanorand::{tls::TlsWyRand, Rng}; use std::io::{self, Write}; use strum::EnumCount; use strum_macros::{Display, EnumCount, FromRepr}; @@ -11,11 +11,11 @@ use text_io::try_read; fn main() { let mut computer_wins = 0; let mut human_wins = 0; - let mut rng = rand::thread_rng(); + 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 num_games_q. + // pass by reference in rust! input() modifies this variable. let mut qty_games = 0; loop { input_int("HOW MANY GAMES", &mut qty_games); @@ -27,7 +27,6 @@ fn main() { for game_number in 1..=qty_games { println!(); println!("GAME NUMBER {}", game_number); - //let my_choice: i32 = rng.gen_range(1..=3); // basic: X=INT(RND(1)*3+1) let mut your_choice = 0; loop { println!("3=ROCK...2=SCISSORS...1=PAPER"); @@ -39,23 +38,28 @@ fn main() { 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 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." + 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!!!" + } } - 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:"); @@ -70,7 +74,7 @@ fn main() { } #[derive(FromRepr, Debug, PartialEq, EnumCount, Display)] -enum Choice { +pub enum Choice { PAPER, SCISSORS, ROCK, @@ -78,8 +82,8 @@ enum Choice { impl Choice { /// Returns randomly selected paper..rock. - fn new_random(rng: &mut ThreadRng) -> Choice { - Choice::from_repr((rng).gen_range(0..Choice::COUNT)).unwrap() + fn new_random(rng: &mut TlsWyRand) -> Choice { + Choice::from_repr(rng.generate_range(0..Choice::COUNT)).unwrap() } } @@ -94,19 +98,19 @@ 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. - fn decide_winner(my_choice: Choice, your_choice: Choice) -> Winner { + 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 { + 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::Computer; } - return Winner::Human + return Winner::Human; } } @@ -116,28 +120,28 @@ fn input_int(prompt: &str, number: &mut i32) { print!("{} ? ", prompt); io::stdout().flush().unwrap(); if let Ok(n) = try_read!() { - *number = n; - return; + *number = n; + return; } } } -// TODO break out the winner decider and test it. +/// Test winner decider for every case. #[cfg(test)] mod tests { #[test] fn winner_test() { - use super::*; - use Winner::*; - use Choice::*; //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); + 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); } }