ditched the multiple threads, because that wasn't valid
This commit is contained in:
parent
226dee33cc
commit
aca97e1c55
4 changed files with 148 additions and 138 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,21 +91,17 @@ 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();
|
||||||
{
|
|
||||||
let arc = Arc::clone(&grid);
|
|
||||||
let mut lock = arc.lock();
|
|
||||||
let grid = lock.as_mut().unwrap();
|
|
||||||
if y > 0 {
|
if y > 0 {
|
||||||
new_directions.push(Point::new(
|
new_directions.push(Point::new(
|
||||||
x,
|
x,
|
||||||
y - 1,
|
y - 1,
|
||||||
grid.get_value(x, y - 1, current_path.length() + 1),
|
grid.get_value(x, y - 1, Some(¤t_path)),
|
||||||
));
|
));
|
||||||
if x < N - 1 {
|
if x < N - 1 {
|
||||||
new_directions.push(Point::new(
|
new_directions.push(Point::new(
|
||||||
x + 1,
|
x + 1,
|
||||||
y - 1,
|
y - 1,
|
||||||
grid.get_value(x + 1, y - 1, current_path.length() + 1),
|
grid.get_value(x + 1, y - 1, Some(¤t_path)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,13 +110,13 @@ pub fn find_optimal_path(
|
||||||
new_directions.push(Point::new(
|
new_directions.push(Point::new(
|
||||||
x - 1,
|
x - 1,
|
||||||
y,
|
y,
|
||||||
grid.get_value(x - 1, y, current_path.length() + 1),
|
grid.get_value(x - 1, y, Some(¤t_path)),
|
||||||
));
|
));
|
||||||
if y > 0 {
|
if y > 0 {
|
||||||
new_directions.push(Point::new(
|
new_directions.push(Point::new(
|
||||||
x - 1,
|
x - 1,
|
||||||
y - 1,
|
y - 1,
|
||||||
grid.get_value(x - 1, y - 1, current_path.length() + 1),
|
grid.get_value(x - 1, y - 1, Some(¤t_path)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,13 +125,13 @@ pub fn find_optimal_path(
|
||||||
new_directions.push(Point::new(
|
new_directions.push(Point::new(
|
||||||
x + 1,
|
x + 1,
|
||||||
y,
|
y,
|
||||||
grid.get_value(x + 1, y, current_path.length() + 1),
|
grid.get_value(x + 1, y, Some(¤t_path)),
|
||||||
));
|
));
|
||||||
if y < N - 1 {
|
if y < N - 1 {
|
||||||
new_directions.push(Point::new(
|
new_directions.push(Point::new(
|
||||||
x + 1,
|
x + 1,
|
||||||
y + 1,
|
y + 1,
|
||||||
grid.get_value(x + 1, y + 1, current_path.length() + 1),
|
grid.get_value(x + 1, y + 1, Some(¤t_path)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -154,13 +140,13 @@ pub fn find_optimal_path(
|
||||||
new_directions.push(Point::new(
|
new_directions.push(Point::new(
|
||||||
x,
|
x,
|
||||||
y + 1,
|
y + 1,
|
||||||
grid.get_value(x, y + 1, current_path.length() + 1),
|
grid.get_value(x, y + 1, Some(¤t_path)),
|
||||||
));
|
));
|
||||||
if x > 0 {
|
if x > 0 {
|
||||||
new_directions.push(Point::new(
|
new_directions.push(Point::new(
|
||||||
x - 1,
|
x - 1,
|
||||||
y + 1,
|
y + 1,
|
||||||
grid.get_value(x - 1, y + 1, current_path.length() + 1),
|
grid.get_value(x - 1, y + 1, Some(¤t_path)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,9 +177,6 @@ pub fn find_optimal_path(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//drop lock
|
|
||||||
}
|
|
||||||
|
|
||||||
// continue?
|
// continue?
|
||||||
let t1 = SystemTime::now();
|
let t1 = SystemTime::now();
|
||||||
if let Ok(elapsed) = t1.duration_since(t0) {
|
if let Ok(elapsed) = t1.duration_since(t0) {
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,7 +11,7 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
@ -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));
|
||||||
|
if let Some(hit_time) = hit_time {
|
||||||
|
if pathlen > *hit_time {
|
||||||
|
// +1 because we are in the process of adding a new point to the path
|
||||||
|
let elapsed_since_hit = (pathlen + 1 - hit_time) as f32;
|
||||||
|
value = f32::min(
|
||||||
elapsed_since_hit * initial_value * RECOVERY_FACTOR,
|
elapsed_since_hit * initial_value * RECOVERY_FACTOR,
|
||||||
initial_value,
|
initial_value,
|
||||||
);
|
);
|
||||||
return 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue