From 860d8ff164ba34ae03cbe6473f1d43ebfea195b3 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Sat, 31 Aug 2024 20:58:52 -0700 Subject: [PATCH] improve remove clusters, kill player if surrounded --- TODO.txt | 5 ++ src/core/execution/AttackExecution.ts | 2 +- src/core/execution/PlayerExecution.ts | 89 +++++++++++++++++---------- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/TODO.txt b/TODO.txt index b3ea2bf8c..8ac3b249d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -67,6 +67,9 @@ * Have terrain affect attack DONE 8/31/2024 * PERF: enable CDN DONE 8/31/2024 * enable load balancing metrics DONE 8/31/2024 +* BUG: island don't check if inscribed, just try to remove it DONE 8/31/2024 +* if completely surrounded by same enemy, lose island DONE 8/31/2024 +* fix boat leaves trail bug * end game when no players left (or after 1 hour or so?) * use better favicon * BUG: tiles get left behind during conquer @@ -78,3 +81,5 @@ * directed expansion * REFACTOR: give terranullius an ID, game.player() returns terranullius * REFACTOR: ocean is considered TerraNullius ? +* BUG: sometimes islands don't get removed + diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 202974adc..a70e5ffb3 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -168,7 +168,7 @@ export class AttackExecution implements Execution { // if (numOwnedByMe > 3) { // numOwnedByMe = 1000 // } - this.toConquer.enqueue(new TileContainer(neighbor, this.random.nextInt(0, 2) - numOwnedByMe + tile.magnitude() / 5)) + this.toConquer.enqueue(new TileContainer(neighbor, this.random.nextInt(0, 4) - numOwnedByMe + tile.magnitude() / 5)) } } this.borderTiles = newBorder diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index 52d86bbb5..1a16bcb6e 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -1,12 +1,11 @@ -import cluster from "cluster" import {Config} from "../configuration/Config" -import {Execution, MutableGame, MutablePlayer, PlayerID, Tile} from "../Game" +import {Execution, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile} from "../Game" import {bfs, calculateBoundingBox, getMode, inscribed, simpleHash} from "../Util" import {GameImpl} from "../GameImpl" export class PlayerExecution implements Execution { - private readonly ticksPerIslandCalc = 50 + private readonly ticksPerClusterCalc = 10 private player: MutablePlayer private config: Config @@ -24,7 +23,7 @@ export class PlayerExecution implements Execution { this.mg = mg this.config = mg.config() this.player = mg.player(this.playerID) - this.lastCalc = ticks + (simpleHash(this.player.name()) % this.ticksPerIslandCalc) + this.lastCalc = ticks + (simpleHash(this.player.name()) % this.ticksPerClusterCalc) } tick(ticks: number) { @@ -33,10 +32,10 @@ export class PlayerExecution implements Execution { } this.player.setTroops(this.config.troopAdditionRate(this.player)) - if (ticks - this.lastCalc > this.ticksPerIslandCalc) { + if (ticks - this.lastCalc > this.ticksPerClusterCalc) { this.lastCalc = ticks const start = performance.now() - this.removeIslands() + this.removeClusters() const end = performance.now() if (end - start > 1000) { console.log(`player ${this.player.name()}, took ${end - start}ms`) @@ -44,35 +43,65 @@ export class PlayerExecution implements Execution { } } - private removeIslands() { + private removeClusters() { const clusters = this.calculateClusters() - if (clusters.length <= 1) { - return - } + // if (clusters.length <= 1) { + // return + // } clusters.sort((a, b) => b.size - a.size); - const main = clusters.shift() - const mainBox = calculateBoundingBox(main) - for (const toRemove of clusters) { - const toRemoveBox = calculateBoundingBox(toRemove) - if (inscribed(mainBox, toRemoveBox)) { - return - } - for (const tile of toRemove) { - if (tile.isOceanShore()) { - return - } + const main = clusters.shift() + if (this.isSurroundedBySamePlayer(main)) { + this.removeCluster(main) + } + + for (const cluster of clusters) { + if (this.isSurrounded(cluster)) { + this.removeCluster(cluster) } - this.removeIsland(toRemove) } } - private removeIsland(cluster: Set) { - console.log('removing island!') + private isSurroundedBySamePlayer(cluster: Set): boolean { + const enemies = new Set() + for (const tile of cluster) { + if (tile.isOceanShore() || tile.neighbors().find(n => !n.hasOwner())) { + return false + } + tile.neighbors() + .filter(n => n.hasOwner() && n.owner() != this.player) + .forEach(p => enemies.add(p.owner() as Player)) + if (enemies.size != 1) { + return false + } + } + return true + } + + private isSurrounded(cluster: Set): boolean { + let enemyTiles = new Set() + for (const tile of cluster) { + if (tile.isOceanShore()) { + return false + } + tile.neighbors() + .filter(n => n.hasOwner() && n.owner() != this.player) + .forEach(n => enemyTiles.add(n)) + } + if (enemyTiles.size == 0) { + return false + } + const enemyBox = calculateBoundingBox(enemyTiles) + const clusterBox = calculateBoundingBox(cluster) + return inscribed(enemyBox, clusterBox) + } + + private removeCluster(cluster: Set) { + console.log('removing cluster!') const arr = Array.from(cluster) const mode = getMode(arr.flatMap(t => t.neighbors()).filter(t => t.hasOwner() && t.owner() != this.player).map(t => t.owner().id())) - if (mode == null) { - console.warn('mode is null') + if (!this.mg.hasPlayer(mode)) { + console.warn('mode is not found') return } const firstTile = arr[0] @@ -108,10 +137,6 @@ export class PlayerExecution implements Execution { const neighbors = (this.mg as GameImpl).neighborsWithDiag(curr) for (const neighbor of neighbors) { - // if (this.mg.ticks() == 736 && loops > 580000) { - // // console.log(`got neighbor ${neighbor.cell().toString()}`) - // gr.paintBlack(neighbor) - // } if (neighbor.isBorder() && border.has(neighbor)) { if (!seen.has(neighbor)) { queue.push(neighbor) @@ -129,9 +154,7 @@ export class PlayerExecution implements Execution { return this.player } - private active = true isActive(): boolean { - // return this.player.isAlive() - return this.active + return this.player.isAlive() } } \ No newline at end of file