Initial commit
This commit is contained in:
		
						commit
						6365c58bd4
					
				
					 7 changed files with 667 additions and 0 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | /target | ||||||
|  | **/*.rs.bk | ||||||
|  | .vscode/** | ||||||
							
								
								
									
										183
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,183 @@ | ||||||
|  | # This file is automatically @generated by Cargo. | ||||||
|  | # It is not intended for manual editing. | ||||||
|  | [[package]] | ||||||
|  | name = "autocfg" | ||||||
|  | version = "0.1.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "csv" | ||||||
|  | version = "1.0.7" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "csv-core" | ||||||
|  | version = "0.1.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "ed_ldr" | ||||||
|  | version = "0.1.0" | ||||||
|  | dependencies = [ | ||||||
|  |  "csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "rstar 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "either" | ||||||
|  | version = "1.5.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "fnv" | ||||||
|  | version = "1.0.6" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "humantime" | ||||||
|  | version = "1.2.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "itertools" | ||||||
|  | version = "0.8.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "itoa" | ||||||
|  | version = "0.4.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "libc" | ||||||
|  | version = "0.2.55" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "memchr" | ||||||
|  | version = "2.2.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "num-traits" | ||||||
|  | version = "0.2.8" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "pdqselect" | ||||||
|  | version = "0.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "proc-macro2" | ||||||
|  | version = "0.4.30" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "quick-error" | ||||||
|  | version = "1.2.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "quote" | ||||||
|  | version = "0.6.12" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rstar" | ||||||
|  | version = "0.4.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "ryu" | ||||||
|  | version = "0.2.8" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "serde" | ||||||
|  | version = "1.0.92" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "serde_derive" | ||||||
|  | version = "1.0.92" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "syn" | ||||||
|  | version = "0.15.34" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "unicode-xid" | ||||||
|  | version = "0.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | 
 | ||||||
|  | [metadata] | ||||||
|  | "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" | ||||||
|  | "checksum csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9044e25afb0924b5a5fc5511689b0918629e85d68ea591e5e87fbf1e85ea1b3b" | ||||||
|  | "checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65" | ||||||
|  | "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" | ||||||
|  | "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" | ||||||
|  | "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" | ||||||
|  | "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" | ||||||
|  | "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" | ||||||
|  | "checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880" | ||||||
|  | "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" | ||||||
|  | "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" | ||||||
|  | "checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" | ||||||
|  | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" | ||||||
|  | "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" | ||||||
|  | "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" | ||||||
|  | "checksum rstar 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd08ae4f9661517777346592956ea6cdbba2895a28037af7daa600382f4b4001" | ||||||
|  | "checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" | ||||||
|  | "checksum serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "32746bf0f26eab52f06af0d0aa1984f641341d06d8d673c693871da2d188c9be" | ||||||
|  | "checksum serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "46a3223d0c9ba936b61c0d2e3e559e3217dbfb8d65d06d26e8b3c25de38bae3e" | ||||||
|  | "checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" | ||||||
|  | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" | ||||||
							
								
								
									
										16
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | [package] | ||||||
|  | name = "ed_ldr" | ||||||
|  | version = "0.1.0" | ||||||
|  | authors = ["Daniel Seiller <earthnuker@gmail.com>"] | ||||||
|  | edition = "2018" | ||||||
|  | 
 | ||||||
|  | # [profile.release] | ||||||
|  | # debug = true | ||||||
|  | 
 | ||||||
|  | [dependencies] | ||||||
|  | csv = "1.0.7" | ||||||
|  | serde = "1.0.92" | ||||||
|  | serde_derive = "1.0.92" | ||||||
|  | rstar = "0.4.0" | ||||||
|  | humantime = "1.2.0" | ||||||
|  | fnv = "1.0.6" | ||||||
							
								
								
									
										18
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | # Elite: Dangerous Long Range Router (Rust Version) | ||||||
|  | 
 | ||||||
|  | ## Usage: | ||||||
|  | 
 | ||||||
|  | #. run `download.sh` and `process.py` (you can hit Ctrl+C when it says "Building KD-Tree..") | ||||||
|  | #. edit source, destination and range in `src/main.rs` | ||||||
|  | #. (optional) `set RUSTFLAGS=-C target-cpu=native` (Windows) or `export RUSTFLAGS=-C target-cpu=native` (Linux) | ||||||
|  | #. `cargo run --release` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Dependencies | ||||||
|  | 
 | ||||||
|  | - Python 3.7 | ||||||
|  |   - Pandas | ||||||
|  |   - uJSON | ||||||
|  | - Working nightly Rust Compiler (tested with `rustc 1.37.0-nightly (5d8f59f4b 2019-06-04)`) | ||||||
|  | - ~8GB of free RAM | ||||||
							
								
								
									
										3
									
								
								download.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								download.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | #!/usr/bin/bash | ||||||
|  | rm systemsWithCoordinates.json bodies.json *.aria2 | ||||||
|  | wget https://www.edsm.net/dump/systemsWithCoordinates.json https://www.edsm.net/dump/bodies.json | ||||||
							
								
								
									
										172
									
								
								process.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								process.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,172 @@ | ||||||
|  | import ujson as json | ||||||
|  | from tqdm import tqdm | ||||||
|  | from pprint import pprint | ||||||
|  | import itertools as ITT | ||||||
|  | import os | ||||||
|  | import sys | ||||||
|  | import csv | ||||||
|  | import sqlite3 | ||||||
|  | import pandas as pd | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def is_scoopable(entry): | ||||||
|  |     first = entry.type.split()[0] | ||||||
|  |     return first == "Neutron" or first == "White" or first in "KGBFOAM" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_mult(name): | ||||||
|  |     try: | ||||||
|  |         first = name.split()[0] | ||||||
|  |     except: | ||||||
|  |         return 1 | ||||||
|  |     if first == "Neutron": | ||||||
|  |         return 4 | ||||||
|  |     if first == "White": | ||||||
|  |         return 1.5 | ||||||
|  |     return 1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def dict_factory(cursor, row): | ||||||
|  |     d = {} | ||||||
|  |     for idx, col in enumerate(cursor.description): | ||||||
|  |         d[col[0]] = row[idx] | ||||||
|  |     return d | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def blocks(files, size=65536): | ||||||
|  |     while True: | ||||||
|  |         b = files.read(size) | ||||||
|  |         if not b: | ||||||
|  |             break | ||||||
|  |         yield b | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def getlines(f, fn, show_progbar=False): | ||||||
|  |     f.seek(0, 2) | ||||||
|  |     size = f.tell() | ||||||
|  |     f.seek(0) | ||||||
|  |     progbar = tqdm( | ||||||
|  |         desc="Processing " + fn, | ||||||
|  |         total=size, | ||||||
|  |         unit="b", | ||||||
|  |         unit_scale=True, | ||||||
|  |         unit_divisor=1024, | ||||||
|  |         ascii=True, | ||||||
|  |         leave=True, | ||||||
|  |         disable=(not show_progbar), | ||||||
|  |     ) | ||||||
|  |     buffer = [] | ||||||
|  |     for block in blocks(f): | ||||||
|  |         progbar.n = f.tell() | ||||||
|  |         progbar.update(0) | ||||||
|  |         if buffer: | ||||||
|  |             buffer += (buffer.pop(0) + block).splitlines(keepends=True) | ||||||
|  |         else: | ||||||
|  |             buffer += block.splitlines(keepends=True) | ||||||
|  |         while buffer and buffer[0].endswith("\n"): | ||||||
|  |             try: | ||||||
|  |                 yield json.loads(buffer.pop(0).strip().rstrip(",")) | ||||||
|  |             except ValueError: | ||||||
|  |                 pass | ||||||
|  |     while buffer: | ||||||
|  |         try: | ||||||
|  |             yield json.loads(buffer.pop(0).strip().rstrip(",")) | ||||||
|  |         except ValueError: | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def process_file(fn, show_progbar=False): | ||||||
|  |     with open(fn, "r") as f: | ||||||
|  |         for line in tqdm( | ||||||
|  |             getlines(f, fn, show_progbar), | ||||||
|  |             desc=fn, | ||||||
|  |             unit=" lines", | ||||||
|  |             unit_scale=True, | ||||||
|  |             ascii=True, | ||||||
|  |             leave=True, | ||||||
|  |             disable=(not show_progbar), | ||||||
|  |         ): | ||||||
|  |             yield line | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if not os.path.isfile("stars.jl"): | ||||||
|  |     print("Filtering for Neutron Stars") | ||||||
|  |     with open("stars.jl", "w") as neut: | ||||||
|  |         for body in process_file("bodies.json", True): | ||||||
|  |             T = body.get("type") or "" | ||||||
|  |             if "Star" in T: | ||||||
|  |                 neut.write(json.dumps(body) + "\n") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def load_systems(load=False): | ||||||
|  |     load = not os.path.isfile("systems.db") | ||||||
|  |     cache = sqlite3.connect("systems.db") | ||||||
|  |     cache.row_factory = dict_factory | ||||||
|  |     c = cache.cursor() | ||||||
|  |     if load: | ||||||
|  |         print("Caching Systems") | ||||||
|  |         c.execute("DROP TABLE IF EXISTS systems") | ||||||
|  |         c.execute( | ||||||
|  |             "CREATE TABLE systems (id64 int primary key, name text, x real, y real, z real)" | ||||||
|  |         ) | ||||||
|  |         cache.commit() | ||||||
|  |         recs = [] | ||||||
|  |         for system in process_file("systemsWithCoordinates.json", True): | ||||||
|  |             rec = [ | ||||||
|  |                 system["id64"], | ||||||
|  |                 system["name"], | ||||||
|  |                 system["coords"]["x"], | ||||||
|  |                 system["coords"]["y"], | ||||||
|  |                 system["coords"]["z"], | ||||||
|  |             ] | ||||||
|  |             recs.append(rec) | ||||||
|  |             if len(recs) % 1024 * 1024 == 0: | ||||||
|  |                 c.executemany("INSERT INTO systems VALUES (?,?,?,?,?)", recs) | ||||||
|  |                 recs.clear() | ||||||
|  |         c.executemany("INSERT INTO systems VALUES (?,?,?,?,?)", recs) | ||||||
|  |         cache.commit() | ||||||
|  |     return cache, c | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if not os.path.isfile("stars.csv"): | ||||||
|  |     cache, cur = load_systems() | ||||||
|  |     rows = [] | ||||||
|  |     with open("stars.csv", "w", newline="") as sys_csv: | ||||||
|  |         csv_writer = csv.writer(sys_csv, dialect="excel") | ||||||
|  |         for neut in process_file("stars.jl", True): | ||||||
|  |             cur.execute( | ||||||
|  |                 "SELECT * FROM systems WHERE id64==?", (neut.get("systemId64"),) | ||||||
|  |             ) | ||||||
|  |             system = cur.fetchone() | ||||||
|  |             if not system: | ||||||
|  |                 continue | ||||||
|  |             row = [ | ||||||
|  |                 neut["systemId64"], | ||||||
|  |                 neut["subType"], | ||||||
|  |                 neut["name"], | ||||||
|  |                 get_mult(neut["subType"]), | ||||||
|  |                 system["x"], | ||||||
|  |                 system["y"], | ||||||
|  |                 system["z"], | ||||||
|  |             ] | ||||||
|  |             rows.append(row) | ||||||
|  |             if len(rows) > 1024: | ||||||
|  |                 csv_writer.writerows(rows) | ||||||
|  |                 rows.clear() | ||||||
|  |         csv_writer.writerows(rows) | ||||||
|  |         print() | ||||||
|  |     cache.close() | ||||||
|  | 
 | ||||||
|  | if not os.path.isfile("stars.kdt"): | ||||||
|  |     tqdm.pandas(ascii=True, leave=True) | ||||||
|  |     print("Loading data...") | ||||||
|  |     data = pd.read_csv( | ||||||
|  |         "stars.csv", | ||||||
|  |         encoding="utf-8", | ||||||
|  |         names=["id", "type", "name", "mult", "x", "y", "z"], | ||||||
|  |     ) | ||||||
|  |     print("Cleaning data...") | ||||||
|  |     data.type.fillna("Unknown", inplace=True) | ||||||
|  |     data.drop_duplicates("id", inplace=True) | ||||||
|  |     print("Writing CSV...") | ||||||
|  |     data.to_csv("stars.csv", header=False, index=False) | ||||||
							
								
								
									
										272
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,272 @@ | ||||||
|  | extern crate csv; | ||||||
|  | extern crate serde; | ||||||
|  | #[macro_use] | ||||||
|  | extern crate serde_derive; | ||||||
|  | 
 | ||||||
|  | extern crate fnv; | ||||||
|  | extern crate humantime; | ||||||
|  | use fnv::{FnvHashMap, FnvHashSet}; | ||||||
|  | use humantime::format_duration; | ||||||
|  | use rstar::{PointDistance, RTree, RTreeObject, AABB}; | ||||||
|  | use std::collections::VecDeque; | ||||||
|  | use std::hash::{Hash, Hasher}; | ||||||
|  | use std::io::Write; | ||||||
|  | use std::time::Instant; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Deserialize)] | ||||||
|  | struct Record { | ||||||
|  |     id: i64, | ||||||
|  |     star_type: String, | ||||||
|  |     name: String, | ||||||
|  |     mult: f32, | ||||||
|  |     x: f32, | ||||||
|  |     y: f32, | ||||||
|  |     z: f32, | ||||||
|  | } | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct System { | ||||||
|  |     id: i64, | ||||||
|  |     star_type: String, | ||||||
|  |     name: String, | ||||||
|  |     mult: f32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct Point { | ||||||
|  |     id: i64, | ||||||
|  |     x: f32, | ||||||
|  |     y: f32, | ||||||
|  |     z: f32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Point { | ||||||
|  |     pub fn dist2(&self, p: &[f32; 3]) -> f32 { | ||||||
|  |         let dx = self.x - p[0]; | ||||||
|  |         let dy = self.y - p[1]; | ||||||
|  |         let dz = self.z - p[2]; | ||||||
|  | 
 | ||||||
|  |         return dx * dx + dy * dy + dz * dz; | ||||||
|  |     } | ||||||
|  |     pub fn distp(&self, p: &Point) -> f32 { | ||||||
|  |         return self.distp2(p).sqrt(); | ||||||
|  |     } | ||||||
|  |     pub fn distp2(&self, p: &Point) -> f32 { | ||||||
|  |         return self.dist2(&[p.x, p.y, p.z]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl PartialEq for Point { | ||||||
|  |     fn eq(&self, other: &Self) -> bool { | ||||||
|  |         self.id == other.id | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Eq for Point {} | ||||||
|  | 
 | ||||||
|  | impl Hash for Point { | ||||||
|  |     fn hash<H: Hasher>(&self, state: &mut H) { | ||||||
|  |         self.id.hash(state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl RTreeObject for Point { | ||||||
|  |     type Envelope = AABB<[f32; 3]>; | ||||||
|  | 
 | ||||||
|  |     fn envelope(&self) -> Self::Envelope { | ||||||
|  |         return AABB::from_point([self.x, self.y, self.z]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PointDistance for Point { | ||||||
|  |     fn distance_2(&self, point: &[f32; 3]) -> f32 { | ||||||
|  |         return self.dist2(&point); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct Router { | ||||||
|  |     tree: RTree<Point>, | ||||||
|  |     systems: FnvHashMap<i64, System>, | ||||||
|  |     range: f32, | ||||||
|  |     scoopable: FnvHashSet<i64>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Router { | ||||||
|  |     pub fn new(path: &str, range: f32) -> Self { | ||||||
|  |         let mut scoopable = FnvHashSet::default(); | ||||||
|  |         let mut systems: FnvHashMap<i64, System> = FnvHashMap::default(); | ||||||
|  |         let mut reader = csv::ReaderBuilder::new() | ||||||
|  |             .has_headers(false) | ||||||
|  |             .from_path(path) | ||||||
|  |             .unwrap(); | ||||||
|  |         println!("Loading {}...", path); | ||||||
|  |         let points = reader | ||||||
|  |             .deserialize() | ||||||
|  |             .map(|res: Result<Record, _>| { | ||||||
|  |                 let sys = res.unwrap(); | ||||||
|  |                 systems.insert( | ||||||
|  |                     sys.id, | ||||||
|  |                     System { | ||||||
|  |                         id: sys.id, | ||||||
|  |                         star_type: sys.star_type.clone(), | ||||||
|  |                         name: sys.name, | ||||||
|  |                         mult: sys.mult, | ||||||
|  |                     }, | ||||||
|  |                 ); | ||||||
|  |                 if sys.mult > 1.0f32 { | ||||||
|  |                     scoopable.insert(sys.id); | ||||||
|  |                 } else { | ||||||
|  |                     for c in "KGBFOAM".chars() { | ||||||
|  |                         if sys.star_type.starts_with(c) { | ||||||
|  |                             scoopable.insert(sys.id); | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return Point { | ||||||
|  |                     x: sys.x, | ||||||
|  |                     y: sys.y, | ||||||
|  |                     z: sys.z, | ||||||
|  |                     id: sys.id, | ||||||
|  |                 }; | ||||||
|  |             }) | ||||||
|  |             .collect(); | ||||||
|  |         return Self { | ||||||
|  |             tree: RTree::<Point>::bulk_load(points), | ||||||
|  |             systems, | ||||||
|  |             range, | ||||||
|  |             scoopable, | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn preload_points(&self) -> FnvHashMap<i64, &Point> { | ||||||
|  |         let mut ret = FnvHashMap::default(); | ||||||
|  |         for point in &self.tree { | ||||||
|  |             ret.insert(point.id, point); | ||||||
|  |         } | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn closest(&self, point: &[f32; 3]) -> &Point { | ||||||
|  |         return self.tree.nearest_neighbor(point).unwrap(); | ||||||
|  |     } | ||||||
|  |     fn points_in_sphere(&self, center: &[f32; 3], radius: f32) -> impl Iterator<Item = &Point> { | ||||||
|  |         let center: [f32; 3] = *center; | ||||||
|  |         return self | ||||||
|  |             .tree | ||||||
|  |             .locate_in_envelope(&AABB::from_corners( | ||||||
|  |                 [ | ||||||
|  |                     center[0] - radius * 1f32, | ||||||
|  |                     center[1] - radius * 1f32, | ||||||
|  |                     center[2] - radius * 1f32, | ||||||
|  |                 ], | ||||||
|  |                 [ | ||||||
|  |                     center[0] + radius * 1f32, | ||||||
|  |                     center[1] + radius * 1f32, | ||||||
|  |                     center[2] + radius * 1f32, | ||||||
|  |                 ], | ||||||
|  |             )) | ||||||
|  |             .filter(move |p| (p.dist2(¢er) < (radius * radius))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn mult(&self, id: i64) -> f32 { | ||||||
|  |         if let Some(sys) = self.systems.get(&id) { | ||||||
|  |             return sys.mult; | ||||||
|  |         }; | ||||||
|  |         return 1.0; | ||||||
|  |     } | ||||||
|  |     fn neighbours(&self, sys: &Point) -> impl Iterator<Item = &Point> { | ||||||
|  |         return self.points_in_sphere(&[sys.x, sys.y, sys.z], self.mult(sys.id) * self.range); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn valid(&self, sys: &Point) -> bool { | ||||||
|  |         return self.scoopable.contains(&sys.id); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn route(&mut self, src: &[f32; 3], dst: &[f32; 3]) -> Vec<(&System, &Point)> { | ||||||
|  |         let start_sys = self.closest(src); | ||||||
|  |         let goal_sys = self.closest(dst); | ||||||
|  |         let total = self.tree.size() as f32; | ||||||
|  |         let mut prev = FnvHashMap::default(); | ||||||
|  |         let mut seen = FnvHashSet::default(); | ||||||
|  |         let t_start = Instant::now(); | ||||||
|  |         let mut depth = 0; | ||||||
|  |         let mut queue: VecDeque<(usize, &Point)> = VecDeque::new(); | ||||||
|  |         let mut r_queue: VecDeque<(usize, &Point)> = VecDeque::new(); | ||||||
|  |         queue.push_front((0, &start_sys)); | ||||||
|  |         r_queue.push_front((0, &goal_sys)); | ||||||
|  |         seen.insert(start_sys.id); | ||||||
|  |         while let Some((d, sys)) = queue.pop_front() { | ||||||
|  |             if d != depth { | ||||||
|  |                 depth = d; | ||||||
|  |                 print!( | ||||||
|  |                     "\r[{}] Depth: {}, Queue: {}, Seen: {} ({:.2}%)    ", | ||||||
|  |                     format_duration(t_start.elapsed()), | ||||||
|  |                     d, | ||||||
|  |                     queue.len(), | ||||||
|  |                     prev.len(), | ||||||
|  |                     ((prev.len() as f32) / total) * 100.0 | ||||||
|  |                 ); | ||||||
|  |                 std::io::stdout().flush().unwrap(); | ||||||
|  |             } | ||||||
|  |             if sys.id == goal_sys.id { | ||||||
|  |                 println!(); | ||||||
|  |                 let points = self.preload_points(); | ||||||
|  |                 let mut v: Vec<(&System, &Point)> = Vec::new(); | ||||||
|  |                 let mut prev_sys_id = sys.id; | ||||||
|  |                 loop { | ||||||
|  |                     if let Some(sys) = self.systems.get(&prev_sys_id) { | ||||||
|  |                         v.push((sys, points[&sys.id])); | ||||||
|  |                     } else { | ||||||
|  |                         break; | ||||||
|  |                     }; | ||||||
|  |                     match prev.get(&prev_sys_id) { | ||||||
|  |                         Some(sys_id) => prev_sys_id = *sys_id, | ||||||
|  |                         None => { | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 v.reverse(); | ||||||
|  |                 return v; | ||||||
|  |             } | ||||||
|  |             let nbs = self | ||||||
|  |                 .neighbours(&sys) | ||||||
|  |                 .filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id))); | ||||||
|  |             for nb in nbs { | ||||||
|  |                 if seen.insert(nb.id) { | ||||||
|  |                     prev.insert(nb.id, sys.id); | ||||||
|  |                     queue.push_back((d + 1, nb)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         println!("No route found!"); | ||||||
|  |         return Vec::new(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn main() { | ||||||
|  |     let path = r#"D:\devel\files\python\EDSM\stars.csv"#; | ||||||
|  |     let t_load = Instant::now(); | ||||||
|  |     let mut router: Router = Router::new(path, 48.0); | ||||||
|  |     println!("Done in {}!", format_duration(t_load.elapsed())); | ||||||
|  |     let t_route = Instant::now(); | ||||||
|  |     let route = router.route( | ||||||
|  |         &[-65.21875, 7.75, -111.03125], // Ix
 | ||||||
|  |         &[-1111.5625, -134.21875, 65269.75], // Beagle Point
 | ||||||
|  |                                         // &[-7095.375, 401.25, 2396.8125], // V1357 Cygni,
 | ||||||
|  |     ); // Ix -> BP 537
 | ||||||
|  |     println!( | ||||||
|  |         "Done in {} ({} Jumps)!\n", | ||||||
|  |         format_duration(t_route.elapsed()), | ||||||
|  |         route.len() | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     let mut total: f32 = 0.0; | ||||||
|  |     for ((sys1, p1), (_, p2)) in route.iter().zip(route.iter().skip(1)) { | ||||||
|  |         let dist = p1.distp(p2); | ||||||
|  |         total += dist; | ||||||
|  |         println!("{} [{}]: {:.2} Ly", sys1.name, sys1.star_type, dist); | ||||||
|  |     } | ||||||
|  |     let sys = route.iter().last().unwrap().0; | ||||||
|  |     println!("{} [{}]: {:.2} Ly\n", sys.name, sys.star_type, 0.0); | ||||||
|  |     println!("Total: {:.2} Ly", total); | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue