diff --git a/TODO.txt b/TODO.txt index ad8035fae..13b021602 100644 --- a/TODO.txt +++ b/TODO.txt @@ -50,16 +50,20 @@ * store & delay tile updates for lag compensation DONE 8/26/2024 * BUG: error if don't spawn and then click after spawn mode DONE 8/26/2024 * BUG: change player name after join lobby DONE 8/26/2024 -* REFACTOR: use new priority queue -* BUG: players attack each other same time creates islands -* add shader to dim border -* REFACTOR: remove player.info() +* REFACTOR: use new priority queue DONE 8/27/2024 +* BUG: players attack each other same time creates islands DONE 8/28/2024 +* make bot spawn better DONE 8/28/2024 +* make UX for attacking TerraNullius by boat better +* make bots more likely to attack weaker players +* make bot territory less funky (more likely attack neighbors with larger border) + - maybe use bounding box, bots want to be circular? +* PERF: use hierarchical a* search for boats +* precompute spawns +* end game when no players left (or after 1 hour or so?) +* boats can go around the world +* Add terrain elevation to map +* use better favicon * REFACTOR: give terranullius an ID, game.player() returns terranullius * REFACTOR: ocean is considered TerraNullius ? * REFACTOR: remove player config? -* PERF: use hierarchical a* search for boats * PERF: render tiles more efficiently -* Add terrain elevation to map -* boats can go around the world -* make bots more likely to attack weaker players -* use better favicon \ No newline at end of file diff --git a/src/core/GameImpl.ts b/src/core/GameImpl.ts index 1df4629dd..24d24aec0 100644 --- a/src/core/GameImpl.ts +++ b/src/core/GameImpl.ts @@ -287,8 +287,8 @@ export class GameImpl implements MutableGame { } tick() { - this.executions().forEach(e => { - if (!this.inSpawnPhase() || e.activeDuringSpawnPhase()) { + this.execs.forEach(e => { + if (e.isActive() && (!this.inSpawnPhase() || e.activeDuringSpawnPhase())) { e.tick(this._ticks) } }) @@ -349,7 +349,7 @@ export class GameImpl implements MutableGame { } executions(): Execution[] { - return this.execs + return [...this.execs, ...this.unInitExecs] } addExecution(...exec: Execution[]) { @@ -357,7 +357,8 @@ export class GameImpl implements MutableGame { } removeExecution(exec: Execution) { - this.execs.filter(execution => execution !== exec) + this.execs = this.execs.filter(execution => execution !== exec) + this.unInitExecs = this.unInitExecs.filter(execution => execution !== exec) } width(): number { diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts index d00c57b19..7cca37118 100644 --- a/src/core/configuration/DevConfig.ts +++ b/src/core/configuration/DevConfig.ts @@ -3,24 +3,24 @@ import {PlayerConfig} from "./Config"; import {DefaultConfig, DefaultPlayerConfig, defaultPlayerConfig} from "./DefaultConfig"; export const devConfig = new class extends DefaultConfig { - // numSpawnPhaseTurns(): number { - // return 40 - // } - // gameCreationRate(): number { - // return 3 * 1000 - // } - // lobbyLifetime(): number { - // return 3 * 1000 - // } - // turnIntervalMs(): number { - // return 100 - // } - // player(): PlayerConfig { - // return devPlayerConfig - // } - // numBots(): number { - // return 250 - // } + numSpawnPhaseTurns(): number { + return 40 + } + gameCreationRate(): number { + return 3 * 1000 + } + lobbyLifetime(): number { + return 3 * 1000 + } + turnIntervalMs(): number { + return 100 + } + player(): PlayerConfig { + return devPlayerConfig + } + numBots(): number { + return 250 + } } export const devPlayerConfig = new class extends DefaultPlayerConfig { diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 167b10ad4..5c436e41a 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -29,6 +29,10 @@ export class AttackExecution implements Execution { } init(mg: MutableGame, ticks: number) { + if (!this.active) { + return + } + // TODO: remove this and fix directed expansion. this.targetCell = null @@ -40,13 +44,13 @@ export class AttackExecution implements Execution { this.mg = mg for (const exec of mg.executions()) { - if (exec.isActive() && exec instanceof AttackExecution) { + if (exec.isActive() && exec instanceof AttackExecution && exec != this) { const otherAttack = exec as AttackExecution // Target has opposing attack, cancel them out - if (this.target.isPlayer() && otherAttack.target == this._owner && this.target == otherAttack._owner) { + if (this.target.isPlayer() && otherAttack.targetID == this._ownerID && this.targetID == otherAttack._ownerID) { if (otherAttack.troops > this.troops) { otherAttack.troops -= this.troops - otherAttack.calculateToConquer() + // otherAttack.calculateToConquer() this.active = false return } else { @@ -55,7 +59,7 @@ export class AttackExecution implements Execution { } } // Existing attack on same target, add troops - if (otherAttack._owner == this._owner && otherAttack.target == this.target) { + if (otherAttack._owner == this._owner && otherAttack.targetID == this.targetID) { otherAttack.troops += this.troops otherAttack.calculateToConquer() this.active = false diff --git a/src/core/execution/BotSpawner.ts b/src/core/execution/BotSpawner.ts index 4ac409ddf..3a32ff82b 100644 --- a/src/core/execution/BotSpawner.ts +++ b/src/core/execution/BotSpawner.ts @@ -1,11 +1,12 @@ import {Cell, Game} from "../Game"; import {PseudoRandom} from "../PseudoRandom"; import {SpawnIntent} from "../Schemas"; +import {bfs} from "../Util"; import {getSpawnCells} from "./Util"; export class BotSpawner { - private cellToIndex; + private cellToIndex: Map; private freeTiles: Cell[]; private numFreeTiles; private random = new PseudoRandom(123); @@ -39,8 +40,7 @@ export class BotSpawner { spawnBot(botName: string): SpawnIntent { const rand = this.random.nextInt(0, this.numFreeTiles); const spawn = this.freeTiles[rand]; - const spawnCells = getSpawnCells(this.gs, spawn); - spawnCells.forEach(c => this.removeCell(c)); + bfs(this.gs.tile(spawn), 50).forEach(t => this.removeCell(t.cell())) const spawnIntent: SpawnIntent = { type: 'spawn', name: botName, @@ -52,9 +52,14 @@ export class BotSpawner { } private removeCell(cell: Cell) { - const index = this.cellToIndex[cell.toString()]; + if (!this.cellToIndex.has(cell.toString())) { + return + } + const index = this.cellToIndex.get(cell.toString()); + this.cellToIndex.delete(cell.toString()) + this.freeTiles[index] = this.freeTiles[this.numFreeTiles - 1]; - this.cellToIndex[this.freeTiles[index].toString()] = index; + this.cellToIndex.set(this.freeTiles[index].toString(), index); this.numFreeTiles--; } }