From af0d6a289aac95368a6d0fb7f72267e3bb982199 Mon Sep 17 00:00:00 2001 From: Evan Date: Sat, 1 Feb 2025 17:03:10 -0800 Subject: [PATCH] rename cansendlliance request fix ally self bug --- src/core/GameRunner.ts | 3 +- src/core/execution/FakeHumanExecution.ts | 69 ++++++++--------- .../alliance/AllianceRequestExecution.ts | 9 +-- src/core/game/Game.ts | 13 ++-- src/core/game/PlayerImpl.ts | 75 +++++++++++-------- 5 files changed, 83 insertions(+), 86 deletions(-) diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index c1f9a640d..a26ccc182 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -154,8 +154,7 @@ export class GameRunner { sharedBorder: player.sharesBorderWith(other), canSendEmoji: player.canSendEmoji(other), canTarget: player.canTarget(other), - canSendAllianceRequest: - !player.recentOrPendingAllianceRequestWith(other), + canSendAllianceRequest: player.canSendAllianceRequest(other), canBreakAlliance: player.isAlliedWith(other), canDonate: player.canDonate(other), }; diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index 1c864b962..2ac120274 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -48,10 +48,10 @@ export class FakeHumanExecution implements Execution { gameID: GameID, private playerInfo: PlayerInfo, private cell: Cell, - private strength: number, + private strength: number ) { this.random = new PseudoRandom( - simpleHash(playerInfo.id) + simpleHash(gameID), + simpleHash(playerInfo.id) + simpleHash(gameID) ); } @@ -110,7 +110,7 @@ export class FakeHumanExecution implements Execution { 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(), + (t) => this.mg.isLand(t) && this.mg.ownerID(t) != this.player.smallID() ); if (enemyborder.length == 0) { @@ -125,7 +125,7 @@ export class FakeHumanExecution implements Execution { } const enemiesWithTN = enemyborder.map((t) => - this.mg.playerBySmallID(this.mg.ownerID(t)), + this.mg.playerBySmallID(this.mg.ownerID(t)) ); if (enemiesWithTN.filter((o) => !o.isPlayer()).length > 0) { this.sendAttack(this.mg.terraNullius()); @@ -139,10 +139,7 @@ export class FakeHumanExecution implements Execution { if (this.random.chance(20)) { const toAlly = this.random.randElement(enemies); - if ( - !this.player.isAlliedWith(toAlly) && - !this.player.recentOrPendingAllianceRequestWith(toAlly) - ) { + if (this.player.canSendAllianceRequest(toAlly)) { this.player.createAllianceRequest(toAlly); return; } @@ -208,7 +205,7 @@ export class FakeHumanExecution implements Execution { this.lastEnemyUpdateTick = this.mg.ticks(); if (target.ally.type() == PlayerType.Human) { this.mg.addExecution( - new EmojiExecution(this.player.id(), target.ally.id(), "👍"), + new EmojiExecution(this.player.id(), target.ally.id(), "👍") ); } } @@ -229,8 +226,8 @@ export class FakeHumanExecution implements Execution { new EmojiExecution( this.player.id(), this.enemy.id(), - this.random.randElement(["🤡", "😡"]), - ), + this.random.randElement(["🤡", "😡"]) + ) ); } } @@ -274,7 +271,7 @@ export class FakeHumanExecution implements Execution { } if (this.player.canBuild(UnitType.AtomBomb, tile)) { this.mg.addExecution( - new NukeExecution(UnitType.AtomBomb, this.player.id(), tile), + new NukeExecution(UnitType.AtomBomb, this.player.id(), tile) ); return; } @@ -285,9 +282,9 @@ export class FakeHumanExecution implements Execution { const closest = closestTwoTiles( this.mg, Array.from(this.player.borderTiles()).filter((t) => - this.mg.isOceanShore(t), + this.mg.isOceanShore(t) ), - Array.from(other.borderTiles()).filter((t) => this.mg.isOceanShore(t)), + Array.from(other.borderTiles()).filter((t) => this.mg.isOceanShore(t)) ); if (closest == null) { return; @@ -301,8 +298,8 @@ export class FakeHumanExecution implements Execution { this.player.id(), other.id(), closest.y, - this.player.troops() / 5, - ), + this.player.troops() / 5 + ) ); } } @@ -311,7 +308,7 @@ export class FakeHumanExecution implements Execution { const ports = this.player.units(UnitType.Port); if (ports.length == 0 && this.player.gold() > this.cost(UnitType.Port)) { const oceanTiles = Array.from(this.player.borderTiles()).filter((t) => - this.mg.isOceanShore(t), + this.mg.isOceanShore(t) ); if (oceanTiles.length > 0) { const buildTile = this.random.randElement(oceanTiles); @@ -322,7 +319,7 @@ export class FakeHumanExecution implements Execution { this.maybeSpawnStructure( UnitType.City, 2, - (t) => new CityExecution(this.player.id(), t), + (t) => new CityExecution(this.player.id(), t) ); if (this.maybeSpawnWarship(UnitType.Destroyer)) { return; @@ -333,14 +330,14 @@ export class FakeHumanExecution implements Execution { this.maybeSpawnStructure( UnitType.MissileSilo, 1, - (t) => new MissileSiloExecution(this.player.id(), t), + (t) => new MissileSiloExecution(this.player.id(), t) ); } private maybeSpawnStructure( type: UnitType, maxNum: number, - build: (tile: TileRef) => Execution, + build: (tile: TileRef) => Execution ) { const units = this.player.units(type); if (units.length >= maxNum) { @@ -363,7 +360,7 @@ export class FakeHumanExecution implements Execution { } private maybeSpawnWarship( - shipType: UnitType.Destroyer | UnitType.Battleship, + shipType: UnitType.Destroyer | UnitType.Battleship ): boolean { if (!this.random.chance(50)) { return false; @@ -388,12 +385,12 @@ export class FakeHumanExecution implements Execution { switch (shipType) { case UnitType.Destroyer: this.mg.addExecution( - new DestroyerExecution(this.player.id(), targetTile), + new DestroyerExecution(this.player.id(), targetTile) ); break; case UnitType.Battleship: this.mg.addExecution( - new BattleshipExecution(this.player.id(), targetTile), + new BattleshipExecution(this.player.id(), targetTile) ); break; } @@ -424,11 +421,11 @@ export class FakeHumanExecution implements Execution { for (let attempts = 0; attempts < 50; attempts++) { const randX = this.random.nextInt( this.mg.x(portTile) - radius, - this.mg.x(portTile) + radius, + this.mg.x(portTile) + radius ); const randY = this.random.nextInt( this.mg.y(portTile) - radius, - this.mg.y(portTile) + radius, + this.mg.y(portTile) + radius ); if (!this.mg.isValidCoord(randX, randY)) { continue; @@ -478,8 +475,8 @@ export class FakeHumanExecution implements Execution { new AllianceRequestReplyExecution( req.requestor().id(), this.player.id(), - accept, - ), + accept + ) ); } @@ -490,7 +487,7 @@ export class FakeHumanExecution implements Execution { if (oceanShore == null) { oceanShore = Array.from(this.player.borderTiles()).filter((t) => - this.mg.isOceanShore(t), + this.mg.isOceanShore(t) ); } if (oceanShore.length == 0) { @@ -503,9 +500,9 @@ export class FakeHumanExecution implements Execution { src, andFN( (gm, t) => gm.isOcean(t) || gm.isOceanShore(t), - manhattanDistFN(src, 200), - ), - ), + manhattanDistFN(src, 200) + ) + ) ).filter((t) => this.mg.isOceanShore(t) && this.mg.owner(t) != this.player); if (otherShore.length == 0) { @@ -529,8 +526,8 @@ export class FakeHumanExecution implements Execution { this.player.id(), this.mg.hasOwner(dst) ? this.mg.owner(dst).id() : null, dst, - this.player.troops() / 5, - ), + this.player.troops() / 5 + ) ); return; } @@ -568,8 +565,8 @@ export class FakeHumanExecution implements Execution { this.player.id(), toAttack.isPlayer() ? toAttack.id() : null, null, - null, - ), + null + ) ); } @@ -577,7 +574,7 @@ export class FakeHumanExecution implements Execution { return ( this.mg.bfs( tile, - andFN((gm, t) => gm.isLand(t), manhattanDistFN(tile, 10)), + andFN((gm, t) => gm.isLand(t), manhattanDistFN(tile, 10)) ).size < 50 ); } diff --git a/src/core/execution/alliance/AllianceRequestExecution.ts b/src/core/execution/alliance/AllianceRequestExecution.ts index 0bff20707..9ad100564 100644 --- a/src/core/execution/alliance/AllianceRequestExecution.ts +++ b/src/core/execution/alliance/AllianceRequestExecution.ts @@ -13,10 +13,7 @@ export class AllianceRequestExecution implements Execution { private requestor: Player; private recipient: Player; - constructor( - private requestorID: PlayerID, - private recipientID: PlayerID, - ) {} + constructor(private requestorID: PlayerID, private recipientID: PlayerID) {} init(mg: Game, ticks: number): void { this.mg = mg; @@ -27,9 +24,7 @@ export class AllianceRequestExecution implements Execution { tick(ticks: number): void { if (this.requestor.isAlliedWith(this.recipient)) { consolex.warn("already allied"); - } else if ( - this.requestor.recentOrPendingAllianceRequestWith(this.recipient) - ) { + } else if (!this.requestor.canSendAllianceRequest(this.recipient)) { consolex.warn("recent or pending alliance request"); } else { this.requestor.createAllianceRequest(this.recipient); diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 8c60f0ba1..a99772144 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -85,7 +85,7 @@ export class Nation { constructor( public readonly name: string, public readonly cell: Cell, - public readonly strength: number, + public readonly strength: number ) {} } @@ -94,10 +94,7 @@ export class Cell { private strRepr: string; - constructor( - public readonly x, - public readonly y, - ) { + constructor(public readonly x, public readonly y) { this.strRepr = `Cell[${this.x},${this.y}]`; } @@ -162,7 +159,7 @@ export class PlayerInfo { // null if bot. public readonly clientID: ClientID | null, // TODO: make player id the small id - public readonly id: PlayerID, + public readonly id: PlayerID ) {} } @@ -267,7 +264,7 @@ export interface Player { allies(): Player[]; isAlliedWith(other: Player): boolean; allianceWith(other: Player): MutableAlliance | null; - recentOrPendingAllianceRequestWith(other: Player): boolean; + canSendAllianceRequest(other: Player): boolean; breakAlliance(alliance: Alliance): void; createAllianceRequest(recipient: Player): AllianceRequest; @@ -333,7 +330,7 @@ export interface Game extends GameMap { displayMessage( message: string, type: MessageType, - playerID: PlayerID | null, + playerID: PlayerID | null ): void; // Nations diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 6cfa6e5df..74c4a3c5b 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -44,10 +44,7 @@ interface Target { } class Donation { - constructor( - public readonly recipient: Player, - public readonly tick: Tick, - ) {} + constructor(public readonly recipient: Player, public readonly tick: Tick) {} } export class PlayerImpl implements Player { @@ -82,7 +79,7 @@ export class PlayerImpl implements Player { private mg: GameImpl, private _smallID: number, private readonly playerInfo: PlayerInfo, - startPopulation: number, + startPopulation: number ) { this._name = playerInfo.name; this._targetTroopRatio = 1; @@ -178,7 +175,7 @@ export class PlayerImpl implements Player { const owner = this.mg.map().ownerID(neighbor); if (owner != this.smallID()) { ns.add( - this.mg.playerBySmallID(owner) as PlayerImpl | TerraNulliusImpl, + this.mg.playerBySmallID(owner) as PlayerImpl | TerraNulliusImpl ); } } @@ -224,7 +221,7 @@ export class PlayerImpl implements Player { alliances(): MutableAlliance[] { return this.mg.alliances_.filter( - (a) => a.requestor() == this || a.recipient() == this, + (a) => a.requestor() == this || a.recipient() == this ); } @@ -244,18 +241,26 @@ export class PlayerImpl implements Player { return null; } return this.alliances().find( - (a) => a.recipient() == other || a.requestor() == other, + (a) => a.recipient() == other || a.requestor() == other ); } - recentOrPendingAllianceRequestWith(other: Player): boolean { + canSendAllianceRequest(other: Player): boolean { + if (other == this) { + return false; + } + if (this.isAlliedWith(other)) { + return false; + } + const hasPending = this.incomingAllianceRequests().find((ar) => ar.requestor() == other) != null || this.outgoingAllianceRequests().find((ar) => ar.recipient() == other) != null; + if (hasPending) { - return true; + return false; } const recent = this.pastOutgoingAllianceRequests @@ -263,12 +268,12 @@ export class PlayerImpl implements Player { .sort((a, b) => b.createdAt() - a.createdAt()); if (recent.length == 0) { - return false; + return true; } const delta = this.mg.ticks() - recent[0].createdAt(); - return delta < this.mg.config().allianceRequestCooldown(); + return delta >= this.mg.config().allianceRequestCooldown(); } breakAlliance(alliance: Alliance): void { @@ -362,7 +367,7 @@ export class PlayerImpl implements Player { targets(): PlayerImpl[] { return this.targets_ .filter( - (t) => this.mg.ticks() - t.tick < this.mg.config().targetDuration(), + (t) => this.mg.ticks() - t.tick < this.mg.config().targetDuration() ) .map((t) => t.target as PlayerImpl); } @@ -394,7 +399,7 @@ export class PlayerImpl implements Player { .filter( (e) => this.mg.ticks() - e.createdAt < - this.mg.config().emojiMessageDuration(), + this.mg.config().emojiMessageDuration() ) .sort((a, b) => b.createdAt - a.createdAt); } @@ -403,7 +408,7 @@ export class PlayerImpl implements Player { const recipientID = recipient == AllPlayers ? AllPlayers : recipient.smallID(); const prevMsgs = this.outgoingEmojis_.filter( - (msg) => msg.recipientID == recipientID, + (msg) => msg.recipientID == recipientID ); for (const msg of prevMsgs) { if ( @@ -439,12 +444,12 @@ export class PlayerImpl implements Player { this.mg.displayMessage( `Sent ${renderTroops(troops)} troops to ${recipient.name()}`, MessageType.INFO, - this.id(), + this.id() ); this.mg.displayMessage( `Recieved ${renderTroops(troops)} troops from ${this.name()}`, MessageType.SUCCESS, - recipient.id(), + recipient.id() ); } @@ -459,7 +464,7 @@ export class PlayerImpl implements Player { removeGold(toRemove: Gold): void { if (toRemove > this._gold) { throw Error( - `Player ${this} does not enough gold (${toRemove} vs ${this._gold}))`, + `Player ${this} does not enough gold (${toRemove} vs ${this._gold}))` ); } this._gold -= toRemove; @@ -485,7 +490,7 @@ export class PlayerImpl implements Player { setTargetTroopRatio(target: number): void { if (target < 0 || target > 1) { throw new Error( - `invalid targetTroopRatio ${target} set on player ${PlayerImpl}`, + `invalid targetTroopRatio ${target} set on player ${PlayerImpl}` ); } this._targetTroopRatio = target; @@ -517,7 +522,7 @@ export class PlayerImpl implements Player { } const prev = unit.owner(); (prev as PlayerImpl)._units = (prev as PlayerImpl)._units.filter( - (u) => u != unit, + (u) => u != unit ); (unit as UnitImpl)._owner = this; this._units.push(unit as UnitImpl); @@ -525,12 +530,12 @@ export class PlayerImpl implements Player { this.mg.displayMessage( `${unit.type()} captured by ${this.displayName()}`, MessageType.ERROR, - prev.id(), + prev.id() ); this.mg.displayMessage( `Captured ${unit.type()} from ${prev.displayName()}`, MessageType.SUCCESS, - this.id(), + this.id() ); } @@ -542,7 +547,7 @@ export class PlayerImpl implements Player { spawnTile, troops, this.mg.nextUnitID(), - this, + this ); this._units.push(b); this.removeGold(cost); @@ -597,7 +602,7 @@ export class PlayerImpl implements Player { .filter((t) => this.mg.owner(t) == this && this.mg.isOceanShore(t)) .sort( (a, b) => - this.mg.manhattanDist(a, tile) - this.mg.manhattanDist(b, tile), + this.mg.manhattanDist(a, tile) - this.mg.manhattanDist(b, tile) ); if (spawns.length == 0) { return false; @@ -613,12 +618,12 @@ export class PlayerImpl implements Player { .filter( (u) => this.mg.manhattanDist(u.tile(), tile) < - this.mg.config().boatMaxDistance(), + this.mg.config().boatMaxDistance() ) .sort( (a, b) => this.mg.manhattanDist(a.tile(), tile) - - this.mg.manhattanDist(b.tile(), tile), + this.mg.manhattanDist(b.tile(), tile) ); if (spawns.length == 0) { return false; @@ -646,7 +651,7 @@ export class PlayerImpl implements Player { tradeShipSpawn(targetTile: TileRef): TileRef | false { const spawns = this.units(UnitType.Port).filter( - (u) => u.tile() == targetTile, + (u) => u.tile() == targetTile ); if (spawns.length == 0) { return false; @@ -664,7 +669,11 @@ export class PlayerImpl implements Player { ); } toString(): string { - return `Player:{name:${this.info().name},clientID:${this.info().clientID},isAlive:${this.isAlive()},troops:${this._troops},numTileOwned:${this.numTilesOwned()}}]`; + return `Player:{name:${this.info().name},clientID:${ + this.info().clientID + },isAlive:${this.isAlive()},troops:${ + this._troops + },numTileOwned:${this.numTilesOwned()}}]`; } public playerProfile(): PlayerProfile { @@ -673,7 +682,7 @@ export class PlayerImpl implements Player { this.allRelationsSorted().map(({ player, relation }) => [ player.smallID(), relation, - ]), + ]) ), alliances: this.alliances().map((a) => a.other(this).smallID()), }; @@ -717,8 +726,8 @@ export class PlayerImpl implements Player { tile, andFN( (gm, t) => gm.ownerID(t) == gm.ownerID(tile) && gm.isLand(t), - manhattanDistFN(tile, 25), - ), + manhattanDistFN(tile, 25) + ) )) { if (this.mg.isOceanShore(t)) { nearOcean = true; @@ -759,8 +768,8 @@ export class PlayerImpl implements Player { tile, andFN( (gm, t) => !gm.hasOwner(t) && gm.isLand(t), - manhattanDistFN(tile, 200), - ), + manhattanDistFN(tile, 200) + ) )) { for (const n of this.mg.neighbors(t)) { if (this.mg.owner(n) == this) {