Lots of changes; search works but is slow
This commit is contained in:
@@ -28,7 +28,6 @@ fn parse_coords(c: TapeObject) -> Result<Point, &'static str> {
|
||||
|
||||
for (k,v) in c {
|
||||
let v = v.as_f64().ok_or("Coord value must be f64")? as f32;
|
||||
let v = (v * 32.) as i32;
|
||||
match k {
|
||||
"x" => pt.0 = v,
|
||||
"y" => pt.1 = v,
|
||||
@@ -114,13 +113,13 @@ fn main() -> anyhow::Result<()> {
|
||||
// Small region
|
||||
#[cfg(ignore)]
|
||||
let region = Box::from_corners(
|
||||
Point(11900*32, -3800*32, -11510*32),
|
||||
Point(17400*32, 3800*32, -6500*32),
|
||||
Point(11900., -3800., -11510.),
|
||||
Point(17400., 3800., -6500.),
|
||||
);
|
||||
// Everything southeast of Haffner 18 LSS 27
|
||||
let region = Box::from_corners(
|
||||
Point(11900*32, -3800*32, -18510*32),
|
||||
Point(64400*32, 3800*32, -6500*32),
|
||||
Point(11900., -3800., -18510.),
|
||||
Point(64400., 3800., -6500.),
|
||||
);
|
||||
|
||||
let mut reg_name = std::io::BufWriter::new(std::fs::File::create("region.nam")?);
|
||||
@@ -128,23 +127,8 @@ fn main() -> anyhow::Result<()> {
|
||||
let mut name_pos = 0;
|
||||
|
||||
for mut sys in f.filter_map(id).filter(|sys| region.contains(sys.coords)) {
|
||||
sys.name_off = name_pos as usize;
|
||||
let hdr = [sys.name.len().min(255) as u8];
|
||||
if hdr[0] == 255 {
|
||||
eprintln!("WARNING: Long system name {}", sys.name);
|
||||
}
|
||||
reg_name.write(&hdr[..])?;
|
||||
reg_name.write(&sys.name.as_bytes()[..hdr[0] as usize])?;
|
||||
name_pos += hdr[0] as u32 + 1;
|
||||
|
||||
reg_data.write(&sys.coords.0 .to_be_bytes()[..])?;
|
||||
reg_data.write(&sys.coords.1 .to_be_bytes()[..])?;
|
||||
reg_data.write(&sys.coords.2 .to_be_bytes()[..])?;
|
||||
|
||||
let rest = (sys.star_flags as u32) + (name_pos << 4);
|
||||
reg_data.write(&rest.to_be_bytes()[..])?;
|
||||
sys.write_name(&mut reg_name)?;
|
||||
sys.write_to(&mut reg_data);
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
|
||||
use std::{io::Cursor, cmp::{Ordering, Reverse}, collections::{VecDeque, BinaryHeap}};
|
||||
use std::{io::Cursor, cmp::{Ordering, Reverse}, collections::{VecDeque, BinaryHeap, HashMap}, hash::Hash};
|
||||
|
||||
use edalyze::bindata::*;
|
||||
use rstar::primitives::{PointWithData, GeomWithData};
|
||||
|
||||
#[derive(Default)]
|
||||
struct PrevLink {
|
||||
system: usize,
|
||||
distance: f32,
|
||||
uses_jumponium: bool,
|
||||
}
|
||||
|
||||
struct IndexData {
|
||||
cost: f32,
|
||||
reachable: bool,
|
||||
last: usize,
|
||||
last: PrevLink,
|
||||
sys_data: System,
|
||||
}
|
||||
|
||||
@@ -16,7 +23,7 @@ impl From<System> for IndexData {
|
||||
Self {
|
||||
cost: f32::INFINITY,
|
||||
reachable: false,
|
||||
last: 0,
|
||||
last: PrevLink::default(),
|
||||
sys_data: sys,
|
||||
}
|
||||
}
|
||||
@@ -26,11 +33,14 @@ struct SearchNode {
|
||||
id: usize,
|
||||
cost: f32,
|
||||
heuristic: f32,
|
||||
last: (usize, bool)
|
||||
}
|
||||
|
||||
trait SearchState {
|
||||
type Id;
|
||||
fn cost(&self) -> f32;
|
||||
fn heuristic(&self) -> f32;
|
||||
fn id(&self) -> Self::Id;
|
||||
}
|
||||
|
||||
// ordering tuned for BinaryHeap
|
||||
@@ -53,11 +63,28 @@ impl PartialEq for SearchNode {
|
||||
}
|
||||
impl Eq for SearchNode {}
|
||||
|
||||
impl SearchState for SearchNode {
|
||||
type Id = usize;
|
||||
|
||||
fn cost(&self) -> f32 {
|
||||
self.cost
|
||||
}
|
||||
|
||||
fn heuristic(&self) -> f32 {
|
||||
self.heuristic
|
||||
}
|
||||
|
||||
fn id(&self) -> Self::Id {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
trait SearchQueue<T> {
|
||||
fn q_next(&mut self) -> Option<T>;
|
||||
fn q_empty(&self) -> bool;
|
||||
fn q_push(&mut self, item: T);
|
||||
fn q_clear(&mut self);
|
||||
fn q_len(&self) -> usize;
|
||||
}
|
||||
|
||||
struct SearchEnv<Q> {
|
||||
@@ -68,6 +95,7 @@ struct SearchEnv<Q> {
|
||||
target_system: usize,
|
||||
jumponium_cost_factor: f32,
|
||||
queue: Q,
|
||||
visits: usize,
|
||||
}
|
||||
|
||||
impl<Q: SearchQueue<SearchNode>> SearchEnv<Q> {
|
||||
@@ -75,13 +103,13 @@ impl<Q: SearchQueue<SearchNode>> SearchEnv<Q> {
|
||||
for item in self.meta.iter_mut() {
|
||||
item.cost = f32::INFINITY;
|
||||
item.reachable = false;
|
||||
item.last = 0;
|
||||
}
|
||||
self.queue.q_clear();
|
||||
let init_node = SearchNode{
|
||||
id: self.init_system,
|
||||
cost: 0,
|
||||
cost: 0.,
|
||||
heuristic: self.heuristic(self.init_system),
|
||||
last: (self.init_system, false),
|
||||
};
|
||||
self.queue.q_push(init_node);
|
||||
}
|
||||
@@ -89,8 +117,34 @@ impl<Q: SearchQueue<SearchNode>> SearchEnv<Q> {
|
||||
fn is_done(&self) -> bool {
|
||||
self.queue.q_empty() || self.meta[self.target_system].reachable
|
||||
}
|
||||
fn search(&mut self) {
|
||||
//
|
||||
fn search(&mut self) -> Option<f32> {
|
||||
self.reset();
|
||||
eprintln!("");
|
||||
while let Some(node) = self.queue.q_next() {
|
||||
// check whether node should be re-expanded
|
||||
{
|
||||
let scoord = self.meta[node.last.0].sys_data.coords;
|
||||
let n = &mut self.meta[node.id];
|
||||
if n.reachable && n.cost < node.cost {
|
||||
continue;
|
||||
}
|
||||
n.cost = node.cost;
|
||||
n.reachable = true;
|
||||
n.last = PrevLink {
|
||||
uses_jumponium: node.last.1,
|
||||
system: node.last.0,
|
||||
distance: n.sys_data.coords.distance(scoord),
|
||||
};
|
||||
}
|
||||
self.visits += 1;
|
||||
if self.visits % 1000 == 0 {
|
||||
eprintln!("\x1b[1A\x1b[K{}k ({} in queue)", self.visits/100, self.queue.q_len());
|
||||
}
|
||||
self.visit_star(node.id, node.cost);
|
||||
}
|
||||
|
||||
let tsys = &self.meta[self.target_system];
|
||||
tsys.reachable.then_some(tsys.cost)
|
||||
}
|
||||
|
||||
fn heuristic(&self, sys_id: usize) -> f32 {
|
||||
@@ -100,16 +154,32 @@ impl<Q: SearchQueue<SearchNode>> SearchEnv<Q> {
|
||||
(dist - self.jdist(sys_id)) / (4. * self.base_jump)
|
||||
}
|
||||
|
||||
fn visit_star(&self, sys_id: usize, base_cost: f32) {
|
||||
fn visit_star(&mut self, sys_id: usize, base_cost: f32) {
|
||||
let star_jump = self.jdist(sys_id);
|
||||
let star_jump_sq = self.boosted_jump * self.boosted_jump;
|
||||
let star_jump_sq = star_jump * star_jump;
|
||||
let base_jump_sq = self.base_jump * self.base_jump;
|
||||
let augm_jump_sq = base_jump_sq * 4.;
|
||||
|
||||
let max_jump_sq = augm_jump_sq.max(star_jump_sq);
|
||||
//eprintln!("Max jump: {max_jump_sq}");
|
||||
|
||||
let cur_pos = self.meta[sys_id].sys_data.coords;
|
||||
for n_star in self.rst.locate_within_distance(cur_pos, (max_jump_sq * 1024.).ceil() as i32) {
|
||||
for n_star in self.rst.locate_within_distance(cur_pos, max_jump_sq) {
|
||||
//eprintln!("Examining {}", self.meta[n_star.data].sys_data.name);
|
||||
let dist_sq = n_star.geom().distance_sq(cur_pos);
|
||||
let (cost_factor, _range, uses_jumponium) = if dist_sq > max_jump_sq {
|
||||
continue;
|
||||
} else if dist_sq < star_jump_sq {
|
||||
(1., star_jump, false)
|
||||
} else {
|
||||
// must be a jumponium jump
|
||||
(self.jumponium_cost_factor, 2. * self.base_jump, true)
|
||||
};
|
||||
|
||||
// TODO: make an estimate of the fuel used
|
||||
let cost = base_cost + cost_factor;
|
||||
let heuristic = self.heuristic(n_star.data);
|
||||
self.queue.q_push(SearchNode { id: n_star.data, cost, heuristic, last: (sys_id, uses_jumponium) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,17 +195,74 @@ impl<T> SearchQueue<T> for BFS<T> {
|
||||
fn q_empty(&self) -> bool { self.0.is_empty() }
|
||||
fn q_push(&mut self, item: T) { self.0.push_back(item); }
|
||||
fn q_clear(&mut self) { self.0.clear(); }
|
||||
fn q_len(&self) -> usize { self.0.len() }
|
||||
}
|
||||
impl<T> Default for BFS<T> {
|
||||
fn default() -> Self { BFS(VecDeque::new()) }
|
||||
}
|
||||
|
||||
struct CostCompare<T: SearchState>(T);
|
||||
impl<T: SearchState> Ord for CostCompare<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
f32::total_cmp(&self.0.cost(), &other.0.cost()).reverse()
|
||||
}
|
||||
}
|
||||
impl<T: SearchState> PartialOrd for CostCompare<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl<T: SearchState> Eq for CostCompare<T> {
|
||||
}
|
||||
impl<T: SearchState> PartialEq for CostCompare<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cmp(other) == Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
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) {
|
||||
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(CostCompare(item));
|
||||
}
|
||||
fn q_clear(&mut self) {
|
||||
self.q.clear();
|
||||
self.seen.clear();
|
||||
}
|
||||
fn q_len(&self) -> usize { self.q.len() }
|
||||
}
|
||||
impl<T: SearchState> Default for BFS1<T> {
|
||||
fn default() -> Self {
|
||||
BFS1{
|
||||
q: BinaryHeap::new(),
|
||||
seen: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct DFS<T>(Vec<T>);
|
||||
impl<T> SearchQueue<T> for DFS<T> {
|
||||
fn q_next(&mut self) -> Option<T> { self.0.pop() }
|
||||
fn q_empty(&self) -> bool { self.0.is_empty() }
|
||||
fn q_push(&mut self, item: T) { self.0.push(item); }
|
||||
fn q_clear(&mut self) { self.0.clear(); }
|
||||
fn q_len(&self) -> usize { self.0.len() }
|
||||
}
|
||||
impl<T> Default for DFS<T> {
|
||||
fn default() -> Self { DFS(Vec::new()) }
|
||||
@@ -147,14 +274,26 @@ impl<T: Ord> SearchQueue<T> for AStar<T> {
|
||||
fn q_empty(&self) -> bool { self.0.is_empty() }
|
||||
fn q_push(&mut self, item: T) { self.0.push(Reverse(item)); }
|
||||
fn q_clear(&mut self) { self.0.clear(); }
|
||||
fn q_len(&self) -> usize { self.0.len() }
|
||||
}
|
||||
impl<T: Ord> Default for AStar<T> {
|
||||
fn default() -> Self { AStar(BinaryHeap::new()) }
|
||||
}
|
||||
|
||||
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
struct Options {
|
||||
#[structopt(short="r", default_value="10")]
|
||||
jump_range: f32,
|
||||
#[structopt(short="j", default_value="10")]
|
||||
cost_factor: f32,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let opts = Options::from_args();
|
||||
|
||||
let mut reg_dat = Cursor::new(std::fs::read("region.dat")?);
|
||||
let mut reg_nam = Cursor::new(std::fs::read("region.nam")?);
|
||||
|
||||
@@ -183,13 +322,48 @@ fn main() -> anyhow::Result<()> {
|
||||
.0;
|
||||
|
||||
let mut env = SearchEnv {
|
||||
rst,meta: systems,base_jump: 10., init_system, target_system,
|
||||
queue: BFS::default()
|
||||
rst,
|
||||
meta: systems,
|
||||
base_jump: opts.jump_range,
|
||||
init_system,
|
||||
target_system,
|
||||
queue: BFS1::default(),
|
||||
jumponium_cost_factor: opts.cost_factor,
|
||||
visits: 0,
|
||||
};
|
||||
if let Some(cost) = env.search() {
|
||||
eprintln!("Reached in {cost}");
|
||||
|
||||
// produce a trace...
|
||||
let mut cur_id = env.target_system;
|
||||
let mut stack = vec![];
|
||||
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));
|
||||
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 { "-" });
|
||||
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");
|
||||
|
||||
|
||||
} else {
|
||||
eprintln!("Unreachable after {} visits", env.visits);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
env.queue.q_push(SearchNode{
|
||||
heuristic: 0., cost: 0., id: 0
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,20 +6,20 @@ pub const IS_FUEL: u8 = 1;
|
||||
pub const IS_WDRF: u8 = 2;
|
||||
pub const IS_NEUT: u8 = 4;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Point(pub i32, pub i32, pub i32);
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct Point(pub f32, pub f32, pub f32);
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Box(pub Point, pub Point);
|
||||
|
||||
const fn isortp(a: i32, b: i32) -> (i32, i32) {
|
||||
fn fsortp(a: f32, b: f32) -> (f32, f32) {
|
||||
if a < b { (a,b) } else { (b,a) }
|
||||
}
|
||||
|
||||
impl Box {
|
||||
pub const fn from_corners(x: Point, y: Point) -> Box {
|
||||
let (ax, bx) = isortp(x.0, y.0);
|
||||
let (ay, by) = isortp(x.1, y.1);
|
||||
let (az, bz) = isortp(x.2, y.2);
|
||||
pub fn from_corners(x: Point, y: Point) -> Box {
|
||||
let (ax, bx) = fsortp(x.0, y.0);
|
||||
let (ay, by) = fsortp(x.1, y.1);
|
||||
let (az, bz) = fsortp(x.2, y.2);
|
||||
|
||||
let pt1 = Point(ax, ay, az);
|
||||
let pt2 = Point(bx, by, bz);
|
||||
@@ -33,7 +33,6 @@ impl Box {
|
||||
(self.0 .1 .. self.1 .1).contains(&x.1) &&
|
||||
(self.0 .2 .. self.1 .2).contains(&x.2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -53,18 +52,18 @@ impl System {
|
||||
return Ok(name_len + 1);
|
||||
}
|
||||
pub fn write_to(&self, writer: &mut dyn Write) -> io::Result<()> {
|
||||
writer.write_i32::<BE>(self.coords.0)?;
|
||||
writer.write_i32::<BE>(self.coords.1)?;
|
||||
writer.write_i32::<BE>(self.coords.2)?;
|
||||
writer.write_i32::<BE>((self.coords.0 * 32.) as i32)?;
|
||||
writer.write_i32::<BE>((self.coords.1 * 32.) as i32)?;
|
||||
writer.write_i32::<BE>((self.coords.2 * 32.) as i32)?;
|
||||
let rest = self.star_flags as u32 + ((self.name_off as u32) << 4);
|
||||
writer.write_u32::<BE>(rest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_from(r: &mut dyn Read) -> io::Result<Self> {
|
||||
let x = r.read_i32::<BE>()?;
|
||||
let y = r.read_i32::<BE>()?;
|
||||
let z = r.read_i32::<BE>()?;
|
||||
let x = r.read_i32::<BE>()? as f32 / 32.;
|
||||
let y = r.read_i32::<BE>()? as f32 / 32.;
|
||||
let z = r.read_i32::<BE>()? as f32 / 32.;
|
||||
let rest = r.read_u32::<BE>()?;
|
||||
let name_off = (rest >> 4) as usize;
|
||||
let star_flags = (rest & 0xF) as u8;
|
||||
@@ -102,7 +101,7 @@ impl System {
|
||||
}
|
||||
|
||||
impl rstar::Point for Point {
|
||||
type Scalar = i32;
|
||||
type Scalar = f32;
|
||||
const DIMENSIONS: usize = 3;
|
||||
fn generate(mut gen: impl FnMut(usize) -> Self::Scalar) -> Self {
|
||||
let x = gen(0);
|
||||
@@ -134,9 +133,9 @@ impl Point {
|
||||
pub fn distance_sq(&self, other: Self) -> f32 {
|
||||
let Point(sx, sy, sz) = *self;
|
||||
let Point(tx, ty, tz) = other;
|
||||
let dx = sx as f32 - tx as f32;
|
||||
let dy = sy as f32 - ty as f32;
|
||||
let dz = sz as f32 - tz as f32;
|
||||
let dx = sx - tx;
|
||||
let dy = sy - ty;
|
||||
let dz = sz - tz;
|
||||
(dx*dx) + (dy*dy) + (dz*dz)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user