|
|
|
|
@ -0,0 +1,268 @@ |
|
|
|
|
use std::{fmt::{Display,Formatter, self}, thread, sync::{mpsc::{channel, Sender}}}; |
|
|
|
|
use num_cpus; |
|
|
|
|
|
|
|
|
|
fn main() { |
|
|
|
|
|
|
|
|
|
// These will hold the users response to thier max acitivity level
|
|
|
|
|
let mut n_push_ups = String::new(); |
|
|
|
|
let mut n_squats = String::new(); |
|
|
|
|
let mut n_sit_ups = String::new(); |
|
|
|
|
let mut n_miles = String::new(); |
|
|
|
|
let mut n_pages = String::new(); |
|
|
|
|
let mut days_left = String::new(); |
|
|
|
|
|
|
|
|
|
println!("How many push ups could you do in 1 day, if that was the only activity you were doing?:"); |
|
|
|
|
std::io::stdin().read_line(&mut n_push_ups).unwrap(); |
|
|
|
|
println!("How many squats could you do in 1 day, if that was the only activity you were doing?:"); |
|
|
|
|
std::io::stdin().read_line(&mut n_squats).unwrap(); |
|
|
|
|
println!("How many sit ups could you do in 1 day, if that was the only activity you were doing?:"); |
|
|
|
|
std::io::stdin().read_line(&mut n_sit_ups).unwrap(); |
|
|
|
|
println!("How many miles could you run in 1 day, if that was the only activity you were doing?:"); |
|
|
|
|
std::io::stdin().read_line(&mut n_miles).unwrap(); |
|
|
|
|
println!("How many pages could you read in 1 day, if that was the only activity you were doing?:"); |
|
|
|
|
std::io::stdin().read_line(&mut n_pages).unwrap(); |
|
|
|
|
println!("How many days left in the competeition += 1.0:"); |
|
|
|
|
std::io::stdin().read_line(&mut days_left).unwrap(); |
|
|
|
|
|
|
|
|
|
// All answers need to be trimed to remove \n and then parsed in to f64
|
|
|
|
|
let days = days_left.trim().parse().unwrap(); |
|
|
|
|
let push_ups = Activity::new(0.2, n_push_ups.trim().parse::<f64>().unwrap(), "PushUps".to_string()); |
|
|
|
|
let squats = Activity::new(1.0/7.5,n_squats.trim().parse::<f64>().unwrap(), "Squats".to_string()); |
|
|
|
|
let sit_ups = Activity::new(0.2,n_sit_ups.trim().parse::<f64>().unwrap(), "SitUps".to_string()); |
|
|
|
|
let running = Activity::new(20.0,n_miles.trim().parse::<f64>().unwrap(), "Miles".to_string()); |
|
|
|
|
let reading = Activity::new(0.5,n_pages.trim().parse::<f64>().unwrap(), "Pages".to_string()); |
|
|
|
|
|
|
|
|
|
// This actitivty list will be cloned and used to store the activity level of each routine
|
|
|
|
|
// This will mean we can easily calc the score and energy use of different routines
|
|
|
|
|
let act_list = [push_ups, squats, sit_ups, running, reading]; |
|
|
|
|
// We want to use as many cores as possible to speed things up
|
|
|
|
|
let n_cpus = num_cpus::get(); |
|
|
|
|
|
|
|
|
|
// This channel allows us to communicate data between threads.
|
|
|
|
|
// a new transmitter will be passed to each thread so that it can send
|
|
|
|
|
// score and routine data to the processor thread
|
|
|
|
|
let (tx,rx) = channel(); |
|
|
|
|
// ub: Upper Bound | This way we can split up the work for different starting number of reps
|
|
|
|
|
let ub = act_list[0].max_reps_per_day as usize; |
|
|
|
|
// This tells us how many combinations each thread should be responsible for
|
|
|
|
|
let inc = ub/n_cpus; |
|
|
|
|
// Here we keep track of our threads so that we can easily join them all later
|
|
|
|
|
let mut threads = vec![]; |
|
|
|
|
// No assign out the top level rep segments to the threads
|
|
|
|
|
for i in 0..n_cpus { |
|
|
|
|
// Each thread gets its own reciever back to the main thread
|
|
|
|
|
let tx = tx.clone(); |
|
|
|
|
// An acitity list with 0 completed acitities is given to each clone
|
|
|
|
|
let act = act_list.clone(); |
|
|
|
|
// Spawn a thread to check a segment of possible routines
|
|
|
|
|
threads.push(thread::spawn(move || thread_task(tx,act,[i*inc,(i*inc)+inc],days))) |
|
|
|
|
} |
|
|
|
|
// Drop the main tranmitter so that the reciver knows the no more data is coming after the last thread drops it's
|
|
|
|
|
// scoped transmitter
|
|
|
|
|
drop(tx); |
|
|
|
|
// Completed will hold the 'winning' routines sent back from the task threads
|
|
|
|
|
let mut completed = vec![]; |
|
|
|
|
// While we are still recieveing data from the other threads do...
|
|
|
|
|
while let Ok(act) = rx.recv() { |
|
|
|
|
// Add this routine to the completed list
|
|
|
|
|
completed.push(act); |
|
|
|
|
// We can easily use up 64gb of mem so we need to limit the number of routines on this list
|
|
|
|
|
if completed.len() > 50 { |
|
|
|
|
// Sort the completed list by score
|
|
|
|
|
completed.sort_by_key(|(s,_a)| *s as u32); |
|
|
|
|
// Reverse it so that the lowest scores are at the back of the vec
|
|
|
|
|
completed.reverse(); |
|
|
|
|
println!("\nCurrent Best: {} | {}\n{:?}", completed[0].0, check_energy_used_up(&completed[0].1).1,completed[0].1); |
|
|
|
|
// Delete the lowest 25 scores
|
|
|
|
|
for _ in 25..50 { |
|
|
|
|
completed.pop(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Wait for all of the threads to finish
|
|
|
|
|
for t in threads { |
|
|
|
|
t.join().unwrap(); |
|
|
|
|
}
|
|
|
|
|
// Sort the completed list by score
|
|
|
|
|
completed.sort_by_key(|(s,_a)| *s as u32); |
|
|
|
|
// Reverse it so that the lowest scores are at the back of the vec
|
|
|
|
|
completed.reverse(); |
|
|
|
|
|
|
|
|
|
if completed.len() > 0 { |
|
|
|
|
println!("\nHighest Routine: Score:{} | Energy: {} | Bonus: {}", completed[0].0, check_energy_used_up(&completed[0].1).1, compute_bonus_points(&completed[0].1, days)); |
|
|
|
|
for act in &completed[0].1 { |
|
|
|
|
println!("{}", act); |
|
|
|
|
} |
|
|
|
|
energy_effiecnt(completed,days) |
|
|
|
|
} else {println!("Activity level too low. All scores less than 750.")} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn thread_task(sender: Sender<(f64,[Activity;5])>, mut act_list: [Activity;5], p_bound: [usize;2], days: f64) { |
|
|
|
|
// Give avg_points a starting place so that not everything is being submitted to the best routine list
|
|
|
|
|
let mut avg_points = 750.0; |
|
|
|
|
let mut routines = 0.0; |
|
|
|
|
// Check every combination of pushups within the defined range in reverse order (highest first)
|
|
|
|
|
for p in (p_bound[0]..p_bound[1]+1).rev() { |
|
|
|
|
// Since this is our first activity energy is at 100
|
|
|
|
|
let energy = 100.0; |
|
|
|
|
// Make sure there are no completed pushups stored in the activity list
|
|
|
|
|
act_list[0].reset(); |
|
|
|
|
// Add p completed reps to the acitity list and record how much energy is being used
|
|
|
|
|
let eng_p = act_list[0].do_activity(p); |
|
|
|
|
// Check what is the maximum number of squats that can be done with our remaining energy.
|
|
|
|
|
let max_sq = act_list[1].possible_reps(energy - eng_p); |
|
|
|
|
// Repeat the above for the rest of the activities
|
|
|
|
|
/* |
|
|
|
|
Note: This may seem ripe for recursion, but due to the memory ownership strucutre of Rust, that quickly becomes impractical |
|
|
|
|
*/ |
|
|
|
|
for sq in (0..max_sq+1).rev() { |
|
|
|
|
|
|
|
|
|
act_list[1].reset(); |
|
|
|
|
let eng_sq = act_list[1].do_activity(sq) + eng_p; |
|
|
|
|
let max_su = act_list[2].possible_reps(energy - eng_sq ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for su in (0..max_su+1).rev() { |
|
|
|
|
|
|
|
|
|
act_list[2].reset(); |
|
|
|
|
let eng_su = act_list[2].do_activity(su) + eng_sq; |
|
|
|
|
let max_mi = act_list[3].possible_reps(energy - eng_su); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for mi in (0..max_mi+1).rev() { |
|
|
|
|
|
|
|
|
|
act_list[3].reset(); |
|
|
|
|
let eng_mi = act_list[3].do_activity(mi) + eng_su; |
|
|
|
|
let max_pg = act_list[4].possible_reps(energy - eng_mi); |
|
|
|
|
|
|
|
|
|
for pg in (0..max_pg+1).rev() { |
|
|
|
|
|
|
|
|
|
act_list[4].reset(); |
|
|
|
|
act_list[4].do_activity(pg); |
|
|
|
|
// Check the score this routine would earch and include any bonus that may be earned
|
|
|
|
|
let score = (check_score(&act_list) * days) + compute_bonus_points(&act_list, days); |
|
|
|
|
//println!("{p}/{} | {sq}/{} | {su}/{} | {mi}/{} | {pg}/{} | Score: {score}", p_bound[1], max_sq,max_su, max_mi, max_pg);
|
|
|
|
|
// If we scored better than average send that score to the main thread
|
|
|
|
|
if score > avg_points {
|
|
|
|
|
sender.send((score, act_list.clone())).unwrap(); |
|
|
|
|
// Increase the min score needed to be sent to the main thread
|
|
|
|
|
routines += 1.0; |
|
|
|
|
avg_points = avg_points + score / routines |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} println!("Thread {p_bound:?} finished!"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn check_energy_used_up(act_list: &[Activity;5]) -> (bool, f64) { |
|
|
|
|
let mut used = 0.0; |
|
|
|
|
for act in act_list { |
|
|
|
|
used += act.energy_used(); |
|
|
|
|
if used > 100.0 { return (true, used) } |
|
|
|
|
} (false, used) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn check_score(act_list: &[Activity;5]) -> f64 { |
|
|
|
|
let mut total_points = 0.0; |
|
|
|
|
for act in act_list { |
|
|
|
|
total_points += act.points_earned(); |
|
|
|
|
} |
|
|
|
|
total_points |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn compute_bonus_points(routine: &[Activity;5], days: f64) -> f64 { |
|
|
|
|
/* |
|
|
|
|
This function is a mess and built very statically for the competition I', involved in |
|
|
|
|
*/ |
|
|
|
|
if routine[0].completed * days > 3000.0 && routine[1].completed * days >= 3000.0 && routine[2].completed * days >= 3000.0 && routine[3].completed * days >= 60.0 && routine[4].completed * days >= 225.0 { |
|
|
|
|
//println!("\n\n++MAX BONUS ACHEIEVED!");
|
|
|
|
|
return 2000.0 + 1000.0 + 600.0 + 350.0 + 200.0 +100.0
|
|
|
|
|
} else if routine[0].completed * days >= 2000.0 && routine[1].completed * days >= 2000.0 && routine[2].completed * days >= 2000.0 && routine[3].completed * days >= 40.0 && routine[4].completed * days >= 150.0 { |
|
|
|
|
return 1000.0 + 600.0 + 350.0 + 200.0 +100.0
|
|
|
|
|
} else if routine[0].completed * days >= 1500.0 && routine[1].completed * days >= 1500.0 && routine[2].completed * days >= 1500.0 && routine[3].completed * days >= days && routine[4].completed * days >= 100.0 { |
|
|
|
|
return 600.0 + 350.0 + 200.0 +100.0
|
|
|
|
|
} else if routine[0].completed * days >= 1000.0 && routine[1].completed * days >= 1000.0 && routine[2].completed * days >= 1000.0 && routine[3].completed * days >= 20.0 && routine[4].completed * days >= 80.0 { |
|
|
|
|
return 350.0 + 200.0 +100.0
|
|
|
|
|
} else if routine[0].completed * days >= 600.0 && routine[1].completed * days >= 600.0 && routine[2].completed * days >= 600.0 && routine[3].completed * days >= 12.0 && routine[4].completed * days >= 50.0 { |
|
|
|
|
return 200.0 +100.0
|
|
|
|
|
} else if routine[0].completed * days >= 300.0 && routine[1].completed * days >= 300.0 && routine[2].completed * days >= 300.0 && routine[3].completed * days >= 6.0 && routine[4].completed * days >= 25.0 { |
|
|
|
|
//println!("\n--min BONUS ACHEIEVED!");
|
|
|
|
|
return 200.0 +100.0 |
|
|
|
|
} |
|
|
|
|
else { return 0.0 } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn energy_effiecnt(routines: Vec<(f64, [Activity;5])>, days:f64) { |
|
|
|
|
let mut best = 0; |
|
|
|
|
let mut best_eff = 0.0; |
|
|
|
|
for i in 0..routines.len() { |
|
|
|
|
if routines[i].0 / check_energy_used_up(&routines[i].1).1 > best_eff { |
|
|
|
|
best_eff = routines[i].0 / check_energy_used_up(&routines[i].1).1; |
|
|
|
|
best = i |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
println!("\nMost Efficient Routine: Score:{} | Energy: {} | Bonus: {}", routines[best].0, check_energy_used_up(&routines[best].1).1, compute_bonus_points(&routines[best].1, days)); |
|
|
|
|
for act in &routines[best].1 { |
|
|
|
|
println!("{}", act); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* |
|
|
|
|
PushUps: 0.2 (1/5) |
|
|
|
|
Squats: 0.1333333 (1/7.5) |
|
|
|
|
Sit-Ups: 0.2 (1/5) |
|
|
|
|
Running: 20 |
|
|
|
|
Reading: 0.5 |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
|
struct Activity { |
|
|
|
|
points_per_rep: f64, |
|
|
|
|
max_reps_per_day: f64, |
|
|
|
|
energy_cost: f64, // Max daily energy is 100, how many reps can you do a day | 100 / Max rep/day == energy cost
|
|
|
|
|
completed: f64, |
|
|
|
|
pub name: String |
|
|
|
|
} |
|
|
|
|
impl Activity { |
|
|
|
|
fn new(points_per_rep: f64, max_reps_per_day: f64, name: String) -> Self { |
|
|
|
|
Activity {
|
|
|
|
|
points_per_rep: points_per_rep,
|
|
|
|
|
max_reps_per_day: max_reps_per_day,
|
|
|
|
|
energy_cost: (100.0/max_reps_per_day), |
|
|
|
|
completed : 0.0, |
|
|
|
|
name: name |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
fn do_activity(&mut self, reps: usize) -> f64{ |
|
|
|
|
// Returns points earned and energy spent
|
|
|
|
|
self.completed += reps as f64; |
|
|
|
|
self.energy_cost * reps as f64 |
|
|
|
|
} |
|
|
|
|
fn points_earned(&self) -> f64 { |
|
|
|
|
self.completed * self.points_per_rep |
|
|
|
|
} |
|
|
|
|
fn reset(&mut self) { |
|
|
|
|
self.completed = 0.0; |
|
|
|
|
} |
|
|
|
|
fn energy_used(&self) -> f64 { |
|
|
|
|
self.completed * self.energy_cost |
|
|
|
|
} |
|
|
|
|
fn possible_reps(&self, energy_available: f64) -> usize { |
|
|
|
|
(energy_available / self.energy_cost) as usize |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
impl Display for Activity { |
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
|
|
|
|
write!(f, "{} | {} | {:2}", self.name, self.completed, self.points_earned()) |
|
|
|
|
} |
|
|
|
|
} |