Skip to content

Commit

Permalink
Merge pull request #815 from AnthonyMichaelTDM/main
Browse files Browse the repository at this point in the history
Rust implementation of 24_Chemist
  • Loading branch information
coding-horror committed Oct 5, 2022
2 parents 72dd9f4 + d20a578 commit c7ed271
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 0 deletions.
9 changes: 9 additions & 0 deletions 24_Chemist/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions 24_Chemist/rust/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 158 additions & 0 deletions 24_Chemist/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 46 additions & 0 deletions 24_Chemist/rust/src/main.rs
Original file line number Diff line number Diff line change
@@ -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.
");
}

0 comments on commit c7ed271

Please sign in to comment.