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;
|
let mut name_pos = 0;
|
||||||
|
|
||||||
for mut sys in f.filter_map(id).filter(|sys| region.contains(sys.coords)) {
|
for mut sys in f.filter_map(id).filter(|sys| region.contains(sys.coords)) {
|
||||||
sys.write_name(&mut reg_name)?;
|
sys.name_off = name_pos;
|
||||||
sys.write_to(&mut reg_data);
|
name_pos += sys.write_name(&mut reg_name)?;
|
||||||
|
sys.write_to(&mut reg_data)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
use std::{io::Cursor, cmp::{Ordering, Reverse}, collections::{VecDeque, BinaryHeap, HashMap}, hash::Hash};
|
use std::{io::Cursor, cmp::{Ordering, Reverse}, collections::{VecDeque, BinaryHeap, HashMap}, hash::Hash};
|
||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use edalyze::bindata::*;
|
use edalyze::bindata::*;
|
||||||
use rstar::primitives::{PointWithData, GeomWithData};
|
use rstar::primitives::{PointWithData, GeomWithData};
|
||||||
@@ -29,6 +30,7 @@ impl From<System> for IndexData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
struct SearchNode {
|
struct SearchNode {
|
||||||
id: usize,
|
id: usize,
|
||||||
cost: f32,
|
cost: f32,
|
||||||
@@ -37,7 +39,7 @@ struct SearchNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait SearchState {
|
trait SearchState {
|
||||||
type Id;
|
type Id: Ord+Hash+Copy;
|
||||||
fn cost(&self) -> f32;
|
fn cost(&self) -> f32;
|
||||||
fn heuristic(&self) -> f32;
|
fn heuristic(&self) -> f32;
|
||||||
fn id(&self) -> Self::Id;
|
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
|
// BFS
|
||||||
struct BFS<T>(VecDeque<T>);
|
struct BFS<T>(VecDeque<T>);
|
||||||
impl<T> SearchQueue<T> for BFS<T> {
|
impl<T> SearchQueue<T> for BFS<T> {
|
||||||
@@ -222,13 +295,14 @@ impl<T: SearchState> PartialEq for CostCompare<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct BFS1<T: SearchState>{
|
struct BFS1<T: SearchState>{
|
||||||
q: BinaryHeap<CostCompare<T>>,
|
q: BinaryHeap<CostCompare<T>>,
|
||||||
seen: HashMap<T::Id, f32>,
|
seen: HashMap<T::Id, f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SearchState> SearchQueue<T> for BFS1<T>
|
impl<T: SearchState> SearchQueue<T> for BFS1<T> {
|
||||||
where T::Id: Hash+Eq {
|
|
||||||
fn q_next(&mut self) -> Option<T> { self.q.pop().map(|x| x.0) }
|
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_empty(&self) -> bool { self.q.is_empty() }
|
||||||
fn q_push(&mut self, item: T) {
|
fn q_push(&mut self, item: T) {
|
||||||
@@ -270,33 +344,23 @@ impl<T> Default for DFS<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct AStar<T: SearchState>{
|
struct AStar<T: SearchState>{
|
||||||
q: BinaryHeap<Reverse<T>>,
|
q: UniquePQueue<T::Id, TOf32, T>,
|
||||||
seen: HashMap<T::Id, f32>,
|
|
||||||
}
|
}
|
||||||
impl<T: Ord + SearchState> SearchQueue<T> for AStar<T> where T::Id: Eq + Hash {
|
impl<T: SearchState + Clone> SearchQueue<T> for AStar<T>{
|
||||||
fn q_next(&mut self) -> Option<T> { self.q.pop().map(|x| x.0) }
|
fn q_next(&mut self) -> Option<T> { self.q.pop_next() }
|
||||||
fn q_empty(&self) -> bool { self.q.is_empty() }
|
fn q_empty(&self) -> bool { self.q.is_empty() }
|
||||||
fn q_push(&mut self, item: T) {
|
fn q_push(&mut self, item: T) {
|
||||||
let id = item.id();
|
self.q.insert(item.id(), TOf32(item.cost() + item.heuristic()), item);
|
||||||
if let Some(odist) = self.seen.get(&id) {
|
|
||||||
if *odist < item.cost() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.seen.insert(id, item.cost());
|
|
||||||
self.q.push(Reverse(item));
|
|
||||||
}
|
}
|
||||||
fn q_clear(&mut self) {
|
fn q_clear(&mut self) {
|
||||||
self.q.clear();
|
self.q.clear();
|
||||||
self.seen.clear();
|
|
||||||
}
|
}
|
||||||
fn q_len(&self) -> usize { self.q.len() }
|
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 {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
q: BinaryHeap::new(),
|
q: UniquePQueue::new(),
|
||||||
seen: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,6 +375,11 @@ struct Options {
|
|||||||
jump_range: f32,
|
jump_range: f32,
|
||||||
#[structopt(short="j", default_value="10")]
|
#[structopt(short="j", default_value="10")]
|
||||||
cost_factor: f32,
|
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<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
@@ -335,11 +404,22 @@ fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
eprintln!("Done reading {} systems", systems.len());
|
eprintln!("Done reading {} systems", systems.len());
|
||||||
let init_system = systems.iter().enumerate()
|
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")
|
.expect("init system should exist")
|
||||||
.0;
|
.0;
|
||||||
let target_system = systems.iter().enumerate()
|
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")
|
.expect("init system should exist")
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
@@ -362,27 +442,30 @@ fn main() -> anyhow::Result<()> {
|
|||||||
let mut total_dist = 0.;
|
let mut total_dist = 0.;
|
||||||
while cur_id != env.init_system {
|
while cur_id != env.init_system {
|
||||||
let sys = &env.meta[cur_id];
|
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;
|
total_dist += sys.last.distance;
|
||||||
cur_id = sys.last.system;
|
cur_id = sys.last.system;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let mut jcount = 0;
|
let mut jcount = 0;
|
||||||
for (name, dist, usej) in stack.iter().rev().copied() {
|
for (sys_data, dist, usej) in stack.iter().rev().copied() {
|
||||||
eprintln!(" {name:-40}: {} {dist:3.0}", if usej { "j" } else { "-" });
|
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 {
|
if usej {
|
||||||
jcount += 1;
|
jcount += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let opt_dist = env.meta[env.init_system].sys_data.coords.distance(env.meta[env.target_system].sys_data.coords);
|
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;
|
let eff = 100. * opt_dist / total_dist;
|
||||||
eprintln!("Total distance: {total_dist} ({eff:5.2}% eff)");
|
println!("Total distance: {total_dist} ({eff:5.2}% efficiency)");
|
||||||
eprintln!("{jcount} boosts used");
|
println!("{jcount} boosts used; {} jumps", stack.len());
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Unreachable after {} visits", env.visits);
|
println!("Unreachable after {} visits", env.visits);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ impl System {
|
|||||||
} else if self.star_flags & IS_WDRF != 0 {
|
} else if self.star_flags & IS_WDRF != 0 {
|
||||||
1.5
|
1.5
|
||||||
} else {
|
} else {
|
||||||
0.
|
1.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user