diff --git a/12_Bombs_Away/rust/Cargo.lock b/12_Bombs_Away/rust/Cargo.lock new file mode 100644 index 00000000..59dc11cc --- /dev/null +++ b/12_Bombs_Away/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/12_Bombs_Away/rust/Cargo.toml b/12_Bombs_Away/rust/Cargo.toml new file mode 100644 index 00000000..3b1d02f5 --- /dev/null +++ b/12_Bombs_Away/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.5" diff --git a/12_Bombs_Away/rust/src/main.rs b/12_Bombs_Away/rust/src/main.rs new file mode 100644 index 00000000..d049fd84 --- /dev/null +++ b/12_Bombs_Away/rust/src/main.rs @@ -0,0 +1,309 @@ +use std::{ + io::{self, Write}, + str::FromStr, +}; + +use rand::Rng; + +struct Italy; +struct Allies; +struct Japan; +struct Germany; + +#[derive(Debug, PartialEq, Eq)] +struct ParseChoiceTargetError; + +#[derive(PartialEq)] +enum ThreeTarget { + One, + Two, + Three, +} + +impl FromStr for ThreeTarget { + type Err = ParseChoiceTargetError; + + fn from_str(s: &str) -> Result { + let n = s.parse::().map_err(|_| ParseChoiceTargetError)?; + match n { + 1 => Ok(Self::One), + 2 => Ok(Self::Two), + 3 => Ok(Self::Three), + _ => Err(ParseChoiceTargetError), + } + } +} + +enum FourTarget { + One, + Two, + Three, + Four, +} + +impl FromStr for FourTarget { + type Err = ParseChoiceTargetError; + + fn from_str(s: &str) -> Result { + let n = s.parse::().map_err(|_| ParseChoiceTargetError)?; + match n { + 1 => Ok(Self::One), + 2 => Ok(Self::Two), + 3 => Ok(Self::Three), + 4 => Ok(Self::Four), + _ => Err(ParseChoiceTargetError), + } + } +} + +pub trait Brefing { + type TargetOption; + fn prompt<'a>(&self) -> &'a str; + fn targets_to_messages<'a>(&self, target: Self::TargetOption) -> &'a str; +} + +impl Brefing for Italy { + type TargetOption = ThreeTarget; + + fn prompt<'a>(&self) -> &'a str { + "YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)" + } + + fn targets_to_messages<'a>(&self, target: Self::TargetOption) -> &'a str { + match target { + ThreeTarget::One => "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.", + ThreeTarget::Two => "BE CAREFUL!!!", + ThreeTarget::Three => "YOU'RE GOING FOR THE OIL, EH?", + } + } +} + +impl Brefing for Allies { + type TargetOption = FourTarget; + + fn prompt<'a>(&self) -> &'a str { + "AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4): " + } + + fn targets_to_messages<'a>(&self, target: Self::TargetOption) -> &'a str { + match target { + FourTarget::One => "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.", + FourTarget::Two => "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.", + FourTarget::Three => "YOU'RE CHASING THE BISMARK IN THE NORTH SEA.", + FourTarget::Four => "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.", + } + } +} + +impl Brefing for Germany { + type TargetOption = ThreeTarget; + + fn prompt<'a>(&self) -> &'a str { + "A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? " + } + + fn targets_to_messages<'a>(&self, target: Self::TargetOption) -> &'a str { + match target { + ThreeTarget::One => "YOU'RE NEARING STALINGRAD.", + ThreeTarget::Two => "NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR.", + ThreeTarget::Three => "NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS.", + } + } +} + +enum Side { + Italy(Italy), + Allies(Allies), + Japan(Japan), + Germany(Germany), +} + +impl From for Side { + fn from(value: FourTarget) -> Self { + match value { + FourTarget::One => Self::Italy(Italy), + FourTarget::Two => Self::Allies(Allies), + FourTarget::Three => Self::Japan(Japan), + FourTarget::Four => Self::Germany(Germany), + } + } +} + +fn stdin_choice(prompt: &str) -> C { + let mut buffer = String::new(); + + print!("{prompt}"); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut buffer).unwrap(); + loop { + if let Ok(choice) = buffer.trim().parse::() { + return choice; + } + print!("TRY AGAIN..."); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut buffer).unwrap(); + } +} + +fn stdin_y_or_n(prompt: &str) -> bool { + let mut buffer = String::new(); + + print!("{prompt}"); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut buffer).unwrap(); + buffer.trim().to_uppercase() == "Y" +} + +fn commence_non_kamikazi_attack() { + let nmissions = loop { + let nmissions = stdin_choice::("HOW MANY MISSIONS HAVE YOU FLOWN? "); + if nmissions < 160 { + break nmissions; + } + println!("MISSIONS, NOT MILES..."); + println!("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS"); + print!("NOW THEN, "); + }; + + if nmissions >= 100 { + println!("THAT'S PUSHING THE ODDS!"); + } + + if nmissions < 25 { + println!("FRESH OUT OF TRAINING, EH?"); + } + + println!(); + + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + + if nmissions as f32 >= 160_f32 * y { + mission_success(); + } else { + mission_failure(); + } +} + +fn play_japan() { + if !stdin_y_or_n("YOUR FIRST KAMIKAZE MISSION? (Y OR N): ") { + player_death(); + return; + } + + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + if y > 0.65 { + mission_success(); + } else { + player_death(); + } +} + +fn player_death() { + println!("* * * * BOOM * * * *"); + println!("YOU HAVE BEEN SHOT DOWN....."); + println!("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR"); + println!("LAST TRIBUTE..."); +} + +fn mission_success() { + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + + let killed = (100f32 * y) as i32; + println!("DIRECT HIT!!!! {killed} KILLED."); + println!("MISSION SUCCESSFUL."); +} + +fn mission_failure() { + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + let miles = 2 + (30f32 * y) as i32; + println!("MISSED TARGET BY {miles} MILES!"); + println!("NOW YOU'RE REALLY IN FOR IT !!"); + println!(); + let enemy_weapons = + stdin_choice::("DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? "); + + let enemy_gunner_accuracy = if enemy_weapons != ThreeTarget::Two { + let m = loop { + let m = + stdin_choice::("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? "); + if m <= 50 { + break m; + } + println!("TRY AGAIN..."); + }; + if m < 10 { + println!("YOU LIE, BUT YOU'LL PAY..."); + player_death(); + return; + } + m + } else { + 0 + }; + + let missile_threat_weighting = if enemy_weapons == ThreeTarget::One { + 0 + } else { + 35 + }; + + let death = + death_with_chance((enemy_gunner_accuracy + missile_threat_weighting) as f32 / 100f32); + + if death { + player_death(); + } else { + player_survived(); + } +} + +fn player_survived() { + println!("YOU MADE IT THROUGH TREMENDOUS FLAK!!"); +} + +fn death_with_chance(p_death: f32) -> bool { + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + p_death > y +} + +fn main() { + loop { + println!("YOU ARE A PILOT IN A WORLD WAR II BOMBER."); + let side: Side = + stdin_choice::("WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ") + .into(); + + match side { + Side::Japan(_) => { + println!("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON."); + } + Side::Italy(ref s) => { + let target = stdin_choice(s.prompt()); + println!("{}", s.targets_to_messages(target)); + } + Side::Allies(ref s) => { + let target = stdin_choice(s.prompt()); + println!("{}", s.targets_to_messages(target)); + } + Side::Germany(ref s) => { + let target = stdin_choice(s.prompt()); + println!("{}", s.targets_to_messages(target)); + } + } + + match side { + Side::Japan(_) => play_japan(), + _ => commence_non_kamikazi_attack(), + } + + println!(); + if !stdin_y_or_n("ANOTHER MISSION? (Y OR N): ") { + break; + } + } +}