From 10cd4fc63f3dd0839bd284f07cfc49d7e70ab348 Mon Sep 17 00:00:00 2001 From: TKTK123456 <103334266+TKTK123456@users.noreply.github.com> Date: Thu, 25 Jun 2026 00:39:33 -0400 Subject: [PATCH] Fixed rail network path length limit and readded tests for it (#4406) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > **Before opening a PR:** discuss new features on [Discord](https://discord.gg/K9zernJB5z) first, and file bugs or small improvements as [issues](https://github.com/openfrontio/OpenFrontIO/issues/new/choose). You must be assigned to an `approved` issue — unsolicited PRs will be auto-closed. **Add approved & assigned issue number here:** Resolves #4396 ## Description: Makes the max rail length 1.4142 * the max station radius to be minimum amount outside of the factory effect radius. Bug: image Fixed: image ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory ## Please put your Discord username so you can be contacted if a bug or regression is found: tktk1234567 --- src/core/configuration/Config.ts | 3 ++ src/core/game/RailNetworkImpl.ts | 35 ++++++++++++---------- tests/core/game/RailNetwork.test.ts | 45 +++++++++++++++-------------- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 68b3ef916..c86586306 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -243,6 +243,9 @@ export class Config { trainStationMaxRange(): number { return 110; } + railroadMaxSize(): number { + return this.trainStationMaxRange() * 1.4142; + } tradeShipGold(dist: number, player: Player | PlayerView): Gold { // Sigmoid: concave start, sharp S-curve middle, linear end - heavily punishes trades under range debuff. diff --git a/src/core/game/RailNetworkImpl.ts b/src/core/game/RailNetworkImpl.ts index e347c8f18..5b94bc45d 100644 --- a/src/core/game/RailNetworkImpl.ts +++ b/src/core/game/RailNetworkImpl.ts @@ -251,6 +251,7 @@ export class RailNetworkImpl implements RailNetwork { const maxRange = this.game.config().trainStationMaxRange(); const minRangeSquared = this.game.config().trainStationMinRange() ** 2; + const maxPathSize = this.game.config().railroadMaxSize(); // A City or Port only joins the rail network when a Factory is already in // range (see CityExecution/PortExecution). A Factory always becomes a @@ -300,11 +301,13 @@ export class RailNetworkImpl implements RailNetwork { } else { continue; } + const path = this.pathService.findTilePath(tile, targetTile); - if (path.length === 0) continue; - paths.push(path); - if (neighborStation) { - connectedStations.push(neighborStation); + if (path.length > 0 && path.length < maxPathSize) { + paths.push(path); + if (neighborStation) { + connectedStations.push(neighborStation); + } } } @@ -375,17 +378,19 @@ export class RailNetworkImpl implements RailNetwork { private connect(from: TrainStation, to: TrainStation) { const path = this.pathService.findTilePath(from.tile(), to.tile()); - if (path.length === 0) return false; - const railroad = new Railroad(from, to, path, this.nextId++); - this.game.addUpdate({ - type: GameUpdateType.RailroadConstructionEvent, - id: railroad.id, - tiles: railroad.tiles, - }); - from.addRailroad(railroad); - to.addRailroad(railroad); - this.railGrid.register(railroad); - return true; + if (path.length > 0 && path.length < this.game.config().railroadMaxSize()) { + const railroad = new Railroad(from, to, path, this.nextId++); + this.game.addUpdate({ + type: GameUpdateType.RailroadConstructionEvent, + id: railroad.id, + tiles: railroad.tiles, + }); + from.addRailroad(railroad); + to.addRailroad(railroad); + this.railGrid.register(railroad); + return true; + } + return false; } private distanceFrom( diff --git a/tests/core/game/RailNetwork.test.ts b/tests/core/game/RailNetwork.test.ts index c99d746d7..c11a5b5a9 100644 --- a/tests/core/game/RailNetwork.test.ts +++ b/tests/core/game/RailNetwork.test.ts @@ -71,6 +71,7 @@ describe("RailNetworkImpl", () => { config: () => ({ trainStationMaxRange: () => 80, trainStationMinRange: () => 10, + railroadMaxSize: () => 100, }), x: vi.fn(() => 0), y: vi.fn(() => 0), @@ -249,11 +250,7 @@ describe("RailNetworkImpl", () => { pathService.findTilePath.mockReturnValue(mockPath); game.nearbyUnits.mockReturnValue([ - { - unit: neighborStation.unit, - distSquared: 400, - euclideanDist: Math.sqrt(400), - }, + { unit: neighborStation.unit, distSquared: 400 }, ]); const result = network.computeGhostRailPaths(UnitType.City, tile); @@ -272,11 +269,7 @@ describe("RailNetworkImpl", () => { // distSquared = 50 <= minRange^2 (10^2 = 100) game.nearbyUnits.mockReturnValue([ - { - unit: neighborStation.unit, - distSquared: 50, - euclideanDist: Math.sqrt(50), - }, + { unit: neighborStation.unit, distSquared: 50 }, ]); const result = network.computeGhostRailPaths(UnitType.City, tile); @@ -290,8 +283,26 @@ describe("RailNetworkImpl", () => { stationManager.findStation.mockReturnValue(null); + game.nearbyUnits.mockReturnValue([{ unit: { id: 1 }, distSquared: 400 }]); + + const result = network.computeGhostRailPaths(UnitType.City, tile); + expect(result).toEqual([]); + }); + + test("skips paths that exceed max railroad size", () => { + const tile = 42 as any; + const railGridMock = { query: vi.fn(() => new Set()) }; + (network as any).railGrid = railGridMock; + + const neighborStation = createMockStation(1); + neighborStation.tile.mockReturnValue(100); + stationManager.findStation.mockReturnValue(neighborStation); + + // Path length >= railroadMaxSize (100) + pathService.findTilePath.mockReturnValue(new Array(100)); + game.nearbyUnits.mockReturnValue([ - { unit: { id: 1 }, distSquared: 400, euclideanDist: Math.sqrt(400) }, + { unit: neighborStation.unit, distSquared: 400 }, ]); const result = network.computeGhostRailPaths(UnitType.City, tile); @@ -303,19 +314,11 @@ describe("RailNetworkImpl", () => { const railGridMock = { query: vi.fn(() => new Set()) }; (network as any).railGrid = railGridMock; - const neighbors: Array<{ - unit: any; - distSquared: number; - euclideanDist: number; - }> = []; + const neighbors: Array<{ unit: any; distSquared: number }> = []; for (let i = 0; i < 7; i++) { const station = createMockStation(i); station.tile.mockReturnValue(100 + i); - neighbors.push({ - unit: station.unit, - distSquared: 400 + i, - euclideanDist: Math.sqrt(400 + i), - }); + neighbors.push({ unit: station.unit, distSquared: 400 + i }); } stationManager.findStation.mockImplementation((unit: any) => {