Much progress

This commit is contained in:
2023-06-22 00:26:43 +02:00
parent dc94ea45fc
commit 0d3a637982
6 changed files with 362 additions and 45 deletions

1
Cargo.lock generated
View File

@@ -107,6 +107,7 @@ name = "edalyze"
version = "0.1.0"
dependencies = [
"anyhow",
"byteorder",
"rstar",
"simd-json",
"structopt",

View File

@@ -10,4 +10,5 @@ structopt = "0.3"
simd-json = "0.10"
rstar = "0.11"
zstd = "0.11"
anyhow = "1"
anyhow = "1"
byteorder = "1"

View File

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

View File

@@ -1,2 +1,3 @@
pub mod tapetool;
pub mod ioutil;
pub mod bindata;