ditched the multiple threads, because that wasn't valid

This commit is contained in:
Shautvast 2024-11-12 13:46:19 +01:00
parent 226dee33cc
commit aca97e1c55
4 changed files with 148 additions and 138 deletions

View file

@ -21,4 +21,5 @@ running:
*solution2* *solution2*
* written in rust * written in rust
* single and multiple drones * single and multiple drones
* uses basically the same method, but after a definitive flight path is calculated, the grid is updated for the next drone
* see tests and main for validity of the algorithm * see tests and main for validity of the algorithm

View file

@ -3,10 +3,6 @@ use rand::Rng;
use std::collections::HashSet; use std::collections::HashSet;
use std::hash::{DefaultHasher, Hash, Hasher}; use std::hash::{DefaultHasher, Hash, Hasher};
use std::time::SystemTime; use std::time::SystemTime;
use std::{
sync::{Arc, Mutex},
thread,
};
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn find_optimal_path_for_n_drones( pub fn find_optimal_path_for_n_drones(
@ -16,25 +12,26 @@ pub fn find_optimal_path_for_n_drones(
t: usize, t: usize,
T: u128, T: u128,
) -> PathsResult { ) -> PathsResult {
let arc = Arc::new(Mutex::new(grid)); // multiple drones are calculated one after another
// grid hits must be updated for the next drone after the optimal for the previous has been calculated
let mut handles = vec![];
let mut paths = vec![];
let mut grid = grid.clone();
for _ in 0..ndrones { for _ in 0..ndrones {
let gridref = Arc::clone(&arc); let mut current_grid = grid.clone();
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
// start at random position // start at random position
let x: u16 = rng.gen_range(0..N); let x: u16 = rng.gen_range(0..N);
let y: u16 = rng.gen_range(0..N); let y: u16 = rng.gen_range(0..N);
// start new thread for a single drone // divide max algorithm time by number of drones
let handle = thread::spawn(move || find_optimal_path(gridref, N, t, T, x, y)); let time_per_drone = T / ndrones as u128;
handles.push(handle); let optimal = find_optimal_path(&mut current_grid, N, t, time_per_drone, x, y);
}
let mut paths = vec![]; // update the grid to the lastes state
for handle in handles { for (index, p) in optimal.points.iter().enumerate() {
paths.push(handle.join().unwrap()); grid.hit(p.x, p.y, index);
}
paths.push(optimal);
} }
let overall_score = paths.iter().map(|p| p.value).sum(); let overall_score = paths.iter().map(|p| p.value).sum();
@ -52,19 +49,12 @@ pub fn find_optimal_path_for_n_drones(
/// T max duration of the algorithm /// T max duration of the algorithm
/// x,y drone start position in the grid /// x,y drone start position in the grid
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn find_optimal_path( pub fn find_optimal_path(grid: &mut Grid, N: u16, t: usize, T: u128, x: u16, y: u16) -> Path {
grid: Arc<Mutex<Grid>>,
N: u16,
t: usize,
T: u128,
x: u16,
y: u16,
) -> Path {
let mut paths_to_consider: Vec<Path> = Vec::new(); let mut paths_to_consider: Vec<Path> = Vec::new();
let mut taken_paths: HashSet<u64> = HashSet::new(); let mut taken_paths: HashSet<u64> = HashSet::new();
// starting point // starting point
let path = Path::new(Arc::clone(&grid), x, y); let path = Path::new(grid, x, y);
// always current max // always current max
let mut max: Path = path.clone(); // sorry let mut max: Path = path.clone(); // sorry
@ -101,97 +91,90 @@ pub fn find_optimal_path(
// create a list of directions to take // create a list of directions to take
new_directions.clear(); new_directions.clear();
{ if y > 0 {
let arc = Arc::clone(&grid); new_directions.push(Point::new(
let mut lock = arc.lock(); x,
let grid = lock.as_mut().unwrap(); y - 1,
if y > 0 { grid.get_value(x, y - 1, Some(&current_path)),
new_directions.push(Point::new( ));
x,
y - 1,
grid.get_value(x, y - 1, current_path.length() + 1),
));
if x < N - 1 {
new_directions.push(Point::new(
x + 1,
y - 1,
grid.get_value(x + 1, y - 1, current_path.length() + 1),
));
}
}
if x > 0 {
new_directions.push(Point::new(
x - 1,
y,
grid.get_value(x - 1, y, current_path.length() + 1),
));
if y > 0 {
new_directions.push(Point::new(
x - 1,
y - 1,
grid.get_value(x - 1, y - 1, current_path.length() + 1),
));
}
}
if x < N - 1 { if x < N - 1 {
new_directions.push(Point::new( new_directions.push(Point::new(
x + 1, x + 1,
y, y - 1,
grid.get_value(x + 1, y, current_path.length() + 1), grid.get_value(x + 1, y - 1, Some(&current_path)),
)); ));
if y < N - 1 {
new_directions.push(Point::new(
x + 1,
y + 1,
grid.get_value(x + 1, y + 1, current_path.length() + 1),
));
}
} }
}
if x > 0 {
new_directions.push(Point::new(
x - 1,
y,
grid.get_value(x - 1, y, Some(&current_path)),
));
if y > 0 {
new_directions.push(Point::new(
x - 1,
y - 1,
grid.get_value(x - 1, y - 1, Some(&current_path)),
));
}
}
if x < N - 1 {
new_directions.push(Point::new(
x + 1,
y,
grid.get_value(x + 1, y, Some(&current_path)),
));
if y < N - 1 { if y < N - 1 {
new_directions.push(Point::new( new_directions.push(Point::new(
x, x + 1,
y + 1, y + 1,
grid.get_value(x, y + 1, current_path.length() + 1), grid.get_value(x + 1, y + 1, Some(&current_path)),
)); ));
if x > 0 { }
new_directions.push(Point::new( }
x - 1,
y + 1, if y < N - 1 {
grid.get_value(x - 1, y + 1, current_path.length() + 1), new_directions.push(Point::new(
)); x,
y + 1,
grid.get_value(x, y + 1, Some(&current_path)),
));
if x > 0 {
new_directions.push(Point::new(
x - 1,
y + 1,
grid.get_value(x - 1, y + 1, Some(&current_path)),
));
}
}
let mut points_added = false;
for point in new_directions.iter() {
if point.value > 0.0 {
let mut new_path = current_path.clone();
new_path.add(point.clone());
let mut s = DefaultHasher::new();
new_path.hash(&mut s);
let hash = s.finish();
if !taken_paths.contains(&hash) {
points_added = true;
grid.hit(point.x, point.y, new_path.length());
paths_to_consider.push(new_path);
taken_paths.insert(hash);
} }
} }
}
let mut points_added = false; if !points_added {
for point in new_directions.iter() { // dead end, evict
if point.value > 0.0 { let ended = paths_to_consider.pop().unwrap();
let mut new_path = current_path.clone(); if ended.value > max.value {
new_path.add(point.clone()); max = ended;
let mut s = DefaultHasher::new();
new_path.hash(&mut s);
let hash = s.finish();
if !taken_paths.contains(&hash) {
points_added = true;
grid.hit(point.x, point.y, new_path.length());
paths_to_consider.push(new_path);
taken_paths.insert(hash);
}
}
} }
if !points_added {
// dead end, evict
let ended = paths_to_consider.pop().unwrap();
if ended.value > max.value {
max = ended;
}
}
//drop lock
} }
// continue? // continue?
@ -211,8 +194,9 @@ mod tests {
#[test] #[test]
pub fn test_single_drone() { pub fn test_single_drone() {
let grid = Grid::new(20); let mut grid = Grid::new(100);
let opt = find_optimal_path(Arc::new(Mutex::new(grid.clone())), 100, 10, 1000, 9, 9); // values for x and y are chosen so that a loop must occur
let opt = find_optimal_path(&mut grid, 20, 20, 1000, 9, 9);
let mut all_points: HashSet<Point> = HashSet::new(); let mut all_points: HashSet<Point> = HashSet::new();
let mut loop_in_path = false; let mut loop_in_path = false;
@ -225,12 +209,14 @@ mod tests {
all_points.insert(point.clone()); all_points.insert(point.clone());
} }
if loop_in_path { if loop_in_path {
//println!("check"); //verify that this occurs println!("check"); //verify that this occurs
let max_sum: f32 = opt let max_sum: f32 = opt
.points .points
.iter() .iter()
.map(|p| grid.get_initial_value(p.x, p.y)) .map(|p| grid.get_initial_value(p.x, p.y))
.sum(); .sum();
println!("max sum {:?}, opt path {:?}", max_sum, opt);
// verify that the grid value was updated because of the hit
assert!(max_sum > opt.value()); assert!(max_sum > opt.value());
} }
} }

View file

@ -1,8 +1,7 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::{BTreeSet, HashMap, LinkedList}, collections::{HashMap, HashSet, LinkedList},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
sync::{Arc, Mutex},
}; };
const RECOVERY_FACTOR: f32 = 0.1; const RECOVERY_FACTOR: f32 = 0.1;
@ -12,9 +11,9 @@ const RECOVERY_FACTOR: f32 = 0.1;
pub struct Grid { pub struct Grid {
data: Vec<Vec<u16>>, data: Vec<Vec<u16>>,
// keep track of every point that has been hit at some time // keep track of every point that has been hit at some time
hits: HashMap<(u16, u16), BTreeSet<usize>>, // TreeSet<usize> is integer times that point has been visited. hits: HashMap<(u16, u16), usize>, // TreeSet<usize> is integer times that point has been visited.
// Must always be sorted and could probably be a single int // Must always be sorted and could probably be a single int
// (keep track of last time hit) // (keep track of last time hit)
} }
impl Grid { impl Grid {
@ -51,30 +50,47 @@ impl Grid {
*(self.data.get(y as usize).unwrap().get(x as usize).unwrap()) as f32 *(self.data.get(y as usize).unwrap().get(x as usize).unwrap()) as f32
} }
pub fn get_value(&self, x: u16, y: u16, time: usize) -> f32 { /// get the value of a point on the grid
let hit = self.hits.get(&(x, y)); /// 1. initial value, given by the datafile
/// 2. value possibly updated by a previous drone in the multiple drone scenario
/// 3. value possibly updated by the drone itself, when it gets to a point that it already occupied
pub fn get_value(&self, x: u16, y: u16, path: Option<&Path>) -> f32 {
let pathlen = path.map_or(0, |p| p.length());
let initial_value = self.get_initial_value(x, y); let initial_value = self.get_initial_value(x, y);
if let Some(hit_times) = hit { // 1.
for t in hit_times.iter().rev() { let mut value = initial_value;
if time > *t {
let elapsed_since_hit = (time - *t) as f32; // 2.
let value = f32::min( let hit_time = self.hits.get(&(x, y));
elapsed_since_hit * initial_value * RECOVERY_FACTOR, if let Some(hit_time) = hit_time {
initial_value, if pathlen > *hit_time {
); // +1 because we are in the process of adding a new point to the path
return value; let elapsed_since_hit = (pathlen + 1 - hit_time) as f32;
} value = f32::min(
elapsed_since_hit * initial_value * RECOVERY_FACTOR,
initial_value,
);
} }
0.0
} else {
initial_value
} }
// 3.
if let Some(path) = path {
let maybe_hit = path.points_lookup.get(&(x, y));
if let Some(hit_time) = maybe_hit {
let elapsed_since_hit = (path.points.len() - hit_time) as f32;
value = f32::min(
elapsed_since_hit * initial_value * RECOVERY_FACTOR,
initial_value,
);
}
}
value
} }
pub fn hit(&mut self, x: u16, y: u16, time: usize) { pub fn hit(&mut self, x: u16, y: u16, time: usize) {
self.hits.entry((x, y)).or_default().insert(time); self.hits.insert((x, y), time);
} }
pub fn size(&self) -> u16 { pub fn size(&self) -> u16 {
@ -85,15 +101,13 @@ impl Grid {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Path { pub struct Path {
pub points: LinkedList<Point>, pub points: LinkedList<Point>,
pub points_lookup: HashMap<(u16, u16), usize>,
pub value: f32, pub value: f32,
} }
impl Path { impl Path {
pub fn new(grid: Arc<Mutex<Grid>>, initial_x: u16, initial_y: u16) -> Self { pub fn new(grid: &Grid, initial_x: u16, initial_y: u16) -> Self {
let mut points = LinkedList::new(); let value = grid.get_value(initial_x, initial_y, None);
let mut lock = grid.lock();
let grid = lock.as_mut().unwrap();
let value = grid.get_value(initial_x, initial_y, 0);
let p = Point { let p = Point {
x: initial_x, x: initial_x,
@ -101,8 +115,16 @@ impl Path {
value, value,
}; };
let mut points = LinkedList::new();
points.push_front(p); points.push_front(p);
Self { points, value } let mut points_lookup = HashMap::new();
points_lookup.insert((initial_x, initial_y), 0);
Self {
points,
value,
points_lookup,
}
} }
// length = age of the path // length = age of the path
@ -204,8 +226,8 @@ mod test {
#[test] #[test]
pub fn test() { pub fn test() {
let grid = Grid::new(20); let grid = Grid::new(20);
assert_eq!(grid.get_value(0, 0, 0), 0.0); assert_eq!(grid.get_value(0, 0, None), 0.0);
assert_eq!(grid.get_value(0, 1, 0), 1.0); assert_eq!(grid.get_value(0, 1, None), 1.0);
} }
} }

View file

@ -1,8 +1,9 @@
use solution2::{algorithm::find_optimal_path_for_n_drones, grid::Grid}; use solution2::{algorithm::find_optimal_path_for_n_drones, grid::Grid};
/// this app calculates paths for 4 drones, concurrently, with a shared grid /// this app calculates paths for 4 drones
fn main() { fn main() {
let result = find_optimal_path_for_n_drones(Grid::new(100), 4, 100, 15, 2000); let grid = Grid::new(100);
let result = find_optimal_path_for_n_drones(grid, 4, 100, 15, 2000);
for (i, path) in result.paths.iter().enumerate() { for (i, path) in result.paths.iter().enumerate() {
println!("path {}: score {}", i, path.value()) println!("path {}: score {}", i, path.value())
} }