From 0bc7c51fb3af3bc2a2c61a2f49c01bcb61c3e4ed Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Sat, 19 Apr 2025 00:00:14 +0200 Subject: [PATCH 1/3] Revised attack parameters --- src/core/configuration/DefaultConfig.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index e3d87ddbe..07b71a325 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -53,7 +53,7 @@ export abstract class DefaultServerConfig implements ServerConfig { abstract env(): GameEnv; abstract discordRedirectURI(): string; turnIntervalMs(): number { - return 100; + return 120; } gameCreationRate(): number { return 60 * 1000; @@ -483,7 +483,7 @@ export class DefaultConfig implements Config { defenderdensity ** 0.6 * within(defender.troops() / attackTroops, 0.3, 10) ** 0.5 * speed, - 8, + 10, 480, ), }; From aeec377befa3df9ec4e08d53ff6134016a4526da Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Sat, 19 Apr 2025 00:00:43 +0200 Subject: [PATCH 2/3] Revised tile targeting --- src/core/execution/AttackExecution.ts | 116 +++++++++++++++----------- 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index ae77969bb..3acdf50b0 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -1,4 +1,3 @@ -import { PriorityQueue } from "@datastructures-js/priority-queue"; import { renderNumber, renderTroops } from "../../client/Utils"; import { Attack, @@ -19,17 +18,8 @@ const malusForRetreat = 25; export class AttackExecution implements Execution { private breakAlliance = false; private active: boolean = true; - private toConquer: PriorityQueue = - new PriorityQueue((a: TileContainer, b: TileContainer) => { - if (a.priority == b.priority) { - if (a.tick == b.tick) { - return 0; - // return this.random.nextInt(-1, 1) - } - return a.tick - b.tick; - } - return a.priority - b.priority; - }); + private toConquer: TileRef[] = []; + private random = new PseudoRandom(123); private _owner: Player; @@ -167,7 +157,7 @@ export class AttackExecution implements Execution { } private refreshToConquer() { - this.toConquer.clear(); + this.toConquer = []; this.border.clear(); for (const tile of this._owner.borderTiles()) { this.addNeighbors(tile); @@ -237,13 +227,73 @@ export class AttackExecution implements Execution { return; } - if (this.toConquer.size() == 0) { + if (this.toConquer.length == 0) { this.refreshToConquer(); this.retreat(); return; } - const tileToConquer = this.toConquer.dequeue().tile; + // Step 1: Separate tiles by number of adjacent owned tiles + const priorityTiles: { tile: TileRef; weight: number }[] = []; + const fallbackTiles: { tile: TileRef; weight: number }[] = []; + + for (const tile of this.toConquer) { + const neighbors = this.mg.neighbors(tile); + const ownedCount = neighbors.filter( + (t) => this.mg.owner(t) === this._owner, + ).length; + + let weight = 1.0; + switch (this.mg.terrainType(tile)) { + case TerrainType.Plains: + weight = 3.0; + break; + case TerrainType.Highland: + weight = 0.5; + break; + case TerrainType.Mountain: + weight = 0.25; + break; + } + + if (ownedCount >= 3) { + priorityTiles.push({ tile, weight }); + } else { + if (ownedCount === 2) { + weight *= 8; // 👈 bonus for 2 owned neighbors + } + fallbackTiles.push({ tile, weight }); + } + } + + // Step 2: Pick from priority group if available, else fallback + const candidates = + priorityTiles.length > 0 ? priorityTiles : fallbackTiles; + + const totalWeight = candidates.reduce((sum, t) => sum + t.weight, 0); + if (totalWeight === 0) { + this.retreat(); + return; + } + + let r = (this.random.nextInt(0, 10000) / 10000) * totalWeight; + let tileToConquer = null; + for (const { tile, weight } of candidates) { + r -= weight; + if (r <= 0) { + tileToConquer = tile; + break; + } + } + + if (!tileToConquer) { + this.retreat(); + return; + } + + // Remove selected tile from the conquer list + this.toConquer = this.toConquer.filter((t) => t !== tileToConquer); + this.border.delete(tileToConquer); const onBorder = @@ -279,33 +329,9 @@ export class AttackExecution implements Execution { continue; } this.border.add(neighbor); - let numOwnedByMe = this.mg - .neighbors(neighbor) - .filter((t) => this.mg.owner(t) == this._owner).length; - const dist = 0; - if (numOwnedByMe > 2) { - numOwnedByMe = 10; + if (!this.toConquer.includes(neighbor)) { + this.toConquer.push(neighbor); } - let mag = 0; - switch (this.mg.terrainType(tile)) { - case TerrainType.Plains: - mag = 1; - break; - case TerrainType.Highland: - mag = Math.random() < 0.4 ? 1 : 1.5; - break; - case TerrainType.Mountain: - mag = Math.random() < 0.4 ? 1 : 2; - break; - } - - this.toConquer.enqueue( - new TileContainer( - neighbor, - dist / 100 + this.random.nextInt(0, 2) - numOwnedByMe + mag, - this.mg.ticks(), - ), - ); } } @@ -351,11 +377,3 @@ export class AttackExecution implements Execution { return this.active; } } - -class TileContainer { - constructor( - public readonly tile: TileRef, - public readonly priority: number, - public readonly tick: number, - ) {} -} From 9988b83b544f58a5dde299640852255ff499b985 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Sat, 19 Apr 2025 00:32:27 +0200 Subject: [PATCH 3/3] fix for empty tile set --- src/core/execution/AttackExecution.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 3acdf50b0..34bfa3e12 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -237,7 +237,17 @@ export class AttackExecution implements Execution { const priorityTiles: { tile: TileRef; weight: number }[] = []; const fallbackTiles: { tile: TileRef; weight: number }[] = []; - for (const tile of this.toConquer) { + const validTiles = this.toConquer.filter((tile) => { + const onBorder = this.mg + .neighbors(tile) + .some((t) => this.mg.owner(t) === this._owner); + return this.mg.owner(tile) === this.target && onBorder; + }); + if (validTiles.length === 0) { + this.retreat(); + return; + } + for (const tile of validTiles) { const neighbors = this.mg.neighbors(tile); const ownedCount = neighbors.filter( (t) => this.mg.owner(t) === this._owner,