Much progress
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -107,6 +107,7 @@ name = "edalyze"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"rstar",
|
||||
"simd-json",
|
||||
"structopt",
|
||||
|
||||
@@ -10,4 +10,5 @@ structopt = "0.3"
|
||||
simd-json = "0.10"
|
||||
rstar = "0.11"
|
||||
zstd = "0.11"
|
||||
anyhow = "1"
|
||||
anyhow = "1"
|
||||
byteorder = "1"
|
||||
|
||||
@@ -3,18 +3,12 @@ use anyhow::anyhow;
|
||||
use simd_json::Node;
|
||||
|
||||
use edalyze::tapetool::{TapeCursor, TapeObject, Value};
|
||||
use edalyze::bindata::{System, Point, Box};
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Point(i32, i32, i32);
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Box(Point, Point);
|
||||
|
||||
const IS_FUEL: u8 = 1;
|
||||
const IS_WDRF: u8 = 2;
|
||||
const IS_NEUT: u8 = 4;
|
||||
|
||||
fn star_typ(sub_type: &str) -> u8 {
|
||||
use edalyze::bindata::{IS_FUEL, IS_WDRF, IS_NEUT};
|
||||
if sub_type.starts_with("Wh") {
|
||||
return IS_WDRF;
|
||||
} else if sub_type.starts_with("N") {
|
||||
@@ -29,38 +23,6 @@ fn star_typ(sub_type: &str) -> u8 {
|
||||
}
|
||||
}
|
||||
|
||||
const fn isortp(a: i32, b: i32) -> (i32, i32) {
|
||||
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);
|
||||
|
||||
let pt1 = Point(ax, ay, az);
|
||||
let pt2 = Point(bx, by, bz);
|
||||
|
||||
Box(pt1, pt2)
|
||||
}
|
||||
|
||||
pub fn contains(&self, x: Point) -> bool {
|
||||
return
|
||||
(self.0 .0 .. self.1 .0).contains(&x.0) &&
|
||||
(self.0 .1 .. self.1 .1).contains(&x.1) &&
|
||||
(self.0 .2 .. self.1 .2).contains(&x.2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct System {
|
||||
coords: Point,
|
||||
name: String,
|
||||
stars: u8,
|
||||
}
|
||||
|
||||
fn parse_coords(c: TapeObject) -> Result<Point, &'static str> {
|
||||
let mut pt = Point::default();
|
||||
|
||||
@@ -113,7 +75,7 @@ fn parse_system<'a>(tape: &'a [Node<'a>]) -> Result<System, &'static str> {
|
||||
"name" => system.name = v.as_str().map(str::to_owned).ok_or("name must be str")?,
|
||||
"coords" => system.coords = parse_coords(v.as_object().ok_or("coords must be object")?)?,
|
||||
"bodies" => {
|
||||
system.stars = v.as_array().ok_or("bodies should be array")?
|
||||
system.star_flags = v.as_array().ok_or("bodies should be array")?
|
||||
.filter_map(parse_body)
|
||||
.map(|x| x.1)
|
||||
.fold(0, |x,y| x|y)
|
||||
@@ -149,28 +111,37 @@ static FILENAME: &str = "/space/data/ed-utils/spansh/galaxy.json.zst";
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let f = edalyze::ioutil::Processor::open_file(FILENAME, processor)?;
|
||||
|
||||
// Small region
|
||||
#[cfg(ignore)]
|
||||
let region = Box::from_corners(
|
||||
Point(11900*32, -3800*32, -11510*32),
|
||||
Point(17400*32, 3800*32, -6500*32),
|
||||
);
|
||||
// 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),
|
||||
);
|
||||
|
||||
let mut reg_name = std::io::BufWriter::new(std::fs::File::create("region.nam")?);
|
||||
let mut reg_data = std::io::BufWriter::new(std::fs::File::create("region.dat")?);
|
||||
let mut name_pos = 0;
|
||||
|
||||
for 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.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.stars as u32) + (name_pos << 4);
|
||||
name_pos += hdr[0] as u32 + 1;
|
||||
let rest = (sys.star_flags as u32) + (name_pos << 4);
|
||||
reg_data.write(&rest.to_be_bytes()[..])?;
|
||||
}
|
||||
|
||||
|
||||
197
src/bin/routefinder.rs
Normal file
197
src/bin/routefinder.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
|
||||
use std::{io::Cursor, cmp::{Ordering, Reverse}, collections::{VecDeque, BinaryHeap}};
|
||||
|
||||
use edalyze::bindata::*;
|
||||
use rstar::primitives::{PointWithData, GeomWithData};
|
||||
|
||||
struct IndexData {
|
||||
cost: f32,
|
||||
reachable: bool,
|
||||
last: usize,
|
||||
sys_data: System,
|
||||
}
|
||||
|
||||
impl From<System> for IndexData {
|
||||
fn from(sys: System) -> Self {
|
||||
Self {
|
||||
cost: f32::INFINITY,
|
||||
reachable: false,
|
||||
last: 0,
|
||||
sys_data: sys,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SearchNode {
|
||||
id: usize,
|
||||
cost: f32,
|
||||
heuristic: f32,
|
||||
}
|
||||
|
||||
trait SearchState {
|
||||
fn cost(&self) -> f32;
|
||||
fn heuristic(&self) -> f32;
|
||||
}
|
||||
|
||||
// ordering tuned for BinaryHeap
|
||||
impl Ord for SearchNode {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
f32::total_cmp(&(self.cost + self.heuristic), &(other.cost + other.heuristic))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for SearchNode {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SearchNode {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.cost + self.heuristic) == (other.cost + other.heuristic)
|
||||
}
|
||||
}
|
||||
impl Eq for SearchNode {}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
struct SearchEnv<Q> {
|
||||
rst: rstar::RTree<GeomWithData<Point, usize>>,
|
||||
meta: Vec<IndexData>,
|
||||
base_jump: f32,
|
||||
init_system: usize,
|
||||
target_system: usize,
|
||||
jumponium_cost_factor: f32,
|
||||
queue: Q,
|
||||
}
|
||||
|
||||
impl<Q: SearchQueue<SearchNode>> SearchEnv<Q> {
|
||||
fn reset(&mut self) {
|
||||
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,
|
||||
heuristic: self.heuristic(self.init_system),
|
||||
};
|
||||
self.queue.q_push(init_node);
|
||||
}
|
||||
|
||||
fn is_done(&self) -> bool {
|
||||
self.queue.q_empty() || self.meta[self.target_system].reachable
|
||||
}
|
||||
fn search(&mut self) {
|
||||
//
|
||||
}
|
||||
|
||||
fn heuristic(&self, sys_id: usize) -> f32 {
|
||||
let tcoord = self.meta[self.target_system].sys_data.coords;
|
||||
let scoord = self.meta[sys_id].sys_data.coords;
|
||||
let dist = tcoord.distance(scoord);
|
||||
(dist - self.jdist(sys_id)) / (4. * self.base_jump)
|
||||
}
|
||||
|
||||
fn visit_star(&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 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);
|
||||
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
fn jdist(&self, sys_id: usize) -> f32 {
|
||||
self.base_jump * self.meta[sys_id].sys_data.jump_scale()
|
||||
}
|
||||
}
|
||||
|
||||
// BFS
|
||||
struct BFS<T>(VecDeque<T>);
|
||||
impl<T> SearchQueue<T> for BFS<T> {
|
||||
fn q_next(&mut self) -> Option<T> { self.0.pop_front() }
|
||||
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(); }
|
||||
}
|
||||
impl<T> Default for BFS<T> {
|
||||
fn default() -> Self { BFS(VecDeque::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(); }
|
||||
}
|
||||
impl<T> Default for DFS<T> {
|
||||
fn default() -> Self { DFS(Vec::new()) }
|
||||
}
|
||||
|
||||
struct AStar<T>(BinaryHeap<Reverse<T>>);
|
||||
impl<T: Ord> SearchQueue<T> for AStar<T> {
|
||||
fn q_next(&mut self) -> Option<T> { self.0.pop().map(|x| x.0) }
|
||||
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(); }
|
||||
}
|
||||
impl<T: Ord> Default for AStar<T> {
|
||||
fn default() -> Self { AStar(BinaryHeap::new()) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let mut reg_dat = Cursor::new(std::fs::read("region.dat")?);
|
||||
let mut reg_nam = Cursor::new(std::fs::read("region.nam")?);
|
||||
|
||||
let mut systems = Vec::with_capacity(reg_dat.get_ref().len() / 16);
|
||||
while let Ok(mut system) = System::read_from(&mut reg_dat) {
|
||||
system.read_name(&mut reg_nam).ok();
|
||||
systems.push(IndexData::from(system));
|
||||
}
|
||||
|
||||
|
||||
let rst = rstar::RTree::bulk_load(
|
||||
systems.iter()
|
||||
.enumerate()
|
||||
.map(|(i,md)| GeomWithData::new(md.sys_data.coords, i))
|
||||
.collect()
|
||||
);
|
||||
|
||||
eprintln!("Done reading {} systems", systems.len());
|
||||
let init_system = systems.iter().enumerate()
|
||||
.find(|(_,sys)| sys.sys_data.name == "Haffner 18 LSS 27")
|
||||
.expect("init system should exist")
|
||||
.0;
|
||||
let target_system = systems.iter().enumerate()
|
||||
.find(|(_,sys)| sys.sys_data.name == "Angosk OM-W d1-0")
|
||||
.expect("init system should exist")
|
||||
.0;
|
||||
|
||||
let mut env = SearchEnv {
|
||||
rst,meta: systems,base_jump: 10., init_system, target_system,
|
||||
queue: BFS::default()
|
||||
};
|
||||
|
||||
env.queue.q_push(SearchNode{
|
||||
heuristic: 0., cost: 0., id: 0
|
||||
});
|
||||
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
146
src/bindata.rs
Normal file
146
src/bindata.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use std::io::{Read, Write, self, Seek};
|
||||
|
||||
use byteorder::{WriteBytesExt, BE, ReadBytesExt};
|
||||
|
||||
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)]
|
||||
pub struct Box(pub Point, pub Point);
|
||||
|
||||
const fn isortp(a: i32, b: i32) -> (i32, i32) {
|
||||
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);
|
||||
|
||||
let pt1 = Point(ax, ay, az);
|
||||
let pt2 = Point(bx, by, bz);
|
||||
|
||||
Box(pt1, pt2)
|
||||
}
|
||||
|
||||
pub fn contains(&self, x: Point) -> bool {
|
||||
return
|
||||
(self.0 .0 .. self.1 .0).contains(&x.0) &&
|
||||
(self.0 .1 .. self.1 .1).contains(&x.1) &&
|
||||
(self.0 .2 .. self.1 .2).contains(&x.2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct System {
|
||||
pub coords: Point,
|
||||
pub name: String,
|
||||
pub name_off: usize,
|
||||
pub star_flags: u8,
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub fn write_name(&self, writer: &mut dyn Write) -> io::Result<usize> {
|
||||
let name_len = self.name.len().min(255);
|
||||
writer.write_u8(name_len as u8)?;
|
||||
writer.write(&self.name.as_bytes()[..name_len])?;
|
||||
|
||||
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)?;
|
||||
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 rest = r.read_u32::<BE>()?;
|
||||
let name_off = (rest >> 4) as usize;
|
||||
let star_flags = (rest & 0xF) as u8;
|
||||
Ok(System{
|
||||
coords: Point(x,y,z),
|
||||
name: String::new(),
|
||||
name_off,
|
||||
star_flags,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_name<R: Read + Seek>(&mut self, r: &mut R) -> io::Result<()> {
|
||||
//eprint!(".");
|
||||
r.seek(io::SeekFrom::Start(self.name_off as u64))?;
|
||||
let mut buf = vec![];
|
||||
let len = r.read_u8()? as usize;
|
||||
//eprintln!("Reading {len} bytes from {}", self.name_off);
|
||||
buf.resize(len, 0);
|
||||
r.read_exact(buf.as_mut_slice())?;
|
||||
self.name = String::from_utf8(buf)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn jump_scale(&self) -> f32 {
|
||||
if self.star_flags & IS_NEUT != 0 {
|
||||
4.0
|
||||
} else if self.star_flags & IS_WDRF != 0 {
|
||||
1.5
|
||||
} else {
|
||||
0.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl rstar::Point for Point {
|
||||
type Scalar = i32;
|
||||
const DIMENSIONS: usize = 3;
|
||||
fn generate(mut gen: impl FnMut(usize) -> Self::Scalar) -> Self {
|
||||
let x = gen(0);
|
||||
let y = gen(1);
|
||||
let z = gen(2);
|
||||
Self(x,y,z)
|
||||
}
|
||||
|
||||
fn nth(&self, index: usize) -> Self::Scalar {
|
||||
match index {
|
||||
0 => self.0,
|
||||
1 => self.1,
|
||||
2 => self.2,
|
||||
_ => 0 as Self::Scalar,
|
||||
}
|
||||
}
|
||||
|
||||
fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar {
|
||||
match index {
|
||||
0 => &mut self.0,
|
||||
1 => &mut self.1,
|
||||
_ => &mut self.2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
(dx*dx) + (dy*dy) + (dz*dz)
|
||||
}
|
||||
|
||||
pub fn distance(&self, other: Self) -> f32 {
|
||||
self.distance_sq(other).sqrt()
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod tapetool;
|
||||
pub mod ioutil;
|
||||
pub mod bindata;
|
||||
|
||||
Reference in New Issue
Block a user