Rust implementation of 92 Trap

This commit is contained in:
Uğur Küpeli
2022-10-04 18:30:30 +03:00
parent c1893239da
commit c18a1004a5
2 changed files with 80 additions and 186 deletions

View File

@@ -1,155 +0,0 @@
/*
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 speed_train_1;
let mut time_difference;
let mut speed_train_2;
let mut guess;
let mut answer;
let mut error:f32;
//Game loop
loop {
//initialize variables
speed_train_1 = rng.gen_range(40..65);
time_difference = rng.gen_range(5..20);
speed_train_2 = rng.gen_range(20..39);
//print starting message / conditions
println!("A CAR TRAVELING {} MPH CAN MAKE A CERTAIN TRIP IN\n{} HOURS LESS THAN A TRAIN TRAVELING AT {} MPH",speed_train_1,time_difference,speed_train_2);
println!();
//get guess
guess = loop {
match get_number_from_input("HOW LONG DOES THE TRIP TAKE BY CAR?",0,-1) {
Ok(num) => break num,
Err(err) => {
eprintln!("{}",err);
continue;
},
}
};
//calculate answer and error
answer = time_difference * speed_train_2 / (speed_train_1 - speed_train_2);
error = ((answer - guess) as isize).abs() as f32 * 100.0/(guess as f32) + 0.5;
//check guess against answer
if error > 5.0 {
println!("SORRY, YOU WERE OFF BY {} PERCENT.", error);
println!("CORRECT ANSWER IS {} HOURS.",answer);
} else {
println!("GOOD! ANSWER WITHIN {} PERCENT.", error);
}
//ask user if they want to go again
match get_string_from_user_input("ANOTHER PROBLEM (Y/N)") {
Ok(s) => if !s.to_uppercase().eq("Y") {break;} else {continue;},
_ => break,
}
}
//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()),
}
}

View File

@@ -1,41 +1,90 @@
use std::process;//allows for some better error handling
use std::io::stdin;
mod lib; //allows access to lib.rs
use lib::Config;
use rand::Rng;
/// 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();
println!("\n\t\tTRAP");
println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
// set up other configuration
let mut config = Config::new().unwrap_or_else(|err| {
eprintln!("Problem configuring program: {}", err);
process::exit(1);
});
let max_guess = 6;
let max_number = 100;
// 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
prompt_instructions();
loop {
let number = rand::thread_rng().gen_range(1..(max_number + 1));
let mut guesses = 1u8;
loop {
let (min, max) = prompt_numbers(guesses);
if min == number && max == number {
println!("\nYou got it!!!");
break;
} else if (min..=max).contains(&number) {
println!("You have trapped my number.");
} else if number < min {
println!("My number is smaller than your trap numbers.");
} else if number > max {
println!("My number is bigger than your trap numbers.");
}
guesses += 1;
if guesses > max_guess {
println!("\nSorry, that was {max_guess} guesses. Number was {number}");
break;
}
}
println!("\nTry again.");
}
//end of program
println!("THANKS FOR PLAYING!");
}
/// print the welcome message
fn welcome() {
println!("
Train
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
fn prompt_instructions() {
println!("Instructions?\t");
TIME - SPEED DISTANCE EXERCISE
");
let mut input = String::new();
if let Ok(_) = stdin().read_line(&mut input) {
match input.to_uppercase().trim() {
"YES" | "Y" => {
println!("\nI am thinking of a number between 1 and 100");
println!("Try to guess my number. On each guess,");
println!("you are to enter 2 numbers, trying to trap");
println!("my number between the two numbers. I will");
println!("tell you if you have trapped my number, if my");
println!("number is larger than your two numbers, or if");
println!("my number is smaller than your two numbers.");
println!("If you want to guess one single number, type");
println!("your guess for both your trap numbers.");
println!("You get 6 guesses to get my number.");
}
_ => (),
}
}
}
fn prompt_numbers(guess: u8) -> (u8, u8) {
loop {
let mut nums: Vec<u8> = Vec::new();
println!("\nGuess # {guess} ?");
let mut input = String::new();
if let Ok(_) = stdin().read_line(&mut input) {
let input: Vec<&str> = input.trim().split(",").collect();
for string in input {
if let Ok(number) = string.parse::<u8>() {
nums.push(number);
} else {
break;
}
}
if nums.len() == 2 {
if nums[0] <= nums[1] {
return (nums[0], nums[1]);
}
}
}
}
}