2022-02-23

This commit is contained in:
Daniel S. 2022-02-23 22:45:59 +01:00
parent 35a0c40d14
commit dc68cce9ed
80 changed files with 859345 additions and 4387 deletions

1
docs_mdbook/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
book

11
docs_mdbook/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"spellright.language": [
"de",
"en"
],
"spellright.documentTypes": [
"latex",
"plaintext",
"git-commit"
]
}

View file

@ -0,0 +1,6 @@
{
"todo": [],
"in-progress": [],
"testing": [],
"done": []
}

22
docs_mdbook/book.toml Normal file
View file

@ -0,0 +1,22 @@
[book]
authors = ["Daniel Seiller"]
language = "en"
multilingual = false
src = "src"
title = "Elite: Dangerous Long Range Router"
[output.html]
preferred-dark-theme = "ayu"
mathjax-support = true
[preprocessor.svgbob]
text_width = 8.0
text_height = 16.0
class = "bob"
font_family = "arial"
font_size = 14.0
stroke_width = 2.0
# there's using css-variables from theme:
stroke_color = "var(--fg)" # see default theme / variables.css
background_color = "transparent" # also useful `var(--bg)`
# all properties are optional.

View file

@ -0,0 +1,12 @@
# Summary
- [Intro](./intro/_index.md)
- [Galactic Travel in Elite: Dangerous](./intro/ed_travel.md)
- [FSD Fuel consumption and jump range](./intro/fsd_fuel.md)
- [Graph Search algorithms](./intro/graph_algos.md)
- [Elite Dangerous Long Range Router](./ed_lrr/_index.md)
- [Graph representation](./ed_lrr/graph.md)
- [Search modes](./ed_lrr/modes.md)
- [Graph precomputation](./ed_lrr/precomp.md)
- [Python API](./ed_lrr/py_api.md)

View file

@ -0,0 +1 @@
# Elite Dangerous Long Range Router

View file

@ -0,0 +1,4 @@
# Graph representation
ED_LRR uses an implicit graph built on top of an R*-Tree for its route computation.
Every node (star system) has edges towards all systems within jump range, edge weights (the distance between star systems) can be computed on the fly when necessary

View file

@ -0,0 +1,31 @@
# Search modes
## Heuristic
A*, Greedy and Beam search all use the following heuristic to select candidates to expand on the next layer of the graph
$$mult(n) =
\begin{cases}
4 &\text{if $n$ is a neutron star} \\\\
1.5 &\text{if $n$ is a white dwarf star} \\\\
1 &\text{otherwise}
\end{cases}
$$
$$d(a,b) = \sqrt{(a_x-b_x)^2+(a_y-b_y)^2+(a_z-b_z)^2}$$
$$
h(\text{node},\text{goal})=
\max(
d(\text{node},\text{goal})-
(
\text{range}*mult(\text{node})
),0)
$$
potential new heuristic:
$$
h(\text{node},\text{next},\text{goal})=
1 - {\cos^{-1}(|(\text{next}-\text{node})| \cdot |(\text{goal}-\text{node})|)\over\pi}
$$

View file

@ -0,0 +1,2 @@
# Graph precomputation

View file

@ -0,0 +1,58 @@
# Python API
First the module needs to be imported
```python
from _ed_lrr import PyRouter, PyShip
```
Then we need to instantiate a route plotter
```python
# callback is passed a dict describing the current search state and progress
def callback(state):
print(state)
r=PyRouter(callback)
```
Optionally ship loadouts can be loaded from the Elite: Dangerous journal files
```python
ships = PyShip.from_journal()
```
To plot a route we need to load a list of star systems with coordinates
```python
r.load("./stars.csv")
```
After a list has been loaded we can resolve star systems to their IDs
```python
systems = [
# resolve by coordinates, needs to build an R*-Tree so uses a few GB of RAM
(0,0,0),
# resolve by name, does fuzzy search, has to scan the whole list of system names
"Colonia",
# resolve by ID, fairly fast, but the IDs are ed_lrr specific
3553323
]
systems = r.resolve_systems(*query) # this will return a dict mapping the input key to a dict
assert sorted(systems.keys())==sorted(query)
sys_ids = {k: v["id"] for k, v in systems.items()}
```
Once the system IDs are known we can compute a route
```python
route = r.route(
[sys_ids["Sol"], sys_ids["Colonia"]] # route hops, can be any number of systems (at least 2)
48.0, # jump range
beam_width=1<<12, # beam width to limit exploration (4096)
greedyness=0, # greedyness for A* (0=BFS,0.5 = A*, 1=Greedy)
max_dist=500, # maximum deviation from straight line (not yet implemented)
num_workers=0 # number of workers to distribute the search accross (0 -> serial mode, >=1 -> spawn worker pool)
)
```

17
docs_mdbook/src/fsd.asy Normal file
View file

@ -0,0 +1,17 @@
import graph;
size(500,0);
real fsd(real m_fuel) {
// 4A drive
real boost = 1.0;
real f_max = 3.0;
real m_opt = 525.0;
real m_ship = 347.0;
real l = 12.0;
real p = 2.30;
return ((boost*m_opt*(1000.0*min(f_max,m_fuel)/l)^(1/p)))/(m_ship+m_fuel);
}
draw(graph(fsd,-100,100,n=500,Hermite),red);
xaxis("$m_{fuel}$");
yaxis("$range (Ly)$",0);

View file

@ -0,0 +1 @@
# Intro

View file

@ -0,0 +1,19 @@
# Galactic Travel in Elite: Dangerous
All ships in Elite: Dangerous (E:D) are equipped with a Frame Shift Drive (FSD) which allows them to jumpst vast distances (multiple light years) from one star system to another.
The maximum range you can traverse in a single jump is limited by the maximum fuel consuption per jump of the specific drive (depends on class and rating) and influenced by the following factors:
- Rating of the FSD
- Class of the FSD
- Mass of your ship (Base mass+Cargo mass+Fuel mass)
- Amount of fuel available in the tank
For details see [the chapter detailing FSD fuel consumption](./fsd_fuel.html)
If the ship is equipped with a Fuel Scoop it can:
- Scoop hydrogen from the corona of a star to refill its fuel tank (only applies to stars of class K, G, B, F, O, A or M)
- Supercharge its FSD to increase the maximum jump range by a factor of:
- 4 if supercharging in the jets of a neutron star
- 1.5 if supercharging in the jets of a white dwarf star

View file

@ -0,0 +1,36 @@
# Notes on FSD Fuel consumption and jump range
FSD Fuel consumption ([Elite: Dangerous Wiki](https://elite-dangerous.fandom.com/wiki/Frame_Shift_Drive#Hyperspace_Fuel_Equation)):
$$Fuel = 0.001 \cdot l \cdot \left(\frac{dist \cdot \left(m_{fuel} + m_{ship}\right)}{boost \cdot m_{opt}}\right)^{p}$$
Solving for \\(dist\\) gives the jump range (in Ly) for a given amount of fuel (in tons) as:
$$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
Assuming \\(f_{max}\\) tons of available fuel gives us the maximum jump range for a single jump as:
$$dist_{max} = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot f_{max}}{l}\right)^{\frac{1}{p}}}{f_{max} + m_{ship}}$$
Since the guardian FSD booster increases the maximum jump range by \\(B_g\\) light years we can calculate a correction factor for the fuel consumption as:
$$ e_{fuel} = 0.001 \cdot l \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{p}$$
Incorporating \\(e_{fuel}\\) into the distance equation yields
$$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot e_{fuel} \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
Expanding \\(e_{fuel}\\) yields
$$dist = \frac{boost \cdot m_{opt} \cdot \left(1.0 \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{p} \cdot \min\left(f_{max}, m_{fuel}\right)\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
Finally, Expanding \\(dist_{max}\\) yields the full equation as
$$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000000.0 \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{- p} \cdot \min\left(f_{max}, m_{fuel}\right)}{l^{2}}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
Where:
- \\(Fuel\\) is the fuel needed to jump (in tons)
- \\(l\\) is the linear constant of your FSD (depends on the rating)
- \\(p\\) is the power constant of your FSD (depends on the class)
- \\(m_{ship}\\) is the mass of your ship (including cargo)
- \\(m_{fuel}\\) is the amount of fuel in tons currently stored in your tanks
- \\(m_{opt}\\) is the optimized mass of your FSD (in tons)
- \\(f_{max}\\) is the maximum amount of fuel your FSD can use per jump
- \\(boost\\) is the "boost factor" of your FSD (1.0 when jumping normally, 1.5 when supercharged by a white dwarf, 4.0 for a neutron star, etc)
- \\(dist\\) is the distance you can jump with a given fuel amount
- \\(dist_{max}\\) is the maximum distance you can jump (when \\(m_{fuel}=f_{max}\\))
- \\(B_{g}\\) is the amount of Ly added by your Guardian FSD Booster
- \\(e_{fuel}\\) is the efficiency increase added by the Guardian FSD Booster

View file

@ -0,0 +1,25 @@
# Graph Search algorithms
## Breadth-first search (BFS)
BFS expand node in breadth first order while keeping track of the parent node of each expanded node
## Beam search
Beam search is similar to BFS but limits the number of expanded nodes based on a heuristic
## Greedy search
Greedy search is essentially Beam search with a beam width of 1
## Dijkstra
Dijkstra's algorithm finds the shortest path across a graph based on some edge weight
## A*
A* is similar to Dijkstra but uses a heuristic to speed up the search
## Beam-Stack search (BSS)
Beam-Stack search is a variation of beam search which keeps a separate priority queue for each layer of the graph to allow backtracking and expand previously unexpanded nodes

47
docs_mdbook/src/range.asy Normal file
View file

@ -0,0 +1,47 @@
import graph;
import stats;
size(500);
srand(10);
struct Star {
pair pos;
real mult;
}
real range = 48.0;
int n_stars=1000;
Star[] stars=new Star[n_stars];
for(int i=0; i < n_stars; ++i) {
Star s=new Star;
s.pos=(Gaussrand()*range*2,Gaussrand()*range*2);
s.mult=1.0;
if (unitrand()<0.2) {
s.mult=1.5;
} else {
if (unitrand()<0.1) {
s.mult=4.0;
}
}
stars[i]=s;
}
Star origin=new Star;
origin.pos=(0,0);
origin.mult=1.0;
draw(circle(origin.pos,range*origin.mult),white);
draw(circle(origin.pos,range),white+dashed);
draw(circle(origin.pos,range*2),white+dashed);
draw(circle(origin.pos,range*4),white+dashed);
fill(circle(origin.pos,2),red);
for (Star s: stars) {
if (length(s.pos-origin.pos)<(range*origin.mult)) {
draw(s.pos--origin.pos,white+dashed);
fill(circle(s.pos,s.mult),green);
} else {
fill(circle(s.pos,s.mult),white);
}
};