From 41deef9e965ea96efdc2673c0ba07a1f404079cb Mon Sep 17 00:00:00 2001 From: FloPinguin <25036848+FloPinguin@users.noreply.github.com> Date: Thu, 27 Nov 2025 04:42:50 +0100 Subject: [PATCH] =?UTF-8?q?Nations=20no=20longer=20send=20random=20boats?= =?UTF-8?q?=20to=20their=20bordering=20enemies=20=F0=9F=9A=A2=20(#2526)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: Nations no longer send random boats to their bordering enemies. Usually it looked a bit stupid when nations did that ("Why don't you attack your bordering enemy directly?") ## 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: FloPinguin --- src/core/execution/FakeHumanExecution.ts | 35 +++++++++++++----------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index 4051339fc..10805d4b1 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -206,36 +206,35 @@ export class FakeHumanExecution implements Execution { if (this.player === null || this.behavior === null) { throw new Error("not initialized"); } + const enemyborder = Array.from(this.player.borderTiles()) .flatMap((t) => this.mg.neighbors(t)) .filter( (t) => this.mg.isLand(t) && this.mg.ownerID(t) !== this.player?.smallID(), ); + const borderPlayers = enemyborder.map((t) => + this.mg.playerBySmallID(this.mg.ownerID(t)), + ); + const borderingEnemies = borderPlayers + .filter((o) => o.isPlayer()) + .sort((a, b) => a.troops() - b.troops()); - let borderingEnemies: Player[] = []; if (enemyborder.length === 0) { if (this.random.chance(5)) { - this.sendBoatRandomly(); + this.sendBoatRandomly(borderingEnemies); } } else { if (this.random.chance(10)) { - this.sendBoatRandomly(); + this.sendBoatRandomly(borderingEnemies); return; } - const borderPlayers = enemyborder.map((t) => - this.mg.playerBySmallID(this.mg.ownerID(t)), - ); if (borderPlayers.some((o) => !o.isPlayer())) { this.behavior.sendAttack(this.mg.terraNullius()); return; } - borderingEnemies = borderPlayers - .filter((o) => o.isPlayer()) - .sort((a, b) => a.troops() - b.troops()); - // 5% chance to send a random alliance request if (this.random.chance(20)) { const toAlly = this.random.randElement(borderingEnemies); @@ -598,7 +597,7 @@ export class FakeHumanExecution implements Execution { return this.mg.unitInfo(type).cost(this.player); } - sendBoatRandomly() { + sendBoatRandomly(borderingEnemies: Player[]) { if (this.player === null) throw new Error("not initialized"); const oceanShore = Array.from(this.player.borderTiles()).filter((t) => this.mg.isOceanShore(t), @@ -610,10 +609,10 @@ export class FakeHumanExecution implements Execution { const src = this.random.randElement(oceanShore); // First look for high-interest targets (unowned or bot-owned). Mainly relevant for earlygame - let dst = this.randomBoatTarget(src, 150, true); + let dst = this.randomBoatTarget(src, borderingEnemies, true); if (dst === null) { // None found? Then look for players - dst = this.randomBoatTarget(src, 150, false); + dst = this.randomBoatTarget(src, borderingEnemies, false); if (dst === null) { return; } @@ -658,15 +657,15 @@ export class FakeHumanExecution implements Execution { private randomBoatTarget( tile: TileRef, - dist: number, + borderingEnemies: Player[], highInterestOnly: boolean = false, ): TileRef | null { if (this.player === null) throw new Error("not initialized"); const x = this.mg.x(tile); const y = this.mg.y(tile); for (let i = 0; i < 500; i++) { - const randX = this.random.nextInt(x - dist, x + dist); - const randY = this.random.nextInt(y - dist, y + dist); + const randX = this.random.nextInt(x - 150, x + 150); + const randY = this.random.nextInt(y - 150, y + 150); if (!this.mg.isValidCoord(randX, randY)) { continue; } @@ -678,6 +677,10 @@ export class FakeHumanExecution implements Execution { if (owner === this.player) { continue; } + // Don't send boats to players with which we share a border, that usually looks stupid + if (owner.isPlayer() && borderingEnemies.includes(owner)) { + continue; + } // Don't spam boats into players that are more than twice as large as us if (owner.isPlayer() && owner.troops() > this.player.troops() * 2) { continue;