Files
OpenFrontIO/docs/MultiSourceAnyTargetBFS.md
T
scamiv 69e422d35e Refactor MultiSourceAnyTargetBFS documentation for clarity and performance
- Updated the design notes to clarify the multi-source, any-target BFS approach for boat routing on a water-only grid.
- Simplified the explanation of the algorithm, focusing on the use of a virtual super-source and super-target.
- Enhanced the API description, detailing the return value structure and movement model.
- Added performance optimizations, including the use of typed arrays and precomputation of water component IDs to improve routing efficiency.
2025-12-27 14:22:59 +01:00

50 lines
2.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MultiSourceAnyTargetBFS (boats) — design notes
## What were doing
Boat routing is now a single, sound **multi-source / any-target** shortest-path solve on a **water-only** grid.
For equal cost edges, thats just **BFS**.
The mental model is the classic “virtual super-source / super-target” trick:
- Pretend there is a `START` node with 0-cost edges to every source `S`.
- Pretend every target `D` has a 0-cost edge to an `END` node.
- Run shortest path `START → END`.
We dont build those nodes: we seed the queue with all sources, and we stop when we **dequeue** any target.
## API shape
Return value: `{ source, target, path } | null`
- `source`: which seed-origin won (useful when seeds are water-adjacent tiles but the origin is a shore tile).
- `target`: the water tile we reached (usually adjacent to the chosen landing shore).
- `path`: water path as a list of `TileRef` to cache and replay (no per-tick pathfinding).
## Movement model (boats)
- Traversal: `gm.isWater(tile)` only.
- Neighbors: **king moves** (8-neighbor / Chebyshev) by default.
- No-corner-cutting: diagonal is only allowed if both touching orthogonals are water.
- All moves cost 1, so BFS is optimal.
## Performance wins (the non-negotiables)
- Use typed arrays + stamps (no `Set`/`Map` in the inner loop).
- **Precompute water-body component IDs** once per map instance (`WaterComponents.ts`) and filter sources/targets:
- If source component ≠ target component, skip it (ocean vs lake becomes O(1) reject).
- This makes “impossible” routes cheap and lets us delete hacky visited-count early exits.
## How this integrates (transport/trade/warships)
- Destination selection stays “near click”: pick a bounded set of landing shore candidates, then convert to adjacent water targets.
- Source selection stays bounded (sampling/extrema/etc), then convert to adjacent water seeds.
- Compute the route once on launch/init, cache `path`, and only advance an index during ticks.
- Retreat follows the existing cached path backwards (no recompute).
## Current known waste
If the client pre-queries and the server recomputes, you will see two searches for a single action.
We should ensure we only do one solve per user action (exact mechanism TBD: remove the pre-query, or reuse/publish the computed route).