mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-21 20:51:15 +00:00
perf: Optimize cluster calculation with DFS and zero-allocation patterns (#2539)
## Description: Replace BFS with DFS and eliminate GC pressure in calculateClusters(): - Switch from O(N) queue.shift() to O(1) stack.pop() operations - Replace Set.has()/Set.add() with Uint8Array bitfield - Add reusable buffer management to avoid repeated allocations - Implement callback-based neighbor iteration to eliminate array allocations - Add forEachNeighborWithDiag() method to Game interface and GameImpl - Remove now unused GameImpl import from PlayerExecution Describe the PR. ## 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 - [x] 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: DISCORD_USERNAME
This commit is contained in:
@@ -672,6 +672,15 @@ export interface Game extends GameMap {
|
||||
map(): GameMap;
|
||||
miniMap(): GameMap;
|
||||
forEachTile(fn: (tile: TileRef) => void): void;
|
||||
// Zero-allocation neighbor iteration (cardinal only) to avoid creating arrays
|
||||
forEachNeighbor(tile: TileRef, callback: (neighbor: TileRef) => void): void;
|
||||
// Zero-allocation neighbor iteration for performance-critical cluster calculation
|
||||
// Alternative to neighborsWithDiag() that returns arrays
|
||||
// Avoids creating intermediate arrays and uses a callback for better performance
|
||||
forEachNeighborWithDiag(
|
||||
tile: TileRef,
|
||||
callback: (neighbor: TileRef) => void,
|
||||
): void;
|
||||
|
||||
// Player Management
|
||||
player(id: PlayerID): Player;
|
||||
|
||||
@@ -517,6 +517,30 @@ export class GameImpl implements Game {
|
||||
return ns;
|
||||
}
|
||||
|
||||
// Zero-allocation neighbor iteration for performance-critical code
|
||||
forEachNeighborWithDiag(
|
||||
tile: TileRef,
|
||||
callback: (neighbor: TileRef) => void,
|
||||
): void {
|
||||
const x = this.x(tile);
|
||||
const y = this.y(tile);
|
||||
for (let dx = -1; dx <= 1; dx++) {
|
||||
for (let dy = -1; dy <= 1; dy++) {
|
||||
if (dx === 0 && dy === 0) continue; // Skip the center tile
|
||||
const newX = x + dx;
|
||||
const newY = y + dy;
|
||||
if (
|
||||
newX >= 0 &&
|
||||
newX < this._width &&
|
||||
newY >= 0 &&
|
||||
newY < this._height
|
||||
) {
|
||||
callback(this._map.ref(newX, newY));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conquer(owner: PlayerImpl, tile: TileRef): void {
|
||||
if (!this.isLand(tile)) {
|
||||
throw Error(`cannot conquer water`);
|
||||
@@ -858,6 +882,15 @@ export class GameImpl implements Game {
|
||||
neighbors(ref: TileRef): TileRef[] {
|
||||
return this._map.neighbors(ref);
|
||||
}
|
||||
// Zero-allocation neighbor iteration (cardinal only)
|
||||
forEachNeighbor(tile: TileRef, callback: (neighbor: TileRef) => void): void {
|
||||
const x = this.x(tile);
|
||||
const y = this.y(tile);
|
||||
if (x > 0) callback(this._map.ref(x - 1, y));
|
||||
if (x + 1 < this._width) callback(this._map.ref(x + 1, y));
|
||||
if (y > 0) callback(this._map.ref(x, y - 1));
|
||||
if (y + 1 < this._height) callback(this._map.ref(x, y + 1));
|
||||
}
|
||||
isWater(ref: TileRef): boolean {
|
||||
return this._map.isWater(ref);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user