Fix non-human player never responding to alliance renewal request

This commit is contained in:
evanpelle
2025-07-28 11:29:55 -07:00
parent 1cd4fdb75d
commit 2a3293a388
3 changed files with 90 additions and 30 deletions
+5 -1
View File
@@ -105,7 +105,11 @@ export class Executor {
case "build_unit":
return new ConstructionExecution(player, intent.unit, intent.tile);
case "allianceExtension": {
return new AllianceExtensionExecution(player, intent.recipient);
return new AllianceExtensionExecution(
this.gameID,
player,
intent.recipient,
);
}
case "upgrade_structure":
@@ -4,13 +4,22 @@ import {
MessageType,
Player,
PlayerID,
PlayerType,
} from "../../game/Game";
import { PseudoRandom } from "../../PseudoRandom";
import { GameID } from "../../Schemas";
import { simpleHash } from "../../Util";
export class AllianceExtensionExecution implements Execution {
private random: PseudoRandom;
constructor(
gameID: GameID,
private readonly from: Player,
private readonly toID: PlayerID,
) {}
) {
this.random = new PseudoRandom(simpleHash(toID) + simpleHash(gameID));
}
init(mg: Game, ticks: number): void {
if (!mg.hasPlayer(this.toID)) {
@@ -36,27 +45,26 @@ export class AllianceExtensionExecution implements Execution {
return;
}
// Mark this player's intent to extend
alliance.addExtensionRequest(this.from);
if (alliance.canExtend()) {
alliance.extend();
mg.displayMessage(
"events_display.alliance_renewed",
MessageType.ALLIANCE_ACCEPTED,
this.from.id(),
undefined,
{ name: to.displayName() },
);
mg.displayMessage(
"events_display.alliance_renewed",
MessageType.ALLIANCE_ACCEPTED,
this.toID,
undefined,
{ name: this.from.displayName() },
);
if (to.type() !== PlayerType.Human) {
if (!this.random.chance(1.3)) return;
} else {
// Mark this player's intent to extend
alliance.addExtensionRequest(this.from);
if (!alliance.canExtend()) return;
}
alliance.extend();
mg.displayMessage(
"events_display.alliance_renewed",
MessageType.ALLIANCE_ACCEPTED,
this.from.id(),
);
mg.displayMessage(
"events_display.alliance_renewed",
MessageType.ALLIANCE_ACCEPTED,
this.toID,
);
}
tick(ticks: number): void {
+56 -8
View File
@@ -7,6 +7,8 @@ import { playerInfo, setup } from "./util/Setup";
let game: Game;
let player1: Player;
let player2: Player;
let player3: Player;
const gameID = "1b3xq";
describe("AllianceExtensionExecution", () => {
beforeEach(async () => {
@@ -20,19 +22,23 @@ describe("AllianceExtensionExecution", () => {
[
playerInfo("player1", PlayerType.Human),
playerInfo("player2", PlayerType.Human),
playerInfo("player3", PlayerType.FakeHuman),
],
);
player1 = game.player("player1");
player2 = game.player("player2");
player3 = game.player("player3");
while (game.inSpawnPhase()) {
game.executeNextTick();
}
});
test("Successfully extends existing alliance", () => {
test("Successfully extends existing alliance between Humans", () => {
jest.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
jest.spyOn(player2, "isAlive").mockReturnValue(true);
jest.spyOn(player1, "isAlive").mockReturnValue(true);
game.addExecution(new AllianceRequestExecution(player1, player2.id()));
game.executeNextTick();
game.executeNextTick();
@@ -47,27 +53,69 @@ describe("AllianceExtensionExecution", () => {
expect(player2.allianceWith(player1)).toBeTruthy();
const allianceBefore = player1.allianceWith(player2)!;
const expirationBefore =
allianceBefore.createdAt() + game.config().allianceDuration();
const allianceSpy = jest.spyOn(allianceBefore, "extend");
const expirationBefore = allianceBefore.expiresAt();
game.addExecution(new AllianceExtensionExecution(player1, player2.id()));
game.addExecution(
new AllianceExtensionExecution(gameID, player1, player2.id()),
);
game.executeNextTick();
expect(allianceSpy).toHaveBeenCalledTimes(0); // both players must agree to extend
game.addExecution(
new AllianceExtensionExecution(gameID, player2, player1.id()),
);
game.executeNextTick();
const allianceAfter = player1.allianceWith(player2)!;
expect(allianceAfter.id()).toBe(allianceBefore.id());
const expirationAfter =
allianceAfter.createdAt() + game.config().allianceDuration();
const expirationAfter = allianceAfter.expiresAt();
expect(expirationAfter).toBeGreaterThanOrEqual(expirationBefore);
expect(expirationAfter).toBeGreaterThan(expirationBefore);
expect(allianceSpy).toHaveBeenCalledTimes(1);
});
test("Fails gracefully if no alliance exists", () => {
game.addExecution(new AllianceExtensionExecution(player1, player2.id()));
game.addExecution(
new AllianceExtensionExecution(gameID, player1, player2.id()),
);
game.executeNextTick();
expect(player1.allianceWith(player2)).toBeFalsy();
expect(player2.allianceWith(player1)).toBeFalsy();
});
test("Successfully extends existing alliance between Human and non-Human", () => {
jest.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
jest.spyOn(player3, "isAlive").mockReturnValue(true);
jest.spyOn(player1, "isAlive").mockReturnValue(true);
game.addExecution(new AllianceRequestExecution(player1, player3.id()));
game.executeNextTick();
game.executeNextTick();
game.addExecution(
new AllianceRequestReplyExecution(player1.id(), player3, true),
);
game.executeNextTick();
game.executeNextTick();
expect(player1.allianceWith(player3)).toBeTruthy();
expect(player3.allianceWith(player1)).toBeTruthy();
const allianceBefore = player1.allianceWith(player3)!;
const expirationBefore = allianceBefore.expiresAt();
const exec = new AllianceExtensionExecution(gameID, player1, player3.id());
jest.spyOn(exec["random"], "chance").mockReturnValue(true);
game.addExecution(exec);
game.executeNextTick();
const allianceAfter = player1.allianceWith(player3)!;
expect(allianceAfter.id()).toBe(allianceBefore.id());
const expirationAfter = allianceBefore.expiresAt();
expect(expirationAfter).toBeGreaterThan(expirationBefore);
});
});