From 5abfe4bfb6d02b8dd6e61e2d1ec0f94ae2fa3d1e Mon Sep 17 00:00:00 2001 From: TQ Hirsch Date: Sat, 24 Jun 2023 15:12:09 +0200 Subject: [PATCH] Modified routefinder a-star algorithm to filter before inserting into the input queue --- src/bin/regionfilter.rs | 5 +- src/bin/routefinder.rs | 137 ++++++++++++++++++++++++++++++++-------- src/bindata.rs | 2 +- 3 files changed, 114 insertions(+), 30 deletions(-) diff --git a/src/bin/regionfilter.rs b/src/bin/regionfilter.rs index 2b94e98..ab3a56f 100644 --- a/src/bin/regionfilter.rs +++ b/src/bin/regionfilter.rs @@ -127,8 +127,9 @@ fn main() -> anyhow::Result<()> { let mut name_pos = 0; for mut sys in f.filter_map(id).filter(|sys| region.contains(sys.coords)) { - sys.write_name(&mut reg_name)?; - sys.write_to(&mut reg_data); + sys.name_off = name_pos; + name_pos += sys.write_name(&mut reg_name)?; + sys.write_to(&mut reg_data)?; } Ok(()) } diff --git a/src/bin/routefinder.rs b/src/bin/routefinder.rs index b1bce2e..8b92028 100644 --- a/src/bin/routefinder.rs +++ b/src/bin/routefinder.rs @@ -1,5 +1,6 @@ use std::{io::Cursor, cmp::{Ordering, Reverse}, collections::{VecDeque, BinaryHeap, HashMap}, hash::Hash}; +use std::collections::{BTreeMap, BTreeSet}; use edalyze::bindata::*; use rstar::primitives::{PointWithData, GeomWithData}; @@ -29,6 +30,7 @@ impl From for IndexData { } } +#[derive(Copy, Clone)] struct SearchNode { id: usize, cost: f32, @@ -37,7 +39,7 @@ struct SearchNode { } trait SearchState { - type Id; + type Id: Ord+Hash+Copy; fn cost(&self) -> f32; fn heuristic(&self) -> f32; fn id(&self) -> Self::Id; @@ -189,6 +191,77 @@ impl> SearchEnv { } } +/// Unique priority queue +/// This contains a list of items, each with an ID and a weight. Only the minimum weight for a given item is kept. +struct UniquePQueue { + node_data: HashMap, + queue: BTreeSet<(Weight, Id)>, +} +impl UniquePQueue +where I: Hash+Ord+Copy, +W: Ord+Copy, +D: Clone { + pub fn insert(&mut self, id: I, weight: W, data: D) { + if let Some((o_weight, o_data)) = self.node_data.get_mut(&id) { + if *o_weight < weight { + return; + } + self.queue.remove(&(*o_weight, id)); + *o_weight = weight; + *o_data = data; + } else { + self.node_data.insert(id, (weight, data)); + } + + self.queue.insert((weight, id)); + } + + pub fn clear(&mut self) { + self.node_data.clear(); + self.queue.clear(); + } + + pub fn is_empty(&self) -> bool { + self.queue.is_empty() + } + + pub fn len(&self) -> usize { + self.queue.len() + } + pub fn pop_next(&mut self) -> Option { + self.queue.pop_first().map(|(_,d)| self.node_data[&d].1.clone()) + } + + pub fn new() -> Self { + Self { + node_data: HashMap::new(), + queue: BTreeSet::new(), + } + } +} + +#[derive(Copy, Clone)] +struct TOf32(f32); + +impl Ord for TOf32 { + fn cmp(&self, other: &Self) -> Ordering { + self.0.total_cmp(&other.0) + } +} +impl PartialOrd for TOf32 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for TOf32 { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for TOf32 {} + // BFS struct BFS(VecDeque); impl SearchQueue for BFS { @@ -222,13 +295,14 @@ impl PartialEq for CostCompare { } + + struct BFS1{ q: BinaryHeap>, seen: HashMap, } -impl SearchQueue for BFS1 -where T::Id: Hash+Eq { +impl SearchQueue for BFS1 { fn q_next(&mut self) -> Option { self.q.pop().map(|x| x.0) } fn q_empty(&self) -> bool { self.q.is_empty() } fn q_push(&mut self, item: T) { @@ -270,33 +344,23 @@ impl Default for DFS { } struct AStar{ - q: BinaryHeap>, - seen: HashMap, + q: UniquePQueue, } -impl SearchQueue for AStar where T::Id: Eq + Hash { - fn q_next(&mut self) -> Option { self.q.pop().map(|x| x.0) } +impl SearchQueue for AStar{ + fn q_next(&mut self) -> Option { self.q.pop_next() } fn q_empty(&self) -> bool { self.q.is_empty() } fn q_push(&mut self, item: T) { - let id = item.id(); - if let Some(odist) = self.seen.get(&id) { - if *odist < item.cost() { - return; - } - } - self.seen.insert(id, item.cost()); - self.q.push(Reverse(item)); + self.q.insert(item.id(), TOf32(item.cost() + item.heuristic()), item); } fn q_clear(&mut self) { self.q.clear(); - self.seen.clear(); } fn q_len(&self) -> usize { self.q.len() } } -impl Default for AStar { +impl Default for AStar { fn default() -> Self { Self { - q: BinaryHeap::new(), - seen: HashMap::new(), + q: UniquePQueue::new(), } } @@ -311,6 +375,11 @@ struct Options { jump_range: f32, #[structopt(short="j", default_value="10")] cost_factor: f32, + #[structopt(short="f", default_value="2MASS J07523444-2626443")] + start: String, + #[structopt(short="t", default_value="Angosk OM-W d1-0")] + end: String, + } fn main() -> anyhow::Result<()> { @@ -335,11 +404,22 @@ fn main() -> anyhow::Result<()> { eprintln!("Done reading {} systems", systems.len()); let init_system = systems.iter().enumerate() - .find(|(_,sys)| sys.sys_data.name == "Haffner 18 LSS 27") + .filter(|(_,sys)| sys.sys_data.name == opts.start) + // .filter(|(_,sys)| sys.sys_data.name == "Haffner 18 LSS 27") + .map(|item| { + eprintln!("Start: {:?}", item.1.sys_data.coords); + item + }) + .next() .expect("init system should exist") .0; let target_system = systems.iter().enumerate() - .find(|(_,sys)| sys.sys_data.name == "Angosk OM-W d1-0") + .filter(|(_,sys)| sys.sys_data.name == opts.end) + .map(|item| { + eprintln!("End: {:?}", item.1.sys_data.coords); + item + }) + .next() .expect("init system should exist") .0; @@ -362,27 +442,30 @@ fn main() -> anyhow::Result<()> { let mut total_dist = 0.; while cur_id != env.init_system { let sys = &env.meta[cur_id]; - stack.push((&sys.sys_data.name, sys.last.distance, sys.last.uses_jumponium)); + stack.push((&sys.sys_data, sys.last.distance, sys.last.uses_jumponium)); total_dist += sys.last.distance; cur_id = sys.last.system; } let mut jcount = 0; - for (name, dist, usej) in stack.iter().rev().copied() { - eprintln!(" {name:-40}: {} {dist:3.0}", if usej { "j" } else { "-" }); + for (sys_data, dist, usej) in stack.iter().rev().copied() { + let jflag = if usej { "j" } else { "-" }; + let name = &sys_data.name; + let fflag = if sys_data.star_flags & IS_FUEL != 0 { "f" } else { "-" }; + println!(" {name:-40}: {fflag}{jflag} {dist:3.0}", ); if usej { jcount += 1; } } let opt_dist = env.meta[env.init_system].sys_data.coords.distance(env.meta[env.target_system].sys_data.coords); let eff = 100. * opt_dist / total_dist; - eprintln!("Total distance: {total_dist} ({eff:5.2}% eff)"); - eprintln!("{jcount} boosts used"); + println!("Total distance: {total_dist} ({eff:5.2}% efficiency)"); + println!("{jcount} boosts used; {} jumps", stack.len()); } else { - eprintln!("Unreachable after {} visits", env.visits); + println!("Unreachable after {} visits", env.visits); std::process::exit(1); } diff --git a/src/bindata.rs b/src/bindata.rs index aa2a58c..76701e2 100644 --- a/src/bindata.rs +++ b/src/bindata.rs @@ -95,7 +95,7 @@ impl System { } else if self.star_flags & IS_WDRF != 0 { 1.5 } else { - 0. + 1. } } }