Skip to content

Conversation

@Amrit-Mohapatra-2008
Copy link

@Amrit-Mohapatra-2008 Amrit-Mohapatra-2008 commented Dec 24, 2025

Description:

Replace Manhattan-based shoreline sampling and per-candidate path checks with a single water-expansion search (multi-source, multi-target) that finds the nearest reachable shoreline by true boat distance.

Describe the PR.
The current boat pathfinding struggles with large disparities between straightline distance and real travel distance. This is because the heuristics based approach didn't punish land tiles enough.

The way I solved for this was by having a fill approach to path finding. Now, the target pixel will select an adjacent water node and check to see if it's a valid expansion. It will continue doing this until it eventually reaches the first shoreline pixel for the player. It then calls the AStar to determine what the ideal route would be.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

gravityswitch

Replace Manhattan-based shoreline sampling and per-candidate path checks with a single water-expansion search (multi-source, multi-target) that finds the nearest reachable shoreline by true boat distance.
@Amrit-Mohapatra-2008 Amrit-Mohapatra-2008 requested a review from a team as a code owner December 24, 2025 05:15
@CLAassistant
Copy link

CLAassistant commented Dec 24, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 24, 2025

Walkthrough

Added a water-aware Dijkstra-like search and MinHeap to TransportShipUtils, plus helpers to identify and traverse water tiles. Implemented and exported closestShoreFromPlayerByWater(...) and replaced several MiniAStar/BFS usages with this water-based search. canBuildTransportShip now returns TileRef | false.

Changes

Cohort / File(s) Summary
Transport ship water pathfinding
src/core/game/TransportShipUtils.ts
Added internal MinHeap<T> and water utilities (isWaterTile, adjacentWaterTiles); implemented exported closestShoreFromPlayerByWater(gm, player, targetShore, opts?) performing a Dijkstra-like search with maxSteps and waterFilter. Replaced prior MiniAStar/BFS usage in canBuildTransportShip, transportShipSpawn, sourceDstOceanShore, bestShoreDeploymentSource, and related helpers to use the new water-based search. Adjusted canBuildTransportShip signature/return to `TileRef

Sequence Diagram(s)

(omitted — changes are internal algorithm and single-module refactor; no multi-component sequential flow to visualize)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

Performance

Suggested reviewers

  • evanpelle

Poem

⛵ MinHeaps hum across the tide,
Tiles map routes where currents glide,
Dijkstra charts a watery course,
Shores connect without remorse,
Boats find home where paths collide.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: refactoring transport ship shore finding logic, which is the core focus of the changeset.
Description check ✅ Passed The description explains the motivation, implementation approach, and author's testing claims, all directly related to the transport ship pathfinding changes in the changeset.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/core/game/TransportShipUtils.ts (4)

4-52: MinHeap implementation looks solid.

The binary min-heap is correctly implemented with standard bubble operations. The bitwise shift for parent index calculation is efficient and idiomatic.

Optional: Consider extracting to a shared utility if other pathfinding code could benefit

If Dijkstra-style searches are used elsewhere in the codebase, extracting MinHeap to a shared data structures file could reduce duplication. For now, keeping it local is fine.


65-144: Dijkstra-based water pathfinding is correctly implemented.

The algorithm correctly performs multi-source (water tiles adjacent to target shore) to multi-target (water tiles adjacent to player shores) expansion. The early termination optimization (lines 98-101) and stale entry filtering (lines 118-119) are standard Dijkstra optimizations.

Minor items:

  1. Line 98 comment typo: "If the like destination water" should be "If the destination water tiles are" or just "If destination water"
  2. Lines 85-86, 87-88: Odd formatting with continue on a separate line - this will be fixed by Prettier
  3. Performance consideration: The default maxSteps of 250,000 provides safety but could take time on complex maps. The caller-specified values of 3,000,000 in some call sites are quite high. Consider documenting the expected performance characteristics or validating with large maps.
Quick typo fix
-  // If the like destination water is already next to the player's shore, return immediately
+  // If destination water is already next to the player's shore, return immediately

196-202: Water-based spawn detection correctly replaces previous logic.

The integration of closestShoreFromPlayerByWater with lake filtering is appropriate for determining valid lake transport spawn points.

The maxSteps: 3_000_000 is quite high. If lake pathfinding on large maps becomes slow, consider tuning this value or adding early-exit optimizations.


283-288: Shore deployment source correctly uses water pathfinding.

The integration is correct and the validation checks (line 287) are appropriate.

Line 283 comment clarity: "IMPORTANT CHANGE:One exact water search replaces MiniAStar + sampling" has a formatting issue (missing space after colon) and could be clearer. Suggest:

-  // IMPORTANT CHANGE:One exact water search replaces MiniAStar + sampling.
+  // Uses single water-expansion search instead of MiniAStar + sampling
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0793153 and 67e6335.

📒 Files selected for processing (1)
  • src/core/game/TransportShipUtils.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-19T06:00:38.007Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 784
File: src/core/game/StatsImpl.ts:125-134
Timestamp: 2025-05-19T06:00:38.007Z
Learning: In StatsImpl.ts, unused parameters in boat/stats-related methods are intentionally kept for future use and shouldn't be removed.

Applied to files:

  • src/core/game/TransportShipUtils.ts
📚 Learning: 2025-08-23T07:48:19.060Z
Learnt from: ElMelchizedek
Repo: openfrontio/OpenFrontIO PR: 1876
File: src/core/execution/FakeHumanExecution.ts:470-473
Timestamp: 2025-08-23T07:48:19.060Z
Learning: In FakeHumanExecution.ts DefensePost placement logic, returning -Infinity from structureSpawnTileValue when no sampled border tiles neighbor enemies is intentional. The logic samples up to 50 border tiles as a heuristic - if none are adjacent to enemies, it assumes DefensePost placement is unnecessary and aborts the entire placement attempt rather than continuing to evaluate individual tiles.

Applied to files:

  • src/core/game/TransportShipUtils.ts
🪛 GitHub Actions: 🧪 CI
src/core/game/TransportShipUtils.ts

[warning] 1-1: Code style issues found in this file. Run 'prettier --write' to fix.

🪛 GitHub Check: 🔍 ESLint
src/core/game/TransportShipUtils.ts

[failure] 57-57:
Prefer using nullish coalescing operator (??) instead of a logical or (||), as it is a safer operator.

🔇 Additional comments (5)
src/core/game/TransportShipUtils.ts (5)

61-63: LGTM!

Clean helper function with clear intent.


214-216: LGTM on spawn point determination.

Correctly uses the new water-based search to find reachable spawn shores.


231-238: Correct bidirectional water pathfinding integration.

Both source and destination shore determination now correctly use water-based reachability rather than Manhattan distance.


310-336: LGTM on refactoring to use filtered border tiles.

The variable naming is clearer with borderShoreTiles. Note that this function still uses Manhattan distance rather than water-based pathfinding, which appears intentional since it's generating candidates for testing rather than performing final pathfinding.


54-59: Remove unnecessary as any cast—all methods are already on the GameMap interface.

The fallback pattern checks for methods (isWater, isOcean, isLake) that are all defined on the GameMap interface. The as any cast can be removed. Simplify to:

function isWaterTile(gm: GameMap, t: TileRef): boolean {
  return gm.isWater(t);
}

This uses clean, idiomatic TypeScript without bypassing the type system.

Note: The || operator on line 57 is correct—it returns true if either condition is true. The ?? operator would not work here.

⛔ Skipped due to learnings
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

@iiamlewis iiamlewis added the needs-grooming This feature has not been approved. label Dec 24, 2025
@iiamlewis iiamlewis added this to the v29 milestone Dec 24, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/game/TransportShipUtils.ts (1)

334-345: Fix broken extremum tracking logic.

The else-if chain is incorrect. If a tile has both the minimum X coordinate AND minimum Y coordinate, only minX gets set, and minY remains null. Each extremum check should be independent.

🔎 Fix with independent checks
     if (cell.x < minX) {
       minX = cell.x;
       extremumTiles.minX = tile;
-    } else if (cell.y < minY) {
+    }
+    if (cell.y < minY) {
       minY = cell.y;
       extremumTiles.minY = tile;
-    } else if (cell.x > maxX) {
+    }
+    if (cell.x > maxX) {
       maxX = cell.x;
       extremumTiles.maxX = tile;
-    } else if (cell.y > maxY) {
+    }
+    if (cell.y > maxY) {
+      maxY = cell.y;
       extremumTiles.maxY = tile;
     }
🧹 Nitpick comments (2)
src/core/game/TransportShipUtils.ts (2)

4-52: MinHeap implementation looks correct.

The min-heap operations (push, pop, bubbleUp, bubbleDown) follow standard algorithms and handle edge cases properly.

Consider using a well-known priority queue library instead of a custom implementation to reduce maintenance burden and potential bugs in the future.


54-62: Avoid any cast; use proper type guards.

The cast to any bypasses TypeScript's type safety. Consider using type guards or extending the GameMap interface to include isWater if it's expected to exist.

🔎 Improved type-safe approach
 function isWaterTile(gm: GameMap, t: TileRef): boolean {
-  const anyGM = gm as any;
-  if (typeof anyGM.isWater === "function") return anyGM.isWater(t);
-  if (typeof anyGM.isOcean === "function") {
-    if (anyGM.isOcean(t)) return true;
+  const gmWithWater = gm as GameMap & { isWater?: (t: TileRef) => boolean };
+  if (gmWithWater.isWater) return gmWithWater.isWater(t);
+  if (gm.isOcean(t)) return true;
-    return gm.isLake(t);
-  }
   return gm.isLake(t);
 }

Note: From the relevant code snippets, GameMap already has isWater, isOcean, and isLake methods, so the defensive checks may be unnecessary.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67e6335 and e3600a1.

📒 Files selected for processing (1)
  • src/core/game/TransportShipUtils.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-19T06:00:38.007Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 784
File: src/core/game/StatsImpl.ts:125-134
Timestamp: 2025-05-19T06:00:38.007Z
Learning: In StatsImpl.ts, unused parameters in boat/stats-related methods are intentionally kept for future use and shouldn't be removed.

Applied to files:

  • src/core/game/TransportShipUtils.ts
📚 Learning: 2025-08-23T07:48:19.060Z
Learnt from: ElMelchizedek
Repo: openfrontio/OpenFrontIO PR: 1876
File: src/core/execution/FakeHumanExecution.ts:470-473
Timestamp: 2025-08-23T07:48:19.060Z
Learning: In FakeHumanExecution.ts DefensePost placement logic, returning -Infinity from structureSpawnTileValue when no sampled border tiles neighbor enemies is intentional. The logic samples up to 50 border tiles as a heuristic - if none are adjacent to enemies, it assumes DefensePost placement is unnecessary and aborts the entire placement attempt rather than continuing to evaluate individual tiles.

Applied to files:

  • src/core/game/TransportShipUtils.ts
🧬 Code graph analysis (1)
src/core/game/TransportShipUtils.ts (3)
src/core/game/GameMap.ts (2)
  • GameMap (6-51)
  • TileRef (3-3)
src/core/game/Game.ts (1)
  • Player (530-667)
src/core/worker/WorkerClient.ts (1)
  • transportShipSpawn (228-256)
🔇 Additional comments (3)
src/core/game/TransportShipUtils.ts (3)

68-147: Verify maxSteps default doesn't cause performance issues.

The default maxSteps of 250,000 is quite large. On maps with extensive water networks, this could cause noticeable delays or even timeouts in the pathfinding loop.

Consider testing this function on the largest expected maps to ensure the default limit provides adequate safety without degrading user experience. If timeouts occur, consider lowering the default or making it configurable based on map size.


236-246: LGTM: Water-based pathfinding correctly integrated.

The refactor properly uses the new water-aware search for both source and destination shores, with appropriate fallback to closestShoreTN for terra nullis.


291-298: Water-based search correctly replaces sampling approach.

The unified water search is cleaner than the previous MiniAStar + sampling logic. The validation at line 297 correctly ensures the result is a valid shore owned by the player.

Note: maxSteps: 500_000 is still quite high; consider reducing if performance testing reveals issues.

Comment on lines +201 to +207
const spawn = closestShoreFromPlayerByWater(game, player, dst, {
waterFilter: (t) => game.isLake(t),
maxSteps: 3_000_000,
});

for (const t of sorted) {
if (game.owner(t) === player) {
return transportShipSpawn(game, player, t);
}
}
return false;
if (spawn === null) return false;
return transportShipSpawn(game, player, spawn);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Excessive maxSteps value may cause performance degradation.

The maxSteps: 3_000_000 is extremely large and could cause significant delays or lock up the game on maps with large lake networks. This is 12× the default value of 250,000.

Consider reducing this to a more reasonable value (e.g., 500,000 or less) or making it proportional to map size. If long searches are truly necessary, consider adding progress indicators or moving this to a web worker to avoid blocking the main thread.

🤖 Prompt for AI Agents
In src/core/game/TransportShipUtils.ts around lines 201 to 207 the explicit
maxSteps value is set to an excessively large 3_000_000 which can freeze or
severely slow the game on large lake networks; change this to a safer default
(e.g., 500_000 or lower) or compute it proportionally from map size (for example
derive maxSteps from total tile count or map dimensions) and use that computed
value here; if long searches are required, move the search off the main thread
(web worker) or make it incremental with progress indicators to avoid blocking;
update the call to closestShoreFromPlayerByWater to use the new
bounded/proportional maxSteps.

Comment on lines +219 to +221
const spawn = closestShoreFromPlayerByWater(game, player, targetTile, {
maxSteps: 3_000_000,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Excessive maxSteps value here as well.

Same concern as in canBuildTransportShip: the maxSteps: 3_000_000 could cause performance issues on large maps.

Reduce to a more reasonable value or make it configurable based on map characteristics.

maxY: null,
};

maxY = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove dead code: maxY = 1; assignment.

This assignment appears to be leftover debug code or a copy-paste error. The value is never read before being potentially overwritten at line 343-344.

🔎 Remove dead code
-  maxY = 1;
   const borderShoreTiles = Array.from(player.borderTiles()).filter((t) =>
     gm.isShore(t),
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
maxY = 1;
const borderShoreTiles = Array.from(player.borderTiles()).filter((t) =>
gm.isShore(t),
);
🤖 Prompt for AI Agents
In src/core/game/TransportShipUtils.ts around line 320, there is a dead
assignment "maxY = 1;" that is never read before being potentially overwritten
later (lines ~343-344); remove that line entirely and run tests/lint to ensure
no other code relies on that temporary value and that variable
initialization/usage remains correct where maxY is actually set.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
src/core/game/TransportShipUtils.ts (3)

201-207: Excessive maxSteps value may cause performance issues.

The maxSteps: 3_000_000 remains extremely large (12× the default 250,000) and could freeze the game on maps with large lake networks.

Consider reducing to a safer bound (e.g., 500,000) or computing it proportionally from map dimensions.


219-221: Excessive maxSteps value here as well.

Same concern: maxSteps: 3_000_000 could cause performance degradation. Reduce to a safer value or make it map-size proportional.


320-320: Remove dead code.

The assignment maxY = 1; is never read before being overwritten later. This appears to be leftover debug code.

🔎 Remove dead code
-  maxY = 1;
   const borderShoreTiles = Array.from(player.borderTiles()).filter((t) =>
     gm.isShore(t),
   );
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3600a1 and a53c394.

📒 Files selected for processing (1)
  • src/core/game/TransportShipUtils.ts
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-05-19T06:00:38.007Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 784
File: src/core/game/StatsImpl.ts:125-134
Timestamp: 2025-05-19T06:00:38.007Z
Learning: In StatsImpl.ts, unused parameters in boat/stats-related methods are intentionally kept for future use and shouldn't be removed.

Applied to files:

  • src/core/game/TransportShipUtils.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: For the window close confirmation feature in `ClientGameRunner.ts`, the troop count requirement (>10,000 troops) from issue #2137 was intentionally removed because it was arbitrary and troop count can be reported as low despite having significant land. The confirmation now triggers for any alive player regardless of troop count.

Applied to files:

  • src/core/game/TransportShipUtils.ts
📚 Learning: 2025-12-13T14:58:29.645Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Applied to files:

  • src/core/game/TransportShipUtils.ts
📚 Learning: 2025-08-23T07:48:19.060Z
Learnt from: ElMelchizedek
Repo: openfrontio/OpenFrontIO PR: 1876
File: src/core/execution/FakeHumanExecution.ts:470-473
Timestamp: 2025-08-23T07:48:19.060Z
Learning: In FakeHumanExecution.ts DefensePost placement logic, returning -Infinity from structureSpawnTileValue when no sampled border tiles neighbor enemies is intentional. The logic samples up to 50 border tiles as a heuristic - if none are adjacent to enemies, it assumes DefensePost placement is unnecessary and aborts the entire placement attempt rather than continuing to evaluate individual tiles.

Applied to files:

  • src/core/game/TransportShipUtils.ts
📚 Learning: 2025-11-29T22:22:37.178Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2539
File: src/core/game/GameImpl.ts:520-542
Timestamp: 2025-11-29T22:22:37.178Z
Learning: In GameImpl.ts, neighborsWithDiag() and forEachNeighborWithDiag() intentionally duplicate coordinate iteration logic. They serve different purposes: forEachNeighborWithDiag() is a zero-allocation hot-path optimization while neighborsWithDiag() is a convenience method that returns an array. Refactoring one to call the other would add callback/closure allocations and indirection overhead, defeating the performance goals.

Applied to files:

  • src/core/game/TransportShipUtils.ts
🧬 Code graph analysis (1)
src/core/game/TransportShipUtils.ts (3)
src/core/game/TerrainSearchMap.ts (1)
  • node (18-29)
src/core/game/GameMap.ts (2)
  • GameMap (6-56)
  • TileRef (3-3)
src/core/game/Game.ts (1)
  • Player (541-681)
🔇 Additional comments (6)
src/core/game/TransportShipUtils.ts (6)

4-52: MinHeap implementation looks correct.

The binary min-heap implementation is clean and appropriate for the Dijkstra-like search. The heap operations (bubbleUp/bubbleDown) correctly maintain the min-heap property.


64-66: LGTM!

Clean helper function that correctly filters water neighbors.


68-147: Water-based pathfinding implementation is sound.

The Dijkstra-based search correctly implements multi-source, multi-target pathfinding through water tiles. The algorithm properly:

  • Starts from water adjacent to the target shore
  • Expands through valid water tiles
  • Terminates early when reaching player-accessible shores
  • Respects the maxSteps safety bound

236-245: Water-based shore resolution is correctly applied.

The function properly uses closestShoreFromPlayerByWater for both source and destination shores, with sensible defaults. The logic correctly handles both Player and TerraNullius cases.


291-298: Well-designed replacement of sampling-based approach.

The single water search with maxSteps: 500_000 is a reasonable bound (2× default) that replaces the previous MiniAStar + sampling logic. The guard checks after the search ensure result validity.


54-62: Remove any cast for type safety.

The cast to any on line 55 defeats TypeScript's type checking. According to the GameMap interface definition, isWater(ref: TileRef): boolean is already defined, so the runtime check is unnecessary.

🔎 Proposed fix
 function isWaterTile(gm: GameMap, t: TileRef): boolean {
-  const anyGM = gm as any;
-  if (typeof anyGM.isWater === "function") return anyGM.isWater(t);
-  if (typeof anyGM.isOcean === "function") {
-    if (anyGM.isOcean(t)) return true;
-    return gm.isLake(t);
-  }
-  return gm.isLake(t);
+  return gm.isWater(t);
 }
⛔ Skipped due to learnings
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Comment on lines +54 to +62
function isWaterTile(gm: GameMap, t: TileRef): boolean {
const anyGM = gm as any;
if (typeof anyGM.isWater === "function") return anyGM.isWater(t);
if (typeof anyGM.isOcean === "function") {
if (anyGM.isOcean(t)) return true;
return gm.isLake(t);
}
return gm.isLake(t);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all GameMap implementations and verify isWater method exists

# Search for class/interface implementations of GameMap
rg -n --type=ts 'class \w+ implements.*GameMap' -A 5

# Search for isWater method definitions
rg -n --type=ts 'isWater\s*\(' -B 2 -A 2

# Find GameMap type/interface definitions
rg -n --type=ts 'interface GameMap|type GameMap' -A 30

Repository: openfrontio/OpenFrontIO

Length of output: 11244


Remove unnecessary defensive checks for interface-guaranteed methods.

The GameMap interface explicitly defines both isWater and isOcean methods. All implementations (GameMapImpl, GameView, GameImpl) implement these methods, so the defensive typeof checks are unnecessary. Simplify to:

function isWaterTile(gm: GameMap, t: TileRef): boolean {
  return gm.isWater(t);
}

The fallback chain to isOcean and isLake is confusing and doesn't match the interface contract or actual implementation behavior.

🤖 Prompt for AI Agents
In src/core/game/TransportShipUtils.ts around lines 54 to 62, the implementation
performs runtime typeof checks and falls back to isOcean/isLake despite the
GameMap interface guaranteeing isWater/isOcean; remove the defensive checks and
simplify the function to directly call and return gm.isWater(t) so it aligns
with the interface and implementations.

const starts = adjacentWaterTiles(gm, targetShore).filter(waterFilter);
if (starts.length === 0) return null;

// If the like destination water is already next to the player's shore, return immediately
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in comment.

Line 100 has a typo: "the like destination water" should be "the destination water" or "the target destination water".

🔎 Proposed fix
-  // If the like destination water is already next to the player's shore, return immediately
+  // If the destination water is already next to the player's shore, return immediately
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// If the like destination water is already next to the player's shore, return immediately
// If the destination water is already next to the player's shore, return immediately
🤖 Prompt for AI Agents
In src/core/game/TransportShipUtils.ts around line 100, fix the typo in the
comment: replace "the like destination water" with "the destination water" (or
"the target destination water") so the comment reads clearly that if the
destination water is already next to the player's shore, return immediately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-grooming This feature has not been approved.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants