Skip to content
Open
Show file tree
Hide file tree
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
20 changes: 18 additions & 2 deletions packages/engine/src/execution/nation/NationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,29 @@ export function isSaveUpWithinReach(
return missing <= incomePerMinute * horizon;
}

type Box = { min: Cell; max: Cell };

// A player's border bounding box, memoised per player. Valid while the
// player's tiles (hence border) are unchanged. Every nation samples the land
// leader each tick for its MIRV estimate, each walking the same whole border.
const borderBoxes = new WeakMap<Player, { tileVersion: number; box: Box }>();

function borderBoundingBox(mg: Game, player: Player): Box {
const tileVersion = player.tileChangeVersion();
const cached = borderBoxes.get(player);
if (cached?.tileVersion === tileVersion) return cached.box;
const box = calculateBoundingBox(mg, player.borderTiles());
borderBoxes.set(player, { tileVersion, box });
return box;
}

export function randTerritoryTileArray(
random: PseudoRandom,
mg: Game,
player: Player,
numTiles: number,
): TileRef[] {
const boundingBox = calculateBoundingBox(mg, player.borderTiles());
const boundingBox = borderBoundingBox(mg, player);
const tiles: TileRef[] = [];
for (let i = 0; i < numTiles; i++) {
const tile = randTerritoryTile(random, mg, player, boundingBox);
Expand All @@ -64,7 +80,7 @@ function randTerritoryTile(
random: PseudoRandom,
mg: Game,
p: Player,
boundingBox: { min: Cell; max: Cell } | null = null,
boundingBox: Box | null = null,
): TileRef | null {
// Prefer sampling inside the bounding box first (fast, usually good enough)
boundingBox ??= calculateBoundingBox(mg, p.borderTiles());
Expand Down
27 changes: 27 additions & 0 deletions tests/NationTerritorySampling.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PlayerInfo, PlayerType } from "@openfront/engine-api/game/GameTypes";
import { PseudoRandom } from "@openfront/engine-lib/PseudoRandom";
import { randTerritoryTileArray } from "@openfront/engine/execution/nation/NationUtils";
import { createGame, L } from "./core/pathfinding/_fixtures";

describe("randTerritoryTileArray", () => {
it("samples the territory as it is now", () => {
const grid = Array(2000).fill(L);
const game = createGame({ width: 100, height: 20, grid });
const info = new PlayerInfo("p", PlayerType.Human, null, "p_id");
const player = game.addPlayer(info);
const conquerColumns = (from: number, to: number) =>
game.map().forEachTile((tile) => {
if (game.x(tile) >= from && game.x(tile) < to) player.conquer(tile);
});
const maxSampledX = () => {
const random = new PseudoRandom(1);
const tiles = randTerritoryTileArray(random, game, player, 40);
return Math.max(...tiles.map((t) => game.x(t)));
};

conquerColumns(0, 10);
expect(maxSampledX()).toBeLessThan(10);
conquerColumns(10, 90);
expect(maxSampledX()).toBeGreaterThanOrEqual(50);
});
});
Loading