rename cansendlliance request fix ally self bug

This commit is contained in:
Evan
2025-02-01 17:03:10 -08:00
parent 0969088dc9
commit af0d6a289a
5 changed files with 83 additions and 86 deletions
+1 -2
View File
@@ -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),
};
+33 -36
View File
@@ -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
);
}
@@ -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);
+5 -8
View File
@@ -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
+42 -33
View File
@@ -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) {