Big Update
Add A*-Routing with tunable Greedy factor Add Greedy-Routing Add permutation option to find shortest order for systems Update CLI Update README
This commit is contained in:
		
							parent
							
								
									ffdd1bb7a2
								
							
						
					
					
						commit
						afae1c08b5
					
				
					 4 changed files with 392 additions and 39 deletions
				
			
		
							
								
								
									
										7
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -68,6 +68,7 @@ dependencies = [ | ||||||
|  "csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", |  "csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", |  "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rstar 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |  "rstar 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", |  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", |  "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | @ -144,6 +145,11 @@ name = "pdqselect" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "permutohedron" | ||||||
|  | version = "0.2.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "proc-macro2" | name = "proc-macro2" | ||||||
| version = "0.4.30" | version = "0.4.30" | ||||||
|  | @ -320,6 +326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" | "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" | ||||||
| "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" | "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" | ||||||
| "checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" | "checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" | ||||||
|  | "checksum permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c" | ||||||
| "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" | ||||||
| "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" | "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" | ||||||
| "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" | "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" | ||||||
|  |  | ||||||
|  | @ -3,9 +3,12 @@ name = "ed_lrr" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| authors = ["Daniel Seiller <earthnuker@gmail.com>"] | authors = ["Daniel Seiller <earthnuker@gmail.com>"] | ||||||
| edition = "2018" | edition = "2018" | ||||||
|  | repository = "https://gitlab.com/Earthnuker/ed_lrr.git" | ||||||
|  | license = "WTFPL" | ||||||
| 
 | 
 | ||||||
| # [profile.release] | [[bin]] | ||||||
| # debug = true | name = "ed_lrr" | ||||||
|  | path = "src/main.rs" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| csv = "1.0.7" | csv = "1.0.7" | ||||||
|  | @ -15,3 +18,4 @@ rstar = "0.4.0" | ||||||
| humantime = "1.2.0" | humantime = "1.2.0" | ||||||
| fnv = "1.0.6" | fnv = "1.0.6" | ||||||
| structopt = "0.2.16" | structopt = "0.2.16" | ||||||
|  | permutohedron = "0.2.4" | ||||||
|  |  | ||||||
|  | @ -2,10 +2,10 @@ | ||||||
| 
 | 
 | ||||||
| ## Usage: | ## Usage: | ||||||
| 
 | 
 | ||||||
| 1. run `download.sh` and `process.py` (you can hit Ctrl+C when it says "Building KD-Tree..") | 1. download `bodies.json` and `systemsWithCoordinates.json` from https://www.edsm.net/en/nightly-dumps/ and place them in the `dumps` folder | ||||||
| 1. edit source, destination and range in `src/main.rs` | 2. run `process.py` in the `dumps` folder | ||||||
| 1. (optional) `set RUSTFLAGS=-C target-cpu=native` (Windows) or `export RUSTFLAGS=-C target-cpu=native` (Linux) | 3. run `cargo install --git https://gitlab.com/Earthnuker/ed_lrr.git` | ||||||
| 1. `cargo run --release` | 4. run `ed_lrr --help` | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										408
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										408
									
								
								src/main.rs
									
										
									
									
									
								
							|  | @ -1,18 +1,21 @@ | ||||||
| extern crate csv; | extern crate csv; | ||||||
| extern crate serde; | extern crate serde; | ||||||
| #[macro_use] |  | ||||||
| extern crate structopt; | extern crate structopt; | ||||||
| #[macro_use] | #[macro_use] | ||||||
| extern crate serde_derive; | extern crate serde_derive; | ||||||
| extern crate fnv; | extern crate fnv; | ||||||
| extern crate humantime; | extern crate humantime; | ||||||
|  | extern crate permutohedron; | ||||||
|  | use core::cmp::Ordering; | ||||||
| use fnv::{FnvHashMap, FnvHashSet}; | use fnv::{FnvHashMap, FnvHashSet}; | ||||||
| use humantime::format_duration; | use humantime::format_duration; | ||||||
|  | use permutohedron::LexicalPermutation; | ||||||
| use rstar::{PointDistance, RTree, RTreeObject, AABB}; | use rstar::{PointDistance, RTree, RTreeObject, AABB}; | ||||||
| use std::collections::VecDeque; | use std::collections::VecDeque; | ||||||
| use std::hash::{Hash, Hasher}; | use std::hash::{Hash, Hasher}; | ||||||
| use std::io::Write; | use std::io::Write; | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
|  | use std::str::FromStr; | ||||||
| use std::time::Instant; | use std::time::Instant; | ||||||
| use structopt::StructOpt; | use structopt::StructOpt; | ||||||
| 
 | 
 | ||||||
|  | @ -41,21 +44,73 @@ struct Point { | ||||||
|     y: f32, |     y: f32, | ||||||
|     z: f32, |     z: f32, | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | enum Mode { | ||||||
|  |     BFS, | ||||||
|  |     Greedy, | ||||||
|  |     AStar, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl FromStr for Mode { | ||||||
|  |     type Err = String; | ||||||
|  |     fn from_str(s: &str) -> Result<Mode, String> { | ||||||
|  |         match s { | ||||||
|  |             "bfs" => Ok(Mode::BFS), | ||||||
|  |             "greedy" => Ok(Mode::Greedy), | ||||||
|  |             "astar" => Ok(Mode::AStar), | ||||||
|  |             _ => Err("Invalid Mode".to_string()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[derive(Debug, StructOpt)] | #[derive(Debug, StructOpt)] | ||||||
| #[structopt(name = "ed_lrr", about = "Elite: Dangerous Long-Range Router")] | #[structopt(
 | ||||||
| /// Plots a route through multiple systems using breadth-first search (Currently needs a lot of RAM (8+GB), sorry)
 |     name = "ed_lrr", | ||||||
|  |     about = "Elite: Dangerous Long-Range Router", | ||||||
|  |     rename_all = "kebab-case" | ||||||
|  | )] | ||||||
|  | /// Plots a route through multiple systems using breadth-first search (Currently needs a lot of RAM (about 6GB), sorry)
 | ||||||
| struct Opt { | struct Opt { | ||||||
|     #[structopt(short = "r", long = "range")] |     #[structopt(short, long = "range")] | ||||||
|     /// Jump Range
 |     /// Jump Range
 | ||||||
|     range: f32, |     range: f32, | ||||||
|     #[structopt(
 |     #[structopt(
 | ||||||
|         parse(from_os_str), |         parse(from_os_str), | ||||||
|         short = "p", |         short = "f", | ||||||
|         long = "path", |         long = "path", | ||||||
|         default_value = "./stars.csv" |         default_value = "./stars.csv" | ||||||
|     )] |     )] | ||||||
|     /// Path to stars.csv
 |     /// Path to stars.csv
 | ||||||
|  |     ///
 | ||||||
|  |     /// Generate using process.py (https://gitlab.com/Earthnuker/ed_lrr/raw/master/dumps/process.py)
 | ||||||
|     file_path: PathBuf, |     file_path: PathBuf, | ||||||
|  | 
 | ||||||
|  |     #[structopt(short = "p", long = "permute", conflicts_with = "full_permute")] | ||||||
|  |     /// Permute intermediate hops to find shortest route
 | ||||||
|  |     permute: bool, | ||||||
|  | 
 | ||||||
|  |     #[structopt(short = "fp", long = "full_permute", conflicts_with = "permute")] | ||||||
|  |     /// Permute all hops to find shortest route
 | ||||||
|  |     full_permute: bool, | ||||||
|  | 
 | ||||||
|  |     #[structopt(short = "g", long = "factor")] | ||||||
|  |     /// Greedyness factor for A-Star (0=BFS, inf=Greedy)
 | ||||||
|  |     factor: Option<f32>, | ||||||
|  | 
 | ||||||
|  |     #[structopt(
 | ||||||
|  |         short, | ||||||
|  |         long = "mode", | ||||||
|  |         raw(possible_values = "&[\"bfs\", \"greedy\",\"astar\"]") | ||||||
|  |     )] | ||||||
|  |     /// Search mode
 | ||||||
|  |     ///
 | ||||||
|  |     /**
 | ||||||
|  |      - BFS is guaranteed to find the shortest route but very slow | ||||||
|  |      - Greedy is a lot faster but will probably not find the shortest route | ||||||
|  |      - A-Star is a good middle ground between speed and accuracy | ||||||
|  |     */ | ||||||
|  |     mode: Mode, | ||||||
|     /// Systems to route through
 |     /// Systems to route through
 | ||||||
|     systems: Vec<String>, |     systems: Vec<String>, | ||||||
| } | } | ||||||
|  | @ -67,6 +122,15 @@ fn dist(p1: &[f32; 3], p2: &[f32; 3]) -> f32 { | ||||||
|     return (dx * dx + dy * dy + dz * dz).sqrt(); |     return (dx * dx + dy * dy + dz * dz).sqrt(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn fcmp(a: &f32, b: &f32) -> Ordering { | ||||||
|  |     match (a, b) { | ||||||
|  |         (x, y) if x.is_nan() && y.is_nan() => Ordering::Equal, | ||||||
|  |         (x, _) if x.is_nan() => Ordering::Greater, | ||||||
|  |         (_, y) if y.is_nan() => Ordering::Less, | ||||||
|  |         (..) => a.partial_cmp(&b).unwrap(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl Point { | impl Point { | ||||||
|     pub fn dist2(&self, p: &[f32; 3]) -> f32 { |     pub fn dist2(&self, p: &[f32; 3]) -> f32 { | ||||||
|         let dx = self.x - p[0]; |         let dx = self.x - p[0]; | ||||||
|  | @ -178,22 +242,7 @@ impl Router { | ||||||
|         return self.tree.nearest_neighbor(point).unwrap(); |         return self.tree.nearest_neighbor(point).unwrap(); | ||||||
|     } |     } | ||||||
|     fn points_in_sphere(&self, center: &[f32; 3], radius: f32) -> impl Iterator<Item = &Point> { |     fn points_in_sphere(&self, center: &[f32; 3], radius: f32) -> impl Iterator<Item = &Point> { | ||||||
|         let center: [f32; 3] = *center; |         return self.tree.locate_within_distance(*center, radius * radius); | ||||||
|         return self |  | ||||||
|             .tree |  | ||||||
|             .locate_in_envelope(&AABB::from_corners( |  | ||||||
|                 [ |  | ||||||
|                     center[0] - radius * 1f32, |  | ||||||
|                     center[1] - radius * 1f32, |  | ||||||
|                     center[2] - radius * 1f32, |  | ||||||
|                 ], |  | ||||||
|                 [ |  | ||||||
|                     center[0] + radius * 1f32, |  | ||||||
|                     center[1] + radius * 1f32, |  | ||||||
|                     center[2] + radius * 1f32, |  | ||||||
|                 ], |  | ||||||
|             )) |  | ||||||
|             .filter(move |p| (p.dist2(¢er) < (radius * radius))); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn mult(&self, id: i64) -> f32 { |     fn mult(&self, id: i64) -> f32 { | ||||||
|  | @ -210,21 +259,82 @@ impl Router { | ||||||
|         return self.scoopable.contains(&sys.id); |         return self.scoopable.contains(&sys.id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn name_multiroute(&self, waypoints: &Vec<String>, range: f32) -> Vec<(&System, &Point)> { |     pub fn best_name_multiroute( | ||||||
|  |         &self, | ||||||
|  |         waypoints: &Vec<String>, | ||||||
|  |         range: f32, | ||||||
|  |         full: bool, | ||||||
|  |         mode: Mode, | ||||||
|  |         factor: f32, | ||||||
|  |     ) -> Vec<(&System, &Point)> { | ||||||
|  |         let mut best_score: f32 = std::f32::MAX; | ||||||
|  |         let mut waypoints = waypoints.clone(); | ||||||
|  |         let mut best_permutation_waypoints = waypoints.clone(); | ||||||
|  |         let first = waypoints.first().cloned(); | ||||||
|  |         let last = waypoints.last().cloned(); | ||||||
|  |         let t_start = Instant::now(); | ||||||
|  |         println!("Finding best permutation of hops..."); | ||||||
|  |         while waypoints.prev_permutation() {} | ||||||
|  |         loop { | ||||||
|  |             let c_first = waypoints.first().cloned(); | ||||||
|  |             let c_last = waypoints.last().cloned(); | ||||||
|  |             if full || ((c_first == first) && (c_last == last)) { | ||||||
|  |                 let mut total_d = 0.0; | ||||||
|  |                 for pair in waypoints.windows(2) { | ||||||
|  |                     match pair { | ||||||
|  |                         [src, dst] => { | ||||||
|  |                             let (src, dst) = (self.name_to_point(&src), self.name_to_point(&dst)); | ||||||
|  |                             total_d += src.distp2(dst); | ||||||
|  |                         } | ||||||
|  |                         _ => panic!("Invalid routing parameters!"), | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if total_d < best_score { | ||||||
|  |                     best_score = total_d; | ||||||
|  |                     best_permutation_waypoints = waypoints.clone(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if !waypoints.next_permutation() { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         println!("Done in {}!", format_duration(t_start.elapsed())); | ||||||
|  |         println!("Best permutation: {:?}", best_permutation_waypoints); | ||||||
|  |         return self.name_multiroute(&best_permutation_waypoints, range, mode, factor); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn name_multiroute( | ||||||
|  |         &self, | ||||||
|  |         waypoints: &Vec<String>, | ||||||
|  |         range: f32, | ||||||
|  |         mode: Mode, | ||||||
|  |         factor: f32, | ||||||
|  |     ) -> Vec<(&System, &Point)> { | ||||||
|         let mut coords = Vec::new(); |         let mut coords = Vec::new(); | ||||||
|         for p in waypoints { |         for p in waypoints { | ||||||
|             let p = self.name_to_point(p); |             let p = self.name_to_point(p); | ||||||
|             let s = [p.x, p.y, p.z]; |             let s = [p.x, p.y, p.z]; | ||||||
|             coords.push(s); |             coords.push(s); | ||||||
|         } |         } | ||||||
|         return self.multiroute(coords.as_slice(), range); |         return self.multiroute(coords.as_slice(), range, mode, factor); | ||||||
|     } |     } | ||||||
|     pub fn multiroute(&self, waypoints: &[[f32; 3]], range: f32) -> Vec<(&System, &Point)> { |     pub fn multiroute( | ||||||
|  |         &self, | ||||||
|  |         waypoints: &[[f32; 3]], | ||||||
|  |         range: f32, | ||||||
|  |         mode: Mode, | ||||||
|  |         factor: f32, | ||||||
|  |     ) -> Vec<(&System, &Point)> { | ||||||
|         let mut route = Vec::new(); |         let mut route = Vec::new(); | ||||||
|         for pair in waypoints.windows(2) { |         for pair in waypoints.windows(2) { | ||||||
|             match pair { |             match pair { | ||||||
|                 &[src, dst] => { |                 &[src, dst] => { | ||||||
|                     let block = self.route(&src, &dst, range); |                     let block = match mode { | ||||||
|  |                         Mode::BFS => self.route_bfs(&src, &dst, range), | ||||||
|  |                         Mode::Greedy => self.route_greedy(&src, &dst, range), | ||||||
|  |                         Mode::AStar => self.route_astar(&src, &dst, range, factor), | ||||||
|  |                     }; | ||||||
|                     if route.is_empty() { |                     if route.is_empty() { | ||||||
|                         route.extend(block.iter()); |                         route.extend(block.iter()); | ||||||
|                     } else { |                     } else { | ||||||
|  | @ -237,32 +347,241 @@ impl Router { | ||||||
|         return route; |         return route; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn sys_to_point(&self, id: i64) -> Option<&Point> { |     fn sys_to_point(&self, id: i64) -> &Point { | ||||||
|         for p in &self.tree { |         for p in &self.tree { | ||||||
|             if p.id == id { |             if p.id == id { | ||||||
|                 return Some(p); |                 return p; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return None; |         eprintln!("Sytem-ID not found: \"{}\"", id); | ||||||
|  |         std::process::exit(1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn name_to_point(&self, name: &str) -> &Point { |     fn name_to_point(&self, name: &str) -> &Point { | ||||||
|         for sys in self.systems.values() { |         for sys in self.systems.values() { | ||||||
|             if sys.name == name { |             if sys.name == name { | ||||||
|                 return self.sys_to_point(sys.id).unwrap(); |                 return self.sys_to_point(sys.id); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         eprintln!("Sytem not found: \"{}\"", name); |         eprintln!("Sytem not found: \"{}\"", name); | ||||||
|         std::process::exit(1); |         std::process::exit(1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn route(&self, src: &[f32; 3], dst: &[f32; 3], range: f32) -> Vec<(&System, &Point)> { |     pub fn route_astar( | ||||||
|  |         &self, | ||||||
|  |         src: &[f32; 3], | ||||||
|  |         dst: &[f32; 3], | ||||||
|  |         range: f32, | ||||||
|  |         factor: f32, | ||||||
|  |     ) -> Vec<(&System, &Point)> { | ||||||
|  |         if factor == 0.0 { | ||||||
|  |             return self.route_bfs(src, dst, range); | ||||||
|  |         } | ||||||
|  |         println!("Running A-Star with greedy factor of {}", factor); | ||||||
|         let start_sys = self.closest(src); |         let start_sys = self.closest(src); | ||||||
|         let goal_sys = self.closest(dst); |         let goal_sys = self.closest(dst); | ||||||
|  |         let start_sys_name = self.systems.get(&start_sys.id).unwrap().name.clone(); | ||||||
|  |         let goal_sys_name = self.systems.get(&goal_sys.id).unwrap().name.clone(); | ||||||
|  |         { | ||||||
|  |             let d = dist(src, dst); | ||||||
|  |             println!( | ||||||
|  |                 "Plotting route from {} to {}...", | ||||||
|  |                 start_sys_name, goal_sys_name | ||||||
|  |             ); | ||||||
|  |             println!( | ||||||
|  |                 "Jump Range: {} Ly, Distance: {} Ly, Theoretical Jumps: {}", | ||||||
|  |                 range, | ||||||
|  |                 d, | ||||||
|  |                 d / range | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         let total = self.tree.size() as f32; | ||||||
|  |         let mut prev = FnvHashMap::default(); | ||||||
|  |         let mut seen = FnvHashSet::default(); | ||||||
|  |         let t_start = Instant::now(); | ||||||
|  |         let mut found = false; | ||||||
|  |         let mut maxd = 0; | ||||||
|  |         let mut queue: Vec<(usize, usize, &Point)> = Vec::new(); | ||||||
|  |         queue.push(( | ||||||
|  |             0,                                            // depth
 | ||||||
|  |             (start_sys.distp(goal_sys) / range) as usize, // h
 | ||||||
|  |             &start_sys, | ||||||
|  |         )); | ||||||
|  |         seen.insert(start_sys.id); | ||||||
|  | 
 | ||||||
|  |         while !(queue.is_empty() || found) { | ||||||
|  |             while let Some((depth, _, sys)) = queue.pop() { | ||||||
|  |                 if depth > maxd { | ||||||
|  |                     maxd = depth; | ||||||
|  |                     print!( | ||||||
|  |                         "[{}] Depth: {}, Queue: {}, Seen: {} ({:.02}%)     \r", | ||||||
|  |                         format_duration(t_start.elapsed()), | ||||||
|  |                         depth, | ||||||
|  |                         queue.len(), | ||||||
|  |                         seen.len(), | ||||||
|  |                         ((seen.len() * 100) as f32) / total | ||||||
|  |                     ); | ||||||
|  |                     std::io::stdout().flush().unwrap(); | ||||||
|  |                 } | ||||||
|  |                 if sys.id == goal_sys.id { | ||||||
|  |                     found = true; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 queue.extend( | ||||||
|  |                     self.neighbours(&sys, range) | ||||||
|  |                         .filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id))) | ||||||
|  |                         .filter(|&nb| seen.insert(nb.id)) | ||||||
|  |                         .map(|nb| { | ||||||
|  |                             prev.insert(nb.id, sys.id); | ||||||
|  |                             let d_g = (nb.distp(goal_sys) / range) as usize; | ||||||
|  |                             return (depth + 1, d_g, nb); | ||||||
|  |                         }), | ||||||
|  |                 ); | ||||||
|  |                 queue.sort_by(|b, a| { | ||||||
|  |                     let (a_0, a_1) = (a.0 as f32, a.1 as f32); | ||||||
|  |                     let (b_0, b_1) = (b.0 as f32, b.1 as f32); | ||||||
|  |                     let v_a = a_0 + a_1 * factor; | ||||||
|  |                     let v_b = b_0 + b_1 * factor; | ||||||
|  |                     return fcmp(&v_a, &v_b); | ||||||
|  |                 }); | ||||||
|  |                 // queue.reverse();
 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         println!(); | ||||||
|  | 
 | ||||||
|  |         println!(); | ||||||
|  |         if !found { | ||||||
|  |             eprintln!( | ||||||
|  |                 "No route from {} to {} found!", | ||||||
|  |                 start_sys_name, goal_sys_name | ||||||
|  |             ); | ||||||
|  |             return Vec::new(); | ||||||
|  |         } | ||||||
|  |         let points = self.preload_points(); | ||||||
|  |         let mut v: Vec<(&System, &Point)> = Vec::new(); | ||||||
|  |         let mut prev_sys_id = goal_sys.id; | ||||||
|  |         loop { | ||||||
|  |             if let Some(sys) = self.systems.get(&prev_sys_id) { | ||||||
|  |                 v.push((sys, points[&sys.id])); | ||||||
|  |             } else { | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |             match prev.get(&prev_sys_id) { | ||||||
|  |                 Some(sys_id) => prev_sys_id = *sys_id, | ||||||
|  |                 None => { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         v.reverse(); | ||||||
|  |         return v; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn route_greedy( | ||||||
|  |         &self, | ||||||
|  |         src: &[f32; 3], | ||||||
|  |         dst: &[f32; 3], | ||||||
|  |         range: f32, | ||||||
|  |     ) -> Vec<(&System, &Point)> { | ||||||
|  |         println!("Running Greedy-Search"); | ||||||
|  |         let start_sys = self.closest(src); | ||||||
|  |         let goal_sys = self.closest(dst); | ||||||
|  |         let start_sys_name = self.systems.get(&start_sys.id).unwrap().name.clone(); | ||||||
|  |         let goal_sys_name = self.systems.get(&goal_sys.id).unwrap().name.clone(); | ||||||
|  |         { | ||||||
|  |             let d = dist(src, dst); | ||||||
|  |             println!( | ||||||
|  |                 "Plotting route from {} to {}...", | ||||||
|  |                 start_sys_name, goal_sys_name | ||||||
|  |             ); | ||||||
|  |             println!( | ||||||
|  |                 "Jump Range: {} Ly, Distance: {} Ly, Theoretical Jumps: {}", | ||||||
|  |                 range, | ||||||
|  |                 d, | ||||||
|  |                 d / range | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         let total = self.tree.size() as f32; | ||||||
|  |         let mut prev = FnvHashMap::default(); | ||||||
|  |         let mut seen = FnvHashSet::default(); | ||||||
|  |         let t_start = Instant::now(); | ||||||
|  |         let mut found = false; | ||||||
|  |         let mut maxd = 0; | ||||||
|  |         let mut queue: Vec<(f32, f32, usize, &Point)> = Vec::new(); | ||||||
|  |         queue.push(( | ||||||
|  |             -self.mult(goal_sys.id), | ||||||
|  |             start_sys.distp2(goal_sys), | ||||||
|  |             0, | ||||||
|  |             &start_sys, | ||||||
|  |         )); | ||||||
|  |         seen.insert(start_sys.id); | ||||||
|  |         while !(queue.is_empty() || found) { | ||||||
|  |             std::io::stdout().flush().unwrap(); | ||||||
|  |             while let Some((_, _, depth, sys)) = queue.pop() { | ||||||
|  |                 if depth > maxd { | ||||||
|  |                     maxd = depth; | ||||||
|  |                     print!( | ||||||
|  |                         "[{}] Depth: {}, Queue: {}, Seen: {} ({:.02}%)     \r", | ||||||
|  |                         format_duration(t_start.elapsed()), | ||||||
|  |                         depth, | ||||||
|  |                         queue.len(), | ||||||
|  |                         seen.len(), | ||||||
|  |                         ((seen.len() * 100) as f32) / total | ||||||
|  |                     ); | ||||||
|  |                 } | ||||||
|  |                 if sys.id == goal_sys.id { | ||||||
|  |                     found = true; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 queue.extend( | ||||||
|  |                     self.neighbours(&sys, range) | ||||||
|  |                         .filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id))) | ||||||
|  |                         .filter(|&nb| seen.insert(nb.id)) | ||||||
|  |                         .map(|nb| { | ||||||
|  |                             prev.insert(nb.id, sys.id); | ||||||
|  |                             return (-self.mult(nb.id), nb.distp2(goal_sys), depth + 1, nb); | ||||||
|  |                         }), | ||||||
|  |                 ); | ||||||
|  |                 queue.sort_by(|a, b| fcmp(&a.0, &b.0).then(fcmp(&a.1, &b.1))); | ||||||
|  |                 queue.reverse(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         println!(); | ||||||
|  |         if !found { | ||||||
|  |             eprintln!( | ||||||
|  |                 "No route from {} to {} found!", | ||||||
|  |                 start_sys_name, goal_sys_name | ||||||
|  |             ); | ||||||
|  |             return Vec::new(); | ||||||
|  |         } | ||||||
|  |         let points = self.preload_points(); | ||||||
|  |         let mut v: Vec<(&System, &Point)> = Vec::new(); | ||||||
|  |         let mut prev_sys_id = goal_sys.id; | ||||||
|  |         loop { | ||||||
|  |             if let Some(sys) = self.systems.get(&prev_sys_id) { | ||||||
|  |                 v.push((sys, points[&sys.id])); | ||||||
|  |             } else { | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |             match prev.get(&prev_sys_id) { | ||||||
|  |                 Some(sys_id) => prev_sys_id = *sys_id, | ||||||
|  |                 None => { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         v.reverse(); | ||||||
|  |         return v; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn route_bfs(&self, src: &[f32; 3], dst: &[f32; 3], range: f32) -> Vec<(&System, &Point)> { | ||||||
|  |         println!("Running BFS"); | ||||||
|  |         let start_sys = self.closest(src); | ||||||
|  |         let goal_sys = self.closest(dst); | ||||||
|  |         let start_sys_name = self.systems.get(&start_sys.id).unwrap().name.clone(); | ||||||
|  |         let goal_sys_name = self.systems.get(&goal_sys.id).unwrap().name.clone(); | ||||||
|         { |         { | ||||||
|             let d = dist(src, dst); |             let d = dist(src, dst); | ||||||
|             let start_sys_name = self.systems.get(&start_sys.id).unwrap().name.clone(); |  | ||||||
|             let goal_sys_name = self.systems.get(&goal_sys.id).unwrap().name.clone(); |  | ||||||
|             println!( |             println!( | ||||||
|                 "Plotting route from {} to {}...", |                 "Plotting route from {} to {}...", | ||||||
|                 start_sys_name, goal_sys_name |                 start_sys_name, goal_sys_name | ||||||
|  | @ -314,6 +633,10 @@ impl Router { | ||||||
|         } |         } | ||||||
|         println!(); |         println!(); | ||||||
|         if !found { |         if !found { | ||||||
|  |             eprintln!( | ||||||
|  |                 "No route from {} to {} found!", | ||||||
|  |                 start_sys_name, goal_sys_name | ||||||
|  |             ); | ||||||
|             return Vec::new(); |             return Vec::new(); | ||||||
|         } |         } | ||||||
|         let points = self.preload_points(); |         let points = self.preload_points(); | ||||||
|  | @ -344,12 +667,31 @@ fn main() { | ||||||
|     let router: Router = Router::new(&path); |     let router: Router = Router::new(&path); | ||||||
|     println!("Done in {}!", format_duration(t_load.elapsed())); |     println!("Done in {}!", format_duration(t_load.elapsed())); | ||||||
|     let t_route = Instant::now(); |     let t_route = Instant::now(); | ||||||
|     let route = router.name_multiroute(&opts.systems, opts.range); |     let route = if opts.permute || opts.full_permute { | ||||||
|  |         router.best_name_multiroute( | ||||||
|  |             &opts.systems, | ||||||
|  |             opts.range, | ||||||
|  |             opts.full_permute, | ||||||
|  |             opts.mode, | ||||||
|  |             opts.factor.unwrap_or(1.0), | ||||||
|  |         ) | ||||||
|  |     } else { | ||||||
|  |         router.name_multiroute( | ||||||
|  |             &opts.systems, | ||||||
|  |             opts.range, | ||||||
|  |             opts.mode, | ||||||
|  |             opts.factor.unwrap_or(1.0), | ||||||
|  |         ) | ||||||
|  |     }; | ||||||
|     println!( |     println!( | ||||||
|         "Done in {} ({} Jumps)!\n", |         "Done in {} ({} Jumps)!\n", | ||||||
|         format_duration(t_route.elapsed()), |         format_duration(t_route.elapsed()), | ||||||
|         route.len(), |         route.len(), | ||||||
|     ); |     ); | ||||||
|  |     if route.len() == 0 { | ||||||
|  |         eprintln!("No route found!"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|     let mut total: f32 = 0.0; |     let mut total: f32 = 0.0; | ||||||
|     for ((sys1, p1), (_, p2)) in route.iter().zip(route.iter().skip(1)) { |     for ((sys1, p1), (_, p2)) in route.iter().zip(route.iter().skip(1)) { | ||||||
|         let dist = p1.distp(p2); |         let dist = p1.distp(p2); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue