Route with A* over numeric cell keys, so a route can reach past the Sun's crowd
The route search widened evenly from the departure, Dijkstra-style, with a budget of 20 000 stars. On the Gaia catalogue those are all within about 40 pc of the Sun, so it found no route to anything farther at any range: Sol to Mirfak (155 pc) failed at 3, 8, 15 and 30 pc alike. Every failure then asked minimumRangeBetween what range would work. That search widened the same way with a 30 pc ceiling, and it ran for up to a minute on the main thread before giving up with nothing. routeBetween is now an A* search. Each star is queued by the distance travelled to it plus the straight line on to the destination, on a binary heap rather than a linear scan of the frontier. It heads for the destination instead of flooding the core around the departure. minimumRangeBetween bisects the range, one routeBetween per step, because whether a chain exists can only become truer as the range grows. Its answer is always the longest hop of a route actually found, so a range it names always opens one. Its ceiling is now the Routes panel's own maximum, MAX_JUMP_RANGE_PC: a range the control cannot be set to is no answer, and raiseTo already clamped any figure above it. The spatial index keys its cells by one number packed from their three indices instead of an "ix,iy,iz" string. A search visits up to 125 cells for every star it expands, and building those strings was half of what a route cost. forEachWithin hands neighbours over unsorted and uncollected, which was most of the other half; within is now that, gathered and sorted. The no-route line said nothing in the catalogue bridged the gap; it now says no chain of jumps up to the panel's maximum reaches the star, which is what was searched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi
This commit is contained in:
@@ -81,9 +81,40 @@ describe('routeBetween', () => {
|
||||
|
||||
expect(route?.longestHopPc).toBeCloseTo(4);
|
||||
});
|
||||
|
||||
it('heads for the destination rather than exhausting a dense knot around the departure', () => {
|
||||
// The Gaia catalogue in miniature: a crowd around the departure, larger than the search's
|
||||
// budget, with the only way on a thin chain leading out of it. A search widening evenly from
|
||||
// the departure spends the budget on the crowd and never reaches the chain's far end.
|
||||
const route = routeBetween(knotAndChain(), 0, CHAIN_END, 1.5);
|
||||
|
||||
expect(route).not.toBeNull();
|
||||
expect(route!.stars[route!.stars.length - 1]).toBe(CHAIN_END);
|
||||
expect(route!.longestHopPc).toBeLessThanOrEqual(1.5);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 21 000 stars scattered through the 30 pc cube around the origin, about ten times the density
|
||||
* around the real Sun and more than a search's budget, with a chain a parsec a hop running along
|
||||
* x from the origin out through the crowd and on to 75 pc.
|
||||
*/
|
||||
const CHAIN_END = 75;
|
||||
function knotAndChain(): StarNeighbourhood {
|
||||
let seed = 7;
|
||||
const random = () => ((seed = (seed * 1103515245 + 12345) % 2147483648) / 2147483648) * 30 - 15;
|
||||
const knot: StarPoint[] = Array.from({ length: 21000 }, (_, i) => ({ id: 1000 + i, x: random(), y: random(), z: random() }));
|
||||
const chainOut: StarPoint[] = Array.from({ length: CHAIN_END }, (_, i) => ({ id: i + 1, x: i + 1, y: 0, z: 0 }));
|
||||
return index([{ id: 0, x: 0, y: 0, z: 0 }, ...knot, ...chainOut]);
|
||||
}
|
||||
|
||||
describe('minimumRangeBetween', () => {
|
||||
it('works out the range past a dense knot around the departure', () => {
|
||||
// Past the crowd the chain's hops of a parsec are the only way on, so a parsec is the
|
||||
// answer, to the half-step the panel rounds up to.
|
||||
expect(minimumRangeBetween(knotAndChain(), 0, CHAIN_END, 8)).toBeCloseTo(1, 1);
|
||||
});
|
||||
|
||||
it('names the shortest range that opens a way through', () => {
|
||||
// Hops of 1 and 4: no range under 4 connects them, and 4 exactly does.
|
||||
const stepped = index([
|
||||
|
||||
Reference in New Issue
Block a user