Skip to content

Conversation

@Aareksio
Copy link
Contributor

@Aareksio Aareksio commented Jan 7, 2026

wasn't supposed to land in main repo

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

Walkthrough

This change adds a new "world" test map asset with configuration files (info.json and manifest.json), registers it in the maps registry, and introduces comprehensive test suites for pathfinding components (MiniAStar and PathFinder) with supporting test utilities for map creation from string representations.

Changes

Cohort / File(s) Summary
World Map Data
map-generator/assets/test_maps/world/info.json, tests/testdata/maps/world/manifest.json
New static JSON configuration files defining world map structure with nation coordinates, flags, and metadata.
Map Registry
map-generator/main.go
Registers new "world" test map entry; potential duplicate entry noted that may require attention.
Pathfinding Tests
tests/core/pathfinding/MiniAStar.test.ts, tests/core/pathfinding/PathFinder.test.ts
New comprehensive test suites covering basic pathfinding operations, error handling, destination changes, and world-scale navigation scenarios.
Test Utilities
tests/core/pathfinding/utils.ts
New exported helper function mapFromString() that builds Game instances from string-based terrain maps using land/water bit flags.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Maps

Suggested reviewers

  • evanpelle
  • Duwibi
  • scottanderson

Poem

🗺️ A world map draws upon the screen,
Pathfinding tests fill gaps unseen,
Land and water dance in bytes,
From one shore to distant heights!
Nations marked, the journey's true. 🧭

Pre-merge checks

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive No description was provided by the author, making it impossible to assess relevance to the changeset. Add a brief description explaining the purpose of the pathfinding tests being introduced, such as coverage goals or what functionality is being tested.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Pathfinding - 1 - Tests' directly matches the changeset focus: adding comprehensive test suites for pathfinding components (MiniAStar, PathFinder) and supporting test utilities.

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.

@Aareksio Aareksio closed this Jan 7, 2026
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

🤖 Fix all issues with AI agents
In @map-generator/main.go:
- Line 71: Remove the duplicate map registry entry for Name: "world" that has
IsTest: true; locate the maps slice/registry where entries like {Name: "world",
IsTest: ...} are defined and delete the test duplicate (the one with IsTest:
true) so the world map is registered only once, or alternatively remove the
non-test entry if you intend the test variant to be canonical—ensure only a
single entry for Name "world" remains.
🧹 Nitpick comments (2)
tests/core/pathfinding/utils.ts (1)

112-112: GameMapType.Asia is hardcoded regardless of actual map used.

The gameConfig uses GameMapType.Asia even when this utility is used to create world maps or other test maps. This is just metadata and doesn't affect pathfinding tests since the actual terrain comes from the constructed GameMapImpl, but it could be confusing when debugging.

tests/core/pathfinding/PathFinder.test.ts (1)

195-259: Consider extracting coordinate constants for reusability.

The world map tests are well structured and the coordinate comment is helpful. However, the coordinates are duplicated across tests (e.g., Spain appears in tests on lines 204 and 241).

🔎 Optional refactor to reduce duplication
 describe("PathFinder world map tests", () => {
   // Ocean shoreline coordinates:
-  // Spain east coast: [926, 283], France south coast: [950, 257]
-  // Poland north coast: [1033, 175], Miami: [488, 355], Rio: [680, 658]
+  const COORDS = {
+    SPAIN_EAST: { x: 926, y: 283 },
+    FRANCE_SOUTH: { x: 950, y: 257 },
+    POLAND_NORTH: { x: 1033, y: 175 },
+    MIAMI: { x: 488, y: 355 },
+    RIO: { x: 680, y: 658 },
+  };
 
   test("finds path Spain to France (Mediterranean)", async () => {
     const game = await setup("world");
     const pathFinder = createPathFinder(game);
 
-    const src = game.ref(926, 283); // Spain east coast
-    const dst = game.ref(950, 257); // France south coast
+    const src = game.ref(COORDS.SPAIN_EAST.x, COORDS.SPAIN_EAST.y);
+    const dst = game.ref(COORDS.FRANCE_SOUTH.x, COORDS.FRANCE_SOUTH.y);
 
     const result = navigateTo(pathFinder, src, dst, 500);
     expect(result.reached).toBe(true);
     expect(result.steps).toBeGreaterThan(0);
   });
   
   // ... apply to other tests
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between effce3d and 380e93a.

⛔ Files ignored due to path filters (4)
  • map-generator/assets/test_maps/world/image.png is excluded by !**/*.png
  • tests/testdata/maps/world/map.bin is excluded by !**/*.bin
  • tests/testdata/maps/world/map16x.bin is excluded by !**/*.bin
  • tests/testdata/maps/world/map4x.bin is excluded by !**/*.bin
📒 Files selected for processing (7)
  • map-generator/assets/test_maps/world/info.json
  • map-generator/main.go
  • tests/core/pathfinding/MiniAStar.test.ts
  • tests/core/pathfinding/PathFinder.test.ts
  • tests/core/pathfinding/utils.ts
  • tests/testdata/maps/world/manifest.json
  • tests/testdata/maps/world/thumbnail.webp
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-08-19T11:00:55.422Z
Learnt from: TheGiraffe3
Repo: openfrontio/OpenFrontIO PR: 1864
File: resources/maps/arabianpeninsula/manifest.json:13-170
Timestamp: 2025-08-19T11:00:55.422Z
Learning: In OpenFrontIO, nation names in map manifests are displayed directly in the UI without translation. They do not need to be added to resources/lang/en.json or processed through translateText(). This is the established pattern across all existing maps including Europe, World, Asia, Africa, and others.

Applied to files:

  • map-generator/assets/test_maps/world/info.json
  • tests/testdata/maps/world/manifest.json
📚 Learning: 2025-07-12T06:35:10.457Z
Learnt from: Aotumuri
Repo: openfrontio/OpenFrontIO PR: 1357
File: resources/lang/ja.json:0-0
Timestamp: 2025-07-12T06:35:10.457Z
Learning: In OpenFrontIO project, "giantworldmap" is the correct localization key name for the giant world map, used consistently across all language files and TypeScript code. Do not suggest renaming this key.

Applied to files:

  • map-generator/assets/test_maps/world/info.json
📚 Learning: 2025-08-10T14:37:35.777Z
Learnt from: TheGiraffe3
Repo: openfrontio/OpenFrontIO PR: 1770
File: resources/maps/world/manifest.json:279-355
Timestamp: 2025-08-10T14:37:35.777Z
Learning: The OpenFrontIO World Map includes both real-world nations and fictional/creative entities like "Polar Bears" as playable nations. These special nations may use non-two-letter flag codes (e.g., "polar_bears" instead of a two-letter code), and this is intentional design. The corresponding flag assets are stored in resources/flags/ with matching filenames.

Applied to files:

  • map-generator/assets/test_maps/world/info.json
  • tests/testdata/maps/world/manifest.json
📚 Learning: 2025-05-22T23:43:54.789Z
Learnt from: jrouillard
Repo: openfrontio/OpenFrontIO PR: 848
File: src/core/execution/TradeShipExecution.ts:110-134
Timestamp: 2025-05-22T23:43:54.789Z
Learning: In the OpenFrontIO project, MiniAStar.compute() only returns PathFindResultType.Pending, PathFindResultType.Completed, or PathFindResultType.PathNotFound - it doesn't return PathFindResultType.NextTile. When PathFindResultType.Completed is returned, the full path can be retrieved via reconstructPath().

Applied to files:

  • tests/core/pathfinding/MiniAStar.test.ts
🧬 Code graph analysis (1)
tests/core/pathfinding/MiniAStar.test.ts (3)
src/core/game/GameMap.ts (1)
  • TileRef (3-3)
src/core/pathfinding/MiniAStar.ts (1)
  • MiniAStar (41-101)
tests/core/pathfinding/utils.ts (1)
  • mapFromString (30-134)
🔇 Additional comments (3)
tests/core/pathfinding/PathFinder.test.ts (3)

58-102: LGTM! Basic behavior tests are well structured.

The test suite covers the essential state machine behaviors: first call, completion, immediate completion, and path continuation. The type guard on line 86 is good defensive programming.


104-145: LGTM! Destination change tests cover dynamic scenarios.

These tests properly validate that the pathfinder handles destination changes during navigation, both for sequential goals and mid-path recomputation.


179-192: LGTM! Good practice to document known limitations.

Using test.fails to track a known bug is excellent. When the upscaling feature is implemented, remember to remove the .fails modifier so this test validates the fix.

{Name: "ocean_and_land", IsTest: true},
{Name: "plains", IsTest: true},
{Name: "giantworldmap", IsTest: true},
{Name: "world", IsTest: true},
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

Remove duplicate "world" map entry.

The maps registry already has a "world" entry at line 62. Adding another "world" entry with IsTest: true at line 71 creates a duplicate that will cause the world map to be processed twice when no --maps flag is provided. While they write to different output directories (test vs production), this wastes resources during map generation.

🔎 Proposed fix

If you need a test version of the world map, either:

  1. Remove the non-test entry at line 62 and keep only the test entry at line 71, or
  2. Remove the test entry at line 71 if the production world map is sufficient for testing
-	{Name: "world", IsTest: true},
📝 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
{Name: "world", IsTest: true},
🤖 Prompt for AI Agents
In @map-generator/main.go at line 71, Remove the duplicate map registry entry
for Name: "world" that has IsTest: true; locate the maps slice/registry where
entries like {Name: "world", IsTest: ...} are defined and delete the test
duplicate (the one with IsTest: true) so the world map is registered only once,
or alternatively remove the non-test entry if you intend the test variant to be
canonical—ensure only a single entry for Name "world" remains.

Comment on lines +16 to +54
function navigateTo(
pathFinder: PathFinder,
from: TileRef,
to: TileRef,
maxIter = 100,
): {
reached: boolean;
notFound: boolean;
pos: TileRef;
steps: number;
path: TileRef[];
} {
const status = {
reached: false,
notFound: false,
pos: from,
steps: 0,
path: [] as TileRef[],
};

for (let i = 0; i < maxIter; i++) {
const result = pathFinder.nextTile(status.pos, to);

if (result.type === PathFindResultType.NextTile) {
status.path.push(result.node);
status.pos = result.node;
status.steps++;
} else if (result.type === PathFindResultType.Completed) {
status.path.push(result.node);
status.reached = true;
return status;
} else if (result.type === PathFindResultType.PathNotFound) {
status.notFound = true;
return status;
}
}

return status;
}
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

Clarify the third parameter usage and timeout state.

Two observations:

  1. Parameter inconsistency: Line 37 calls pathFinder.nextTile(status.pos, to) with 2 arguments, but line 83 shows the method accepts a third parameter. If this parameter is meaningful, it should be passed here.

  2. Ambiguous timeout state: When maxIter is reached, both reached and notFound remain false. Consider adding a third state like timeout: boolean to distinguish timeouts from in-progress navigation.

🔎 Suggested improvements
 function navigateTo(
   pathFinder: PathFinder,
   from: TileRef,
   to: TileRef,
   maxIter = 100,
 ): {
   reached: boolean;
   notFound: boolean;
+  timeout: boolean;
   pos: TileRef;
   steps: number;
   path: TileRef[];
 } {
   const status = {
     reached: false,
     notFound: false,
+    timeout: false,
     pos: from,
     steps: 0,
     path: [] as TileRef[],
   };
 
   for (let i = 0; i < maxIter; i++) {
     const result = pathFinder.nextTile(status.pos, to);
 
     if (result.type === PathFindResultType.NextTile) {
       status.path.push(result.node);
       status.pos = result.node;
       status.steps++;
     } else if (result.type === PathFindResultType.Completed) {
       status.path.push(result.node);
       status.reached = true;
       return status;
     } else if (result.type === PathFindResultType.PathNotFound) {
       status.notFound = true;
       return status;
     }
   }
 
+  status.timeout = true;
   return status;
 }
📝 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
function navigateTo(
pathFinder: PathFinder,
from: TileRef,
to: TileRef,
maxIter = 100,
): {
reached: boolean;
notFound: boolean;
pos: TileRef;
steps: number;
path: TileRef[];
} {
const status = {
reached: false,
notFound: false,
pos: from,
steps: 0,
path: [] as TileRef[],
};
for (let i = 0; i < maxIter; i++) {
const result = pathFinder.nextTile(status.pos, to);
if (result.type === PathFindResultType.NextTile) {
status.path.push(result.node);
status.pos = result.node;
status.steps++;
} else if (result.type === PathFindResultType.Completed) {
status.path.push(result.node);
status.reached = true;
return status;
} else if (result.type === PathFindResultType.PathNotFound) {
status.notFound = true;
return status;
}
}
return status;
}
function navigateTo(
pathFinder: PathFinder,
from: TileRef,
to: TileRef,
maxIter = 100,
): {
reached: boolean;
notFound: boolean;
timeout: boolean;
pos: TileRef;
steps: number;
path: TileRef[];
} {
const status = {
reached: false,
notFound: false,
timeout: false,
pos: from,
steps: 0,
path: [] as TileRef[],
};
for (let i = 0; i < maxIter; i++) {
const result = pathFinder.nextTile(status.pos, to);
if (result.type === PathFindResultType.NextTile) {
status.path.push(result.node);
status.pos = result.node;
status.steps++;
} else if (result.type === PathFindResultType.Completed) {
status.path.push(result.node);
status.reached = true;
return status;
} else if (result.type === PathFindResultType.PathNotFound) {
status.notFound = true;
return status;
}
}
status.timeout = true;
return status;
}

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant