From ec21a0f100c896b536bdef2da06d8e23feb507cb Mon Sep 17 00:00:00 2001 From: Scott Anderson <662325+scottanderson@users.noreply.github.com> Date: Mon, 18 Aug 2025 19:04:45 -0400 Subject: [PATCH] Enable the `no-multiple-empty-lines` eslint rule (#1856) ## Description: Enable the `no-multiple-empty-lines` eslint rule. ## 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 --- eslint.config.js | 1 + src/core/execution/FakeHumanExecution.ts | 49 +------------------ src/core/execution/utils/BotBehavior.ts | 60 ++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 51 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index f7c4f927e..ada18ae08 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -113,6 +113,7 @@ export default [ "object-curly-newline": ["error", { multiline: true, consistent: true }], "object-curly-spacing": ["error", "always"], "object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }], + "no-multiple-empty-lines": ["warn", { max: 1, maxEOF: 0 }], "no-undef": "error", "no-unused-vars": "off", // @typescript-eslint/no-unused-vars "quote-props": ["error", "consistent-as-needed"], diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index a550c2b19..1bc782b27 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -173,6 +173,7 @@ export class FakeHumanExecution implements Execution { ); if (enemyborder.length === 0) { + // No more land to expand in to if (this.random.chance(10)) { this.sendBoatRandomly(); } @@ -200,22 +201,12 @@ export class FakeHumanExecution implements Execution { const toAlly = this.random.randElement(enemies); if (this.player.canSendAllianceRequest(toAlly)) { this.player.createAllianceRequest(toAlly); - return; } } - // 50-50 attack weakest player vs random player - const toAttack = this.random.chance(2) - ? enemies[0] - : this.random.randElement(enemies); - if (this.shouldAttack(toAttack)) { - this.behavior.sendAttack(toAttack); - return; - } - this.behavior.forgetOldEnemies(); this.behavior.assistAllies(); - const enemy = this.behavior.selectEnemy(); + const enemy = this.behavior.selectEnemy(enemies); if (!enemy) return; this.maybeSendEmoji(enemy); this.maybeSendNuke(enemy); @@ -226,42 +217,6 @@ export class FakeHumanExecution implements Execution { } } - private shouldAttack(other: Player): boolean { - if (this.player === null) throw new Error("not initialized"); - if (this.player.isOnSameTeam(other)) { - return false; - } - if (this.player.isFriendly(other)) { - if (this.shouldDiscourageAttack(other)) { - return this.random.chance(200); - } - return this.random.chance(50); - } else { - if (this.shouldDiscourageAttack(other)) { - return this.random.chance(4); - } - return true; - } - } - - private shouldDiscourageAttack(other: Player) { - if (other.isTraitor()) { - return false; - } - const difficulty = this.mg.config().gameConfig().difficulty; - if ( - difficulty === Difficulty.Hard || - difficulty === Difficulty.Impossible - ) { - return false; - } - if (other.type() !== PlayerType.Human) { - return false; - } - // Only discourage attacks on Humans who are not traitors on easy or medium difficulty. - return true; - } - private maybeSendEmoji(enemy: Player) { if (this.player === null) throw new Error("not initialized"); if (enemy.type() !== PlayerType.Human) return; diff --git a/src/core/execution/utils/BotBehavior.ts b/src/core/execution/utils/BotBehavior.ts index 4722a6606..dae59b5af 100644 --- a/src/core/execution/utils/BotBehavior.ts +++ b/src/core/execution/utils/BotBehavior.ts @@ -1,5 +1,6 @@ import { AllianceRequest, + Difficulty, Game, Player, PlayerType, @@ -65,11 +66,48 @@ export class BotBehavior { this.game.addExecution(new EmojiExecution(this.player, player.id(), emoji)); } - private setNewEnemy(newEnemy: Player | null) { + private setNewEnemy(newEnemy: Player | null, force = false) { + if (newEnemy !== null && !force && !this.shouldAttack(newEnemy)) return; this.enemy = newEnemy; this.enemyUpdated = this.game.ticks(); } + private shouldAttack(other: Player): boolean { + if (this.player === null) throw new Error("not initialized"); + if (this.player.isOnSameTeam(other)) { + return false; + } + if (this.player.isFriendly(other)) { + if (this.shouldDiscourageAttack(other)) { + return this.random.chance(200); + } + return this.random.chance(50); + } else { + if (this.shouldDiscourageAttack(other)) { + return this.random.chance(4); + } + return true; + } + } + + private shouldDiscourageAttack(other: Player) { + if (other.isTraitor()) { + return false; + } + const difficulty = this.game.config().gameConfig().difficulty; + if ( + difficulty === Difficulty.Hard || + difficulty === Difficulty.Impossible + ) { + return false; + } + if (other.type() !== PlayerType.Human) { + return false; + } + // Only discourage attacks on Humans who are not traitors on easy or medium difficulty. + return true; + } + private clearEnemy() { this.enemy = null; } @@ -98,7 +136,7 @@ export class BotBehavior { largestAttacker = attack.attacker(); } if (largestAttacker !== undefined) { - this.setNewEnemy(largestAttacker); + this.setNewEnemy(largestAttacker, true); } } @@ -134,7 +172,8 @@ export class BotBehavior { } } - selectEnemy(): Player | null { + /** Nation enemy selection logic */ + selectEnemy(enemies: Player[]): Player | null { if (this.enemy === null) { // Save up troops until we reach the trigger ratio if (!this.hasSufficientTroops()) return null; @@ -165,11 +204,13 @@ export class BotBehavior { // Retaliate against incoming attacks if (this.enemy === null) { + // Only after clearing bots this.checkIncomingAttacks(); } // Select the most hated player - if (this.enemy === null) { + if (this.enemy === null && this.random.chance(2)) { + // 50% chance const mostHated = this.player.allRelationsSorted()[0]; if ( mostHated !== undefined && @@ -178,12 +219,23 @@ export class BotBehavior { this.setNewEnemy(mostHated.player); } } + + // Select the weakest player + if (this.enemy === null && enemies.length > 0) { + this.setNewEnemy(enemies[0]); + } + + // Select a random player + if (this.enemy === null) { + this.setNewEnemy(this.random.randElement(enemies)); + } } // Sanity check, don't attack our allies or teammates return this.enemySanityCheck(); } + /** Bot enemy selection logic */ selectRandomEnemy(): Player | TerraNullius | null { if (this.enemy === null) { // Save up troops until we reach the trigger ratio