From 365d87a6f1a2df9bc105079b14bd298d9cd8c192 Mon Sep 17 00:00:00 2001 From: MrDev-lang Date: Wed, 6 May 2026 18:04:09 -0500 Subject: [PATCH 1/2] Update NationStructureBehavior.ts Added a leaky bucket algorithm using stress meter to hopefully make the defence posts more consistent. --- .../nation/NationStructureBehavior.ts | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/core/execution/nation/NationStructureBehavior.ts b/src/core/execution/nation/NationStructureBehavior.ts index 03f3db17de..0743a05185 100644 --- a/src/core/execution/nation/NationStructureBehavior.ts +++ b/src/core/execution/nation/NationStructureBehavior.ts @@ -134,11 +134,25 @@ export class NationStructureBehavior { private _hasHighStartingGold: boolean | null = null; private _postSaveUpStartTick: number | null = null; + private attackStressMeter = 0; + private lastAttackCheckTick = 0; constructor( private random: PseudoRandom, private game: Game, private player: Player, ) {} + private isThreatenedByAttack(ratio: number): boolean { + const currentTick = this.game.ticks(); + const elapsed = this.lastAttackCheckTick === 0 ? 0 : currentTick - this.lastAttackCheckTick; + this.lastAttackCheckTick = currentTick; + + // Cooldown by 1x elapsed. If attacked (>5%), heat up by 3x elapsed (net +2x per tick) + this.attackStressMeter = Math.max(0, this.attackStressMeter - elapsed); + if (ratio > 0.05) this.attackStressMeter += elapsed * 3; + + // 200 stress = approx 10 seconds of sustained fighting + return ratio >= UNDER_ATTACK_THREAT_RATIO || (this.attackStressMeter > 200 && ratio > 0); + } handleStructures(): boolean { // Defense posts are handled outside the normal pacing/counter system: @@ -195,15 +209,15 @@ export class NationStructureBehavior { const incomingTroops = landAttacks.reduce((sum, a) => sum + a.troops(), 0); const ratio = incomingTroops / ourTroops; - if (ratio < UNDER_ATTACK_THREAT_RATIO) return false; + + if (!this.isThreatenedByAttack(ratio)) return false; let allowed: number; if (difficulty === Difficulty.Medium) { allowed = 1; } else { - allowed = Math.ceil(ratio / DEFENSE_POST_RATIO_PER_POST); + allowed = Math.max(1, Math.ceil(ratio / DEFENSE_POST_RATIO_PER_POST)); } - const frontTiles = this.getAttackFrontTiles(landAttacks); if (this.countDefensePostsNearFront(frontTiles, allowed) >= allowed) return false; @@ -236,7 +250,8 @@ export class NationStructureBehavior { const ourTroops = this.player.troops(); if (ourTroops <= 0) return false; const incomingTroops = landAttacks.reduce((sum, a) => sum + a.troops(), 0); - return incomingTroops / ourTroops >= UNDER_ATTACK_THREAT_RATIO; + const ratio = incomingTroops / ourTroops; + return this.isThreatenedByAttack(ratio); } /** From f5f834468302713234668f1497e3c751c1ec3d44 Mon Sep 17 00:00:00 2001 From: MrDev-lang Date: Fri, 8 May 2026 17:59:43 -0500 Subject: [PATCH 2/2] Update NationStructureBehavior.ts Fix code rabbit issues, transfered over from decimals to whole numbers. --- .../execution/nation/NationStructureBehavior.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/core/execution/nation/NationStructureBehavior.ts b/src/core/execution/nation/NationStructureBehavior.ts index 0743a05185..9bdff5256c 100644 --- a/src/core/execution/nation/NationStructureBehavior.ts +++ b/src/core/execution/nation/NationStructureBehavior.ts @@ -143,15 +143,20 @@ export class NationStructureBehavior { ) {} private isThreatenedByAttack(ratio: number): boolean { const currentTick = this.game.ticks(); - const elapsed = this.lastAttackCheckTick === 0 ? 0 : currentTick - this.lastAttackCheckTick; + // Cap elapsed at 100 ticks (10s) to prevent stress spikes after long peace + const elapsed = this.lastAttackCheckTick === 0 ? 0 : Math.min(100, currentTick - this.lastAttackCheckTick); this.lastAttackCheckTick = currentTick; - // Cooldown by 1x elapsed. If attacked (>5%), heat up by 3x elapsed (net +2x per tick) + // Convert to Basis Points (whole numbers) for multiplayer safety (desync prevention) + const ratioBps = Math.floor(ratio * 10000); + const thresholdBps = Math.floor(UNDER_ATTACK_THREAT_RATIO * 10000); + + // Cooldown by 1x. If attacked (>5%), heat up by 3x this.attackStressMeter = Math.max(0, this.attackStressMeter - elapsed); - if (ratio > 0.05) this.attackStressMeter += elapsed * 3; + if (ratioBps > 500) this.attackStressMeter += elapsed * 3; - // 200 stress = approx 10 seconds of sustained fighting - return ratio >= UNDER_ATTACK_THREAT_RATIO || (this.attackStressMeter > 200 && ratio > 0); + // Trigger if over 35% threshold OR sustained stress over 200 (approx 10s) + return ratioBps >= thresholdBps || (this.attackStressMeter > 200 && ratioBps > 0); } handleStructures(): boolean {