mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 15:37:51 -08:00
Merge pull request #815 from AnthonyMichaelTDM/main
Rust implementation of 24_Chemist
This commit is contained in:
9
24_Chemist/rust/Cargo.toml
Normal file
9
24_Chemist/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"
|
||||
3
24_Chemist/rust/README.md
Normal file
3
24_Chemist/rust/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
|
||||
158
24_Chemist/rust/src/lib.rs
Normal file
158
24_Chemist/rust/src/lib.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
lib.rs contains all the logic of the program
|
||||
*/
|
||||
use rand::{Rng, prelude::thread_rng}; //rng
|
||||
use std::error::Error; //better errors
|
||||
use std::io::{self, Write}; //io interactions
|
||||
use std::{str::FromStr, fmt::Display}; //traits
|
||||
|
||||
//DATA
|
||||
|
||||
/// handles setup for the game
|
||||
pub struct Config {
|
||||
}
|
||||
impl Config {
|
||||
/// creates and returns a new Config from user input
|
||||
pub fn new() -> Result<Config, Box<dyn Error>> {
|
||||
//DATA
|
||||
let config: Config = Config {
|
||||
};
|
||||
|
||||
//return new config
|
||||
return Ok(config);
|
||||
}
|
||||
}
|
||||
|
||||
/// run the program
|
||||
pub fn run(_config: &Config) -> Result<(), Box<dyn Error>> {
|
||||
//DATA
|
||||
let mut rng = thread_rng();
|
||||
let mut lives: i8 = 9;
|
||||
|
||||
let mut amount_of_acid:i8;
|
||||
|
||||
let mut guess:f32;
|
||||
let mut answer:f32;
|
||||
|
||||
let mut error:f32;
|
||||
|
||||
//Game loop
|
||||
loop {
|
||||
//initialize variables
|
||||
amount_of_acid = rng.gen_range(1..50);
|
||||
answer = 7.0 * (amount_of_acid as f32/3.0);
|
||||
|
||||
//print starting message / conditions
|
||||
println!();
|
||||
//get guess
|
||||
guess = loop {
|
||||
match get_number_from_input(&format!("{} Liters of Kryptocyanic acid. How much water? ", amount_of_acid),0.0,-1.0) {
|
||||
Ok(num) => break num,
|
||||
Err(err) => {
|
||||
eprintln!("{}",err);
|
||||
continue;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
//calculate error
|
||||
error = (answer as f32 - guess).abs() / guess;
|
||||
|
||||
println!("answer: {} | error: {}%", answer,error*100.);
|
||||
|
||||
//check guess against answer
|
||||
if error > 0.05 { //error > 5%
|
||||
println!(" Sizzle! You may have just been desalinated into a blob");
|
||||
println!(" of quivering protoplasm!");
|
||||
//update lives
|
||||
lives -= 1;
|
||||
|
||||
if lives <= 0 {
|
||||
println!(" Your 9 lives are used, but you will be long remembered for");
|
||||
println!(" your contributions to the field of comic book chemistry.");
|
||||
break;
|
||||
}
|
||||
else {
|
||||
println!(" However, you may try again with another life.")
|
||||
}
|
||||
} else {
|
||||
println!(" Good job! You may breathe now, but don't inhale the fumes!");
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
//return to main
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// gets a string from user input
|
||||
fn get_string_from_user_input(prompt: &str) -> Result<String, Box<dyn Error>> {
|
||||
//DATA
|
||||
let mut raw_input = String::new();
|
||||
|
||||
//print prompt
|
||||
print!("{}", prompt);
|
||||
//make sure it's printed before getting input
|
||||
io::stdout().flush().expect("couldn't flush stdout");
|
||||
|
||||
//read user input from standard input, and store it to raw_input, then return it or an error as needed
|
||||
raw_input.clear(); //clear input
|
||||
match io::stdin().read_line(&mut raw_input) {
|
||||
Ok(_num_bytes_read) => return Ok(String::from(raw_input.trim())),
|
||||
Err(err) => return Err(format!("ERROR: CANNOT READ INPUT!: {}", err).into()),
|
||||
}
|
||||
}
|
||||
/// generic function to get a number from the passed string (user input)
|
||||
/// pass a min lower than the max to have minimum and maximum bounds
|
||||
/// pass a min higher than the max to only have a minimum bound
|
||||
/// pass a min equal to the max to only have a maximum bound
|
||||
///
|
||||
/// Errors:
|
||||
/// no number on user input
|
||||
fn get_number_from_input<T:Display + PartialOrd + FromStr>(prompt: &str, min:T, max:T) -> Result<T, Box<dyn Error>> {
|
||||
//DATA
|
||||
let raw_input: String;
|
||||
let processed_input: String;
|
||||
|
||||
|
||||
//input loop
|
||||
raw_input = loop {
|
||||
match get_string_from_user_input(prompt) {
|
||||
Ok(input) => break input,
|
||||
Err(e) => {
|
||||
eprintln!("{}",e);
|
||||
continue;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
//filter out non-numeric characters from user input
|
||||
processed_input = raw_input.chars().filter(|c| c.is_numeric()).collect();
|
||||
|
||||
//from input, try to read a number
|
||||
match processed_input.trim().parse() {
|
||||
Ok(i) => {
|
||||
//what bounds must the input fall into
|
||||
if min < max { //have a min and max bound: [min,max]
|
||||
if i >= min && i <= max {//is input valid, within bounds
|
||||
return Ok(i); //exit the loop with the value i, returning it
|
||||
} else { //print error message specific to this case
|
||||
return Err(format!("ONLY BETWEEN {} AND {}, PLEASE!", min, max).into());
|
||||
}
|
||||
} else if min > max { //only a min bound: [min, infinity)
|
||||
if i >= min {
|
||||
return Ok(i);
|
||||
} else {
|
||||
return Err(format!("NO LESS THAN {}, PLEASE!", min).into());
|
||||
}
|
||||
} else { //only a max bound: (-infinity, max]
|
||||
if i <= max {
|
||||
return Ok(i);
|
||||
} else {
|
||||
return Err(format!("NO MORE THAN {}, PLEASE!", max).into());
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_e) => return Err(format!("Error: couldn't find a valid number in {}",raw_input).into()),
|
||||
}
|
||||
}
|
||||
46
24_Chemist/rust/src/main.rs
Normal file
46
24_Chemist/rust/src/main.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use std::process;//allows for some better error handling
|
||||
|
||||
mod lib; //allows access to lib.rs
|
||||
use lib::Config;
|
||||
|
||||
/// main function
|
||||
/// responsibilities:
|
||||
/// - Calling the command line logic with the argument values
|
||||
/// - Setting up any other configuration
|
||||
/// - Calling a run function in lib.rs
|
||||
/// - Handling the error if run returns an error
|
||||
fn main() {
|
||||
//greet user
|
||||
welcome();
|
||||
|
||||
// set up other configuration
|
||||
let mut config = Config::new().unwrap_or_else(|err| {
|
||||
eprintln!("Problem configuring program: {}", err);
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
// run the program
|
||||
if let Err(e) = lib::run(&mut config) {
|
||||
eprintln!("Application Error: {}", e); //use the eprintln! macro to output to standard error
|
||||
process::exit(1); //exit the program with an error code
|
||||
}
|
||||
|
||||
//end of program
|
||||
println!("THANKS FOR PLAYING!");
|
||||
}
|
||||
|
||||
/// print the welcome message
|
||||
fn welcome() {
|
||||
println!("
|
||||
Chemist
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
|
||||
|
||||
The fictitious chemical kryptocyanic acid can only be
|
||||
diluted by the ratio of 7 parts water to 3 parts acid.
|
||||
If any other ratio is attempted, the acid becomes unstable
|
||||
and soon explodes. Given the amount of acid, you must
|
||||
decide how much water to add for dilution. If you miss
|
||||
you face the consequences.
|
||||
");
|
||||
}
|
||||
Reference in New Issue
Block a user