master
Griffiths Lott 3 years ago
commit 79969ccfa9
  1. 1
      .gitignore
  2. 25
      Cargo.lock
  3. 9
      Cargo.toml
  4. 268
      src/main.rs

1
.gitignore vendored

@ -0,0 +1 @@
/target

25
Cargo.lock generated

@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "NSSO"
version = "0.1.0"
dependencies = [
"num_cpus",
]
[[package]]
name = "libc"
version = "0.2.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
[[package]]
name = "num_cpus"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee7e88156f3f9e19bdd598f8d6c9db7bf4078f99f8381f43a55b09648d1a6e3"
dependencies = [
"libc",
]

@ -0,0 +1,9 @@
[package]
name = "NSSO"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
num_cpus = "0.2"

@ -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())
}
}
Loading…
Cancel
Save