Modified routefinder a-star algorithm to filter before inserting into the input queue
This commit is contained in:
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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<System> 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<Q: SearchQueue<SearchNode>> SearchEnv<Q> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<Id, Weight, Data> {
|
||||
node_data: HashMap<Id, (Weight, Data)>,
|
||||
queue: BTreeSet<(Weight, Id)>,
|
||||
}
|
||||
impl<I,W,D> UniquePQueue<I,W,D>
|
||||
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<D> {
|
||||
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<Ordering> {
|
||||
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<T>(VecDeque<T>);
|
||||
impl<T> SearchQueue<T> for BFS<T> {
|
||||
@@ -222,13 +295,14 @@ impl<T: SearchState> PartialEq for CostCompare<T> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct BFS1<T: SearchState>{
|
||||
q: BinaryHeap<CostCompare<T>>,
|
||||
seen: HashMap<T::Id, f32>,
|
||||
}
|
||||
|
||||
impl<T: SearchState> SearchQueue<T> for BFS1<T>
|
||||
where T::Id: Hash+Eq {
|
||||
impl<T: SearchState> SearchQueue<T> for BFS1<T> {
|
||||
fn q_next(&mut self) -> Option<T> { 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<T> Default for DFS<T> {
|
||||
}
|
||||
|
||||
struct AStar<T: SearchState>{
|
||||
q: BinaryHeap<Reverse<T>>,
|
||||
seen: HashMap<T::Id, f32>,
|
||||
q: UniquePQueue<T::Id, TOf32, T>,
|
||||
}
|
||||
impl<T: Ord + SearchState> SearchQueue<T> for AStar<T> where T::Id: Eq + Hash {
|
||||
fn q_next(&mut self) -> Option<T> { self.q.pop().map(|x| x.0) }
|
||||
impl<T: SearchState + Clone> SearchQueue<T> for AStar<T>{
|
||||
fn q_next(&mut self) -> Option<T> { 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<T: Ord+SearchState> Default for AStar<T> {
|
||||
impl<T: SearchState+Clone> Default for AStar<T> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ impl System {
|
||||
} else if self.star_flags & IS_WDRF != 0 {
|
||||
1.5
|
||||
} else {
|
||||
0.
|
||||
1.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user