Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust implementation of 24_Chemist #815

Merged
merged 1 commit into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
");
}