Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions src/core/pathfinding/algorithms/AStar.AbstractGraph.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { PathFinder } from "../types";
import { AbstractGraph } from "./AbstractGraph";
import { BucketQueue, MinHeap, PriorityQueue } from "./PriorityQueue";
import { MinHeap, PriorityQueue } from "./PriorityQueue";

export interface AbstractGraphAStarConfig {
heuristicWeight?: number;
maxIterations?: number;
useMinHeap?: boolean; // Use MinHeap instead of BucketQueue (better for variable costs)
}
Comment on lines 5 to 8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find any lingering callers passing the removed option, and existing tests for this file.
rg -nP '\buseMinHeap\b' -C2 --type=ts
rg -nP 'AbstractGraphAStar' -C2 --glob '**/*test*' --glob '**/*spec*'

Repository: openfrontio/OpenFrontIO

Length of output: 49


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Re-check remaining callers for `useMinHeap`
echo "=== useMinHeap occurrences (TypeScript) ==="
rg -n --no-heading '\buseMinHeap\b' --type=ts -S .

# 2) Find tests/specs that mention AStar / pathfinding
echo
echo "=== AStar-related tests/specs ==="
rg -n --no-heading 'AStar|AbstractGraphAStar|pathfinding|PriorityQueue|MinHeap|useMinHeap' \
  --glob '**/*test*.*' --glob '**/*spec*.*' .

# 3) Inspect the AStar file for queue/resize/numNodes overflow logic hints
echo
echo "=== Key queue/overflow keywords in AStar.AbstractGraph.ts ==="
rg -n --no-heading 'queue|PriorityQueue|numNodes|resize|overflow|twisty|height|width|maxIterations|heuristicWeight' \
  src/core/pathfinding/algorithms/AStar.AbstractGraph.ts

# 4) Show a small window around interface and config usage
echo
echo "=== Interface + config usage around AbstractGraphAStarConfig ==="
rg -n --no-heading 'AbstractGraphAStarConfig|heuristicWeight|maxIterations' \
  -n src/core/pathfinding/algorithms/AStar.AbstractGraph.ts

Repository: openfrontio/OpenFrontIO

Length of output: 111


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "=== useMinHeap occurrences (TypeScript) ==="
rg -n --no-heading '\buseMinHeap\b' --type=ts -S . || true

echo
echo "=== AStar-related tests/specs ==="
rg -n --no-heading 'AStar|AbstractGraphAStar|pathfinding|PriorityQueue|MinHeap|useMinHeap' \
  --glob '**/*test*.*' --glob '**/*spec*.*' . || true

echo
echo "=== Key queue/overflow keywords in AStar.AbstractGraph.ts ==="
rg -n --no-heading 'queue|PriorityQueue|numNodes|resize|overflow|twisty|height|width|maxIterations|heuristicWeight' \
  src/core/pathfinding/algorithms/AStar.AbstractGraph.ts || true

echo
echo "=== Interface + config usage around AbstractGraphAStarConfig ==="
rg -n --no-heading 'AbstractGraphAStarConfig|heuristicWeight|maxIterations' \
  src/core/pathfinding/algorithms/AStar.AbstractGraph.ts || true

Repository: openfrontio/OpenFrontIO

Length of output: 5383


🏁 Script executed:

#!/bin/bash
set +euo pipefail

# Inspect the relevant AStar file sections
echo "=== AStar.AbstractGraph.ts (relevant sections) ==="
sed -n '1,260p' src/core/pathfinding/algorithms/AStar.AbstractGraph.ts

# Look for direct test coverage references
echo
echo "=== Test files referencing AStar.AbstractGraph / MinHeap / PriorityQueue ==="
rg -n --no-heading 'AStar\.AbstractGraph|AbstractGraphAStarConfig|new (MinHeap|PriorityQueue)|MinHeap|PriorityQueue' \
  --glob '**/*test*.*' --glob '**/*spec*.*' tests . || true

# Look for useMinHeap in the whole repo (non-type limited)
echo
echo "=== useMinHeap occurrences (whole repo) ==="
rg -n --no-heading '\buseMinHeap\b' . || true

Repository: openfrontio/OpenFrontIO

Length of output: 7563


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "=== AStar.AbstractGraph.ts (relevant sections) ==="
sed -n '1,260p' src/core/pathfinding/algorithms/AStar.AbstractGraph.ts

echo
echo "=== Test files referencing AStar.AbstractGraph / AbstractGraphAStarConfig / MinHeap / PriorityQueue ==="
rg -n --no-heading 'AStar\.AbstractGraph|AbstractGraphAStarConfig|new (MinHeap|PriorityQueue)|MinHeap|PriorityQueue' \
  --glob '**/*test*.*' --glob '**/*spec*.*' tests . || true

echo
echo "=== useMinHeap occurrences (whole repo) ==="
rg -n --no-heading '\buseMinHeap\b' . || true

Repository: openfrontio/OpenFrontIO

Length of output: 7590


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "=== Where AbstractGraphAStar is used (src + tests) ==="
rg -n --no-heading '\bAbstractGraphAStar\b' src tests . || true

echo
echo "=== Where MinHeap/PriorityQueue are used ==="
rg -n --no-heading '\bMinHeap\b|\bPriorityQueue\b' src/core/pathfinding . || true

echo
echo "=== Check AStar wrapper/pathfinder implementations that might hit AbstractGraphAStar ==="
rg -n --no-heading 'UniversalPathFinding|PathFinderStepper|ConnectedComponents|PathFinding\.(Air|Rail|Water)|PathFinder\.' \
  src/core/pathfinding tests/core/pathfinding || true

Repository: openfrontio/OpenFrontIO

Length of output: 12836


🏁 Script executed:

rg -n --no-heading 'WaterHierarchical|AbstractGraphAStar' src/core/pathfinding src/core/pathfinding/algorithms/ AStar.WaterHierarchical.ts

Repository: openfrontio/OpenFrontIO

Length of output: 2433


🏁 Script executed:

#!/bin/bash
set +euo pipefail

# Show MinHeap overflow/resize logic
echo "=== PriorityQueue.ts (MinHeap capacity/resize checks) ==="
rg -n --no-heading 'capacity|exceeded|resize|grow|push\(' src/core/pathfinding/algorithms/PriorityQueue.ts
echo
sed -n '1,220p' src/core/pathfinding/algorithms/PriorityQueue.ts

# Show WaterHierarchical algorithm using AbstractGraphAStar
echo
echo "=== AStar.WaterHierarchical.ts ==="
sed -n '1,220p' src/core/pathfinding/algorithms/AStar.WaterHierarchical.ts

# Show PathFinding.Water test coverage context
echo
echo "=== tests/core/pathfinding/PathFinding.Water.test.ts ==="
sed -n '1,240p' tests/core/pathfinding/PathFinding.Water.test.ts

Repository: openfrontio/OpenFrontIO

Length of output: 19250


🏁 Script executed:

#!/bin/bash
set +euo pipefail

# Show MinHeap overflow/resize logic
echo "=== PriorityQueue.ts (MinHeap capacity/resize checks) ==="
rg -n --no-heading 'capacity|exceeded|resize|grow|push\(' src/core/pathfinding/algorithms/PriorityQueue.ts
echo
sed -n '1,220p' src/core/pathfinding/algorithms/PriorityQueue.ts

# Show WaterHierarchical algorithm using AbstractGraphAStar
echo
echo "=== AStar.WaterHierarchical.ts ==="
sed -n '1,220p' src/core/pathfinding/algorithms/AStar.WaterHierarchical.ts

# Show PathFinding.Water test coverage context
echo
echo "=== tests/core/pathfinding/PathFinding.Water.test.ts ==="
sed -n '1,240p' tests/core/pathfinding/PathFinding.Water.test.ts

Repository: openfrontio/OpenFrontIO

Length of output: 19250


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "=== Search tests for MinHeap overflow warning text ==="
rg -n --no-heading 'MinHeap capacity exceeded' tests src . || true

echo
echo "=== Search tests for PriorityQueue/MinHeap resize-related spies ==="
rg -n --no-heading 'capacity exceeded|Resizing, but this indicates a bug|MinHeap|PriorityQueue' tests . || true

echo
echo "=== Search tests that spy on console.error ==="
rg -n --no-heading 'spyOn\(console,\s*"error"\)|console\.error' tests . || true

Repository: openfrontio/OpenFrontIO

Length of output: 21191


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "=== Search tests for MinHeap overflow warning text ==="
rg -n --no-heading 'MinHeap capacity exceeded' tests src . || true

echo
echo "=== Search tests for PriorityQueue/MinHeap resize-related spies ==="
rg -n --no-heading 'capacity exceeded|Resizing, but this indicates a bug|MinHeap|PriorityQueue' tests . || true

echo
echo "=== Search tests that spy on console.error ==="
rg -n --no-heading 'spyOn\(console,\s*"error"\)|console\.error' tests . || true

Repository: openfrontio/OpenFrontIO

Length of output: 21191


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "=== PathFinder.Water wiring ==="
rg -n --no-heading 'Water\(game\)|PathFinding\.Water|new AStarWaterHierarchical|AStarWaterHierarchical|WaterHierarchical' src/core/pathfinding/PathFinder.ts src/core/pathfinding/PathFinderBuilder.ts src/core/pathfinding/PathFinder.*.ts src/core/pathfinding/*.ts src/core/pathfinding/**.ts || true

# Show the Water factory implementation if found in PathFinder.ts
rg -n --no-heading 'PathFinding\.Water|function Water|static Water|Water\(game\)' src/core/pathfinding/PathFinder.ts || true
sed -n '1,240p' src/core/pathfinding/PathFinder.ts

Repository: openfrontio/OpenFrontIO

Length of output: 8147


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== PathFinding.Water wiring (which A* gets used) ==="
rg -n --no-heading 'PathFinding\.Water|Water\(game\)|AStar\.WaterHierarchical|AStarWaterHierarchical|WaterHierarchical' src/core/pathfinding/PathFinder.ts src/core/pathfinding/PathFinder.*.ts src/core/pathfinding/**.ts || true

echo
echo "=== PathFinder.ts (where Water is built) ==="
sed -n '1,260p' src/core/pathfinding/PathFinder.ts

Repository: openfrontio/OpenFrontIO

Length of output: 7857


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "=== Find miniWaterHPA/miniWaterGraph methods ==="
rg -n --no-heading 'miniWaterHPA\(|miniWaterGraph\(' src/core/game src/core -S || true

echo
echo "=== Inspect Setup util to see how test worlds are initialized ==="
rg -n --no-heading 'setup\("' tests/util/Setup.* tests -S || true
fd -t f Setup tests/util -x echo {} | head -n 20 || true
ls -la tests/util || true

Repository: openfrontio/OpenFrontIO

Length of output: 5681


🏁 Script executed:

#!/bin/bash
set +euo pipefail

echo "=== Find miniWaterHPA / miniWaterGraph ==="
rg -n --no-heading 'miniWaterHPA\(|miniWaterGraph\(' src/core/game src/core -S || true

echo
echo "=== Locate tests/util/Setup ==="
ls -la tests/util || true
fd -t f 'Setup.*' tests/util -a || true
fd -t f 'Setup.*' tests -a || true

echo
echo "=== Find how `setup('ocean_and_land')` is configured ==="
rg -n --no-heading 'setup\("ocean_and_land"|setup\("world"' tests -S || true

echo
echo "=== Open tests/util/Setup.ts (or closest match) ==="
setup_file=$(fd -t f 'Setup.*\.(ts|js|tsx)$' tests/util tests -a | head -n 1)
echo "Using: ${setup_file:-<not found>}"
if [[ -n "${setup_file}" ]]; then
  sed -n '1,240p' "$setup_file"
fi

Repository: openfrontio/OpenFrontIO

Length of output: 5835


Add a regression test for AbstractGraphAStar MinHeap overflow behavior

AStar.AbstractGraph.ts now sizes the MinHeap to avoid the “capacity exceeded”/resize path caused by lazy duplicate pushes.

  • Add a regression test that runs AbstractGraphAStar.findPath on an AbstractGraph setup that forces enough re-entries (lazy duplicates) to exceed the old numNodes-sized heap.
  • Assert console.error is not called with MinHeap capacity exceeded (and keep asserting the returned path is still valid).

There are no remaining useMinHeap usages in the repo, so caller updates are already handled by the type changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/pathfinding/algorithms/AStar.AbstractGraph.ts` around lines 5 - 8,
Write a regression test that constructs an AbstractGraph instance and invokes
AbstractGraphAStar.findPath with a config that forces many lazy duplicate pushes
(enough re-entries to exceed the previous numNodes-sized heap) so the MinHeap
must be sized correctly; mock/spy on console.error and assert it is never called
with the string "MinHeap capacity exceeded", and also assert the returned path
is correct/valid. Use the AbstractGraphAStar.findPath entrypoint and supply a
graph topology (e.g., a grid or crafted node re-entry pattern) and A* config
(heuristicWeight/maxIterations) to trigger duplicate re-insertions, and verify
no capacity error and that path length/contents match expected.


export class AbstractGraphAStar implements PathFinder<number> {
Expand Down Expand Up @@ -34,17 +33,10 @@ export class AbstractGraphAStar implements PathFinder<number> {
this.cameFrom = new Int32Array(numNodes);
this.startNode = new Int32Array(numNodes);

// For abstract graphs with variable costs, MinHeap may be better
// BucketQueue is O(1) but requires integer priorities
if (config?.useMinHeap) {
this.queue = new MinHeap(numNodes);
} else {
// Estimate max priority: weight * (mapWidth + mapHeight)
// Use cluster size * clusters as approximation
const maxDist = graph.clusterSize * Math.max(graph.clustersX, 10) * 2;
const maxF = this.heuristicWeight * maxDist;
this.queue = new BucketQueue(maxF);
}
// MinHeap: abstract edge costs are variable and long routes accumulate f-values beyond any cheap bucket-range estimate.
// A* also pushes lazy duplicates (a node re-enters the queue each time its gScore improves), so live entries can exceed
// numNodes; size for the worst case — one push per directed edge relaxation — to avoid the heap's resize-with-error path.
this.queue = new MinHeap(numNodes + graph.edgeCount * 2);
}

findPath(start: number | number[], goal: number): number[] | null {
Expand Down
Loading