Cache maxTroops during leaderboard update (#4379)

**Add approved & assigned issue number here:**

Resolves #4316

## Description:

At the beginning of the leaderboard update cycle, evaluates `maxTroops`
once for each `PlayerView` and uses the cached value for the rest of the
`maxTroops` lookups in the function.

Measured a reduction in `updateLeaderboard` processing time from 6ms/sec
down to 2ms/sec (measured over the first minute of a singleplayer game
on world map and default settings).

## 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

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

Demonessica
This commit is contained in:
Demonessica
2026-06-23 17:19:40 -07:00
committed by GitHub
parent 06cc01668b
commit d01be405c7
+18 -10
View File
@@ -71,38 +71,46 @@ export class Leaderboard extends LitElement implements Controller {
if (this.game === null) throw new Error("Not initialized");
const myPlayer = this.game.myPlayer();
let sorted = this.game.playerViews();
interface PlayerViewTroopsCache {
pv: PlayerView;
maxTroops: number;
}
const compare = (a: number, b: number) =>
this._sortOrder === "asc" ? a - b : b - a;
const maxTroops = (p: PlayerView) => this.game!.config().maxTroops(p);
const sorted: PlayerViewTroopsCache[] = this.game
.playerViews()
.map((p) => ({ pv: p, maxTroops: maxTroops(p) }));
switch (this._sortKey) {
case "gold":
sorted = sorted.sort((a, b) =>
compare(Number(a.gold()), Number(b.gold())),
sorted.sort((a, b) =>
compare(Number(a.pv.gold()), Number(b.pv.gold())),
);
break;
case "maxtroops":
sorted = sorted.sort((a, b) => compare(maxTroops(a), maxTroops(b)));
sorted.sort((a, b) => compare(a.maxTroops, b.maxTroops));
break;
default:
sorted = sorted.sort((a, b) =>
compare(a.numTilesOwned(), b.numTilesOwned()),
sorted.sort((a, b) =>
compare(a.pv.numTilesOwned(), b.pv.numTilesOwned()),
);
}
const numTilesWithoutFallout =
this.game.numLandTiles() - this.game.numTilesWithFallout();
const alivePlayers = sorted.filter((player) => player.isAlive());
const alivePlayers = sorted.filter((player) => player.pv.isAlive());
const playersToShow = this.showTopFive
? alivePlayers.slice(0, 5)
: alivePlayers;
this.players = playersToShow.map((player, index) => {
const maxTroops = this.game!.config().maxTroops(player);
this.players = playersToShow.map((playerCache, index) => {
const player = playerCache.pv;
const maxTroops = playerCache.maxTroops;
return {
name: player.displayName(),
position: index + 1,
@@ -126,7 +134,7 @@ export class Leaderboard extends LitElement implements Controller {
let place = 0;
for (const p of sorted) {
place++;
if (p === myPlayer) {
if (p.pv === myPlayer) {
break;
}
}