mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-05 20:40:25 -08:00
Port 12_Bombs_Away to Rust
This commit is contained in:
75
12_Bombs_Away/rust/Cargo.lock
generated
Normal file
75
12_Bombs_Away/rust/Cargo.lock
generated
Normal file
@@ -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"
|
||||
9
12_Bombs_Away/rust/Cargo.toml
Normal file
9
12_Bombs_Away/rust/Cargo.toml
Normal file
@@ -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"
|
||||
309
12_Bombs_Away/rust/src/main.rs
Normal file
309
12_Bombs_Away/rust/src/main.rs
Normal file
@@ -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<Self, Self::Err> {
|
||||
let n = s.parse::<u8>().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<Self, Self::Err> {
|
||||
let n = s.parse::<u8>().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<FourTarget> 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<C: FromStr>(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::<C>() {
|
||||
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::<i32>("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::<ThreeTarget>("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::<i32>("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::<FourTarget>("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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user