multiple drones
This commit is contained in:
parent
796c844c68
commit
906818d864
9 changed files with 407 additions and 118 deletions
|
|
@ -14,14 +14,15 @@ public record Grid(List<List<Integer>> grid) {
|
|||
|
||||
public static Grid fromFile(String resource) {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(Grid.class.getClassLoader().getResourceAsStream(resource)));
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
Grid.class.getClassLoader().getResourceAsStream(resource)));
|
||||
String line;
|
||||
List<List<Integer>> rows = new ArrayList<>();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String[] values = line.split(" ");
|
||||
List<Integer> row = new ArrayList<>(values.length);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
row.add(Integer.parseInt(values[i]));
|
||||
for (String value : values) {
|
||||
row.add(Integer.parseInt(value));
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ public class OptimalPathFinder {
|
|||
newDirections.add(Point.create(g, path, x - 1, y + 1));
|
||||
}
|
||||
|
||||
if (!newDirections.isEmpty()) {
|
||||
boolean pointsAdded = false;
|
||||
for (Point p : newDirections) {
|
||||
// is it worthwile going there?
|
||||
|
|
@ -112,7 +111,6 @@ public class OptimalPathFinder {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,15 @@ public class GridReadTest {
|
|||
String row10line = row10.stream().map(i -> "" + i).collect(Collectors.joining(" "));
|
||||
assertEquals("1 1 1 2 0 0 1 1 2 2 0 2 2 2 2 1 1 2 0 2", row10line);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGridReader1() {
|
||||
Grid grid = Grid.fromFile("grids/100.txt");
|
||||
System.out.println(grid.getInitialValue(9, 9));
|
||||
System.out.println(grid.getInitialValue(10, 8));
|
||||
System.out.println(grid.getInitialValue(8, 10));
|
||||
|
||||
Path optimalPath = new OptimalPathFinder().findOptimalPath(grid, 100, 2, 1000, 9, 9);
|
||||
System.out.println(optimalPath);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ public class PathFinderTest {
|
|||
@Test
|
||||
public void testBestPath20() {
|
||||
Grid grid = Grid.fromFile("grids/20.txt");
|
||||
Path path = new OptimalPathFinder().findOptimalPath(grid, 20, 8, 1000, 9, 9);
|
||||
Path path = new OptimalPathFinder().findOptimalPath(grid, 20, 10, 1000, 9, 9);
|
||||
System.out.println(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBestPath100() {
|
||||
Grid grid = Grid.fromFile("grids/100.txt");
|
||||
Path path = new OptimalPathFinder().findOptimalPath(grid, 100, 10, 100, 50, 50);
|
||||
Path path = new OptimalPathFinder().findOptimalPath(grid, 100, 10, 100, 9, 9);
|
||||
System.out.println(path);
|
||||
}
|
||||
|
||||
|
|
|
|||
133
solution2/Cargo.lock
generated
133
solution2/Cargo.lock
generated
|
|
@ -2,6 +2,139 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.162"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solution2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{BTreeSet, HashMap},
|
||||
collections::{BTreeSet, HashMap, LinkedList},
|
||||
hash::{Hash, Hasher},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug)]
|
||||
pub struct Grid {
|
||||
data: Vec<Vec<u16>>,
|
||||
|
|
@ -14,6 +16,7 @@ pub struct Grid {
|
|||
}
|
||||
|
||||
impl Grid {
|
||||
#[allow(non_snake_case)]
|
||||
pub fn new(N: usize) -> Self {
|
||||
let grid20 = include_str!("grids/20.txt");
|
||||
let grid100 = include_str!("grids/100.txt");
|
||||
|
|
@ -27,10 +30,12 @@ impl Grid {
|
|||
};
|
||||
|
||||
let mut data = vec![];
|
||||
for row in datafile.split("\n") {
|
||||
for row in datafile.split('\n') {
|
||||
let mut datarow = vec![];
|
||||
for col in row.split(" ") {
|
||||
datarow.push(u16::from_str_radix(col, 10).unwrap());
|
||||
for col in row.split(' ') {
|
||||
if let Ok(v) = col.parse::<u16>() {
|
||||
datarow.push(v);
|
||||
}
|
||||
}
|
||||
data.push(datarow);
|
||||
}
|
||||
|
|
@ -50,10 +55,7 @@ impl Grid {
|
|||
if time > *t {
|
||||
let elapsed_since_hit = (time - *t) as f32;
|
||||
|
||||
return f32::min(
|
||||
elapsed_since_hit * initial_value as f32 * 0.1,
|
||||
initial_value,
|
||||
);
|
||||
return f32::min(elapsed_since_hit * initial_value * 0.1, initial_value);
|
||||
}
|
||||
}
|
||||
0.0
|
||||
|
|
@ -62,6 +64,10 @@ impl Grid {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn hit(&mut self, x: u16, y: u16, time: usize) {
|
||||
self.hits.entry((x, y)).or_default().insert(time);
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u16 {
|
||||
self.data.len() as u16
|
||||
}
|
||||
|
|
@ -69,13 +75,15 @@ impl Grid {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Path {
|
||||
points: Vec<Point>,
|
||||
points: LinkedList<Point>,
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn new(grid: &Grid, initial_x: u16, initial_y: u16) -> Self {
|
||||
let mut points = vec![];
|
||||
pub fn new(grid: Arc<Mutex<Grid>>, initial_x: u16, initial_y: u16) -> Self {
|
||||
let mut points = LinkedList::new();
|
||||
let mut lock = grid.lock();
|
||||
let grid = lock.as_mut().unwrap();
|
||||
let value = grid.get_value(initial_x, initial_y, 0);
|
||||
|
||||
let p = Point {
|
||||
|
|
@ -84,7 +92,7 @@ impl Path {
|
|||
value,
|
||||
};
|
||||
|
||||
points.push(p);
|
||||
points.push_front(p);
|
||||
Self { points, value }
|
||||
}
|
||||
|
||||
|
|
@ -92,27 +100,23 @@ impl Path {
|
|||
self.points.len()
|
||||
}
|
||||
|
||||
pub fn head(&self) -> &Point {
|
||||
self.points.get(self.points.len() - 1).unwrap() // assert Some
|
||||
pub fn last(&self) -> &Point {
|
||||
self.points.front().unwrap() // assert Some
|
||||
}
|
||||
|
||||
pub fn value(&self) -> f32 {
|
||||
self.points.iter().map(|p| p.value).sum()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, p: Point) {
|
||||
self.points.push_front(p);
|
||||
self.value = self.value();
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Path {
|
||||
fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
|
||||
match self.value > other.value {
|
||||
true => Some(Ordering::Greater),
|
||||
false => {
|
||||
if self.value == other.value {
|
||||
Some(Ordering::Equal)
|
||||
} else {
|
||||
Some(Ordering::Less)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,13 +124,39 @@ impl Eq for Path {}
|
|||
|
||||
impl Ord for Path {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.partial_cmp(other).unwrap()
|
||||
match self.value > other.value {
|
||||
true => Ordering::Greater,
|
||||
false => {
|
||||
if self.value() == other.value {
|
||||
Ordering::Equal
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Path {
|
||||
fn eq(&self, other: &Path) -> bool {
|
||||
self.value == other.value
|
||||
if self.points.len() != other.points.len() {
|
||||
return false;
|
||||
}
|
||||
for p in self.points.iter() {
|
||||
if !other.points.contains(p) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Path {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for p in self.points.iter() {
|
||||
p.x.hash(state);
|
||||
p.y.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +169,7 @@ pub struct Point {
|
|||
|
||||
impl Point {
|
||||
pub fn new(x: u16, y: u16, value: f32) -> Self {
|
||||
Self { x, y, value: 0.0 }
|
||||
Self { x, y, value }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,3 +186,15 @@ impl Hash for Point {
|
|||
self.y.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Grid;
|
||||
|
||||
#[test]
|
||||
pub fn test() {
|
||||
let grid = Grid::new(20);
|
||||
assert_eq!(grid.get_value(0, 0, 0), 0.0);
|
||||
assert_eq!(grid.get_value(0, 1, 0), 1.0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,73 @@
|
|||
use grid::{Grid, Path, Point};
|
||||
use std::collections::BTreeSet;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::collections::HashSet;
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub mod grid;
|
||||
|
||||
pub fn find_optimal_path(grid: &Grid, t: usize, T: u128, x: u16, y: u16) {
|
||||
let mut paths_to_consider: BTreeSet<Path> = BTreeSet::new();
|
||||
let taken_paths: Vec<u64> = vec![];
|
||||
/// finds the optimal path for a drone in a grid of points(x,y) that each has a fixed initial value
|
||||
/// observing a point (hit) resets the value to 0, after wich it gradually increases with time in a fixed rate
|
||||
/// N size of square grid (rows and cols)
|
||||
/// t max length of a flight path
|
||||
/// T max duration of the algorithm
|
||||
/// x,y drone start position in the grid
|
||||
#[allow(non_snake_case)]
|
||||
pub fn find_optimal_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 taken_paths: HashSet<u64> = HashSet::new();
|
||||
|
||||
let path = Path::new(grid, x, y);
|
||||
// starting point
|
||||
let path = Path::new(Arc::clone(&grid), x, y);
|
||||
|
||||
// always current max
|
||||
let mut max: Path = path.clone(); // sorry
|
||||
|
||||
paths_to_consider.insert(path);
|
||||
// add the first path
|
||||
paths_to_consider.push(path);
|
||||
|
||||
// keep track of time
|
||||
let t0 = SystemTime::now();
|
||||
let mut running = true;
|
||||
|
||||
// this is time spent for the path, grid keeps the global timeframe
|
||||
// same as path length, so remove?
|
||||
let mut discrete_elapsed = 0;
|
||||
|
||||
while running {
|
||||
let N = grid.size();
|
||||
let mut current_path = paths_to_consider.pop_last().unwrap(); // assert Some
|
||||
|
||||
if current_path.value > max.value {
|
||||
max = current_path.clone(); // sorry
|
||||
}
|
||||
while current_path.length() >= t {
|
||||
current_path = paths_to_consider.pop_last().unwrap(); // highest
|
||||
if current_path.value > max.value {
|
||||
max = current_path.clone(); // sorry
|
||||
}
|
||||
}
|
||||
|
||||
let head = current_path.head();
|
||||
let x = head.x;
|
||||
let y = head.y;
|
||||
// will keep at most 8 new directions from current location
|
||||
let mut new_directions = vec![];
|
||||
|
||||
let mut current_path;
|
||||
while running && !paths_to_consider.is_empty() {
|
||||
paths_to_consider.sort();
|
||||
current_path = paths_to_consider.last().unwrap().clone(); // assert = Some
|
||||
|
||||
// evict paths that are of max len
|
||||
while current_path.length() >= t {
|
||||
_ = paths_to_consider.remove(paths_to_consider.len() - 1);
|
||||
if current_path.value() > max.value() {
|
||||
max = current_path.clone(); // sorry
|
||||
}
|
||||
current_path = paths_to_consider.last().unwrap().clone();
|
||||
}
|
||||
|
||||
let head = current_path.last();
|
||||
let x = head.x;
|
||||
let y = head.y;
|
||||
|
||||
// create a list of directions to take
|
||||
new_directions.clear();
|
||||
{
|
||||
let arc = Arc::clone(&grid);
|
||||
let mut lock = arc.lock();
|
||||
let grid = lock.as_mut().unwrap();
|
||||
if y > 0 {
|
||||
new_directions.push(Point::new(
|
||||
x,
|
||||
|
|
@ -96,6 +128,33 @@ pub fn find_optimal_path(grid: &Grid, t: usize, T: u128, x: u16, y: u16) {
|
|||
}
|
||||
}
|
||||
|
||||
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, discrete_elapsed);
|
||||
paths_to_consider.push(new_path);
|
||||
taken_paths.insert(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !points_added {
|
||||
// dead end, evict
|
||||
let ended = paths_to_consider.remove(paths_to_consider.len() - 1);
|
||||
if ended.value > max.value {
|
||||
max = ended;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// continue?
|
||||
let t1 = SystemTime::now();
|
||||
if let Ok(elapsed) = t1.duration_since(t0) {
|
||||
|
|
@ -105,4 +164,17 @@ pub fn find_optimal_path(grid: &Grid, t: usize, T: u128, x: u16, y: u16) {
|
|||
}
|
||||
discrete_elapsed += 1;
|
||||
}
|
||||
max
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test() {
|
||||
let mut grid = Grid::new(20);
|
||||
let opt = find_optimal_path(Arc::new(Mutex::new(grid)), 100, 10, 1000, 9, 9);
|
||||
println!("value {:?}", opt);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,36 @@
|
|||
use solution2::grid::Grid;
|
||||
use rand::Rng;
|
||||
use solution2::{find_optimal_path, grid::Grid};
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
/// this app calculates paths for 4 drones, concurrently, with a shared grid
|
||||
fn main() {
|
||||
Grid::new(20);
|
||||
find_optimal_path_for_n_drones(4, 100, 10, 1000);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn find_optimal_path_for_n_drones(ndrones: usize, N: u16, t: usize, T: u128) {
|
||||
let grid = Grid::new(100);
|
||||
let arc = Arc::new(Mutex::new(grid));
|
||||
|
||||
let mut handles = vec![];
|
||||
|
||||
for _ in 0..ndrones {
|
||||
let gridref = Arc::clone(&arc);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
// start at random position
|
||||
let x: u16 = rng.gen_range(0..100);
|
||||
let y: u16 = rng.gen_range(0..100);
|
||||
// start new thread for a single drone
|
||||
let handle = thread::spawn(move || find_optimal_path(gridref, N, t, T, x, y));
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
let result = handle.join().unwrap();
|
||||
println!("{result:?}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue