Modified routefinder a-star algorithm to filter before inserting into the input queue

This commit is contained in:
2023-06-24 15:12:09 +02:00
parent 3bb5a75878
commit 5abfe4bfb6
3 changed files with 114 additions and 30 deletions

View File

@@ -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(())
}

View File

@@ -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);
}

View File

@@ -95,7 +95,7 @@ impl System {
} else if self.star_flags & IS_WDRF != 0 {
1.5
} else {
0.
1.
}
}
}