nukes now reduce attacking troops and transport ships (same as rest of pop)

Also made NukeExecution build in two separate steps (build then execute)
as for other execution. Otherwise it would instantly explode if I set
high speeds. high speeds in necessary for some tests.
It implied changing a bit some tests. This includes removing the test
that nukes.length == 0 in missile silo which had nothing to do there as
we are just checking cooldown. One test should test only one thing. Here
it was breaking because I changed the NukeExecution which made no sense.
This commit is contained in:
ilan schemoul
2025-03-31 12:29:27 -07:00
committed by Evan
parent f12d14fd22
commit 94dcb67a40
8 changed files with 232 additions and 34 deletions
+114
View File
@@ -0,0 +1,114 @@
import {
Game,
Player,
PlayerInfo,
PlayerType,
UnitType,
} from "../src/core/game/Game";
import { SpawnExecution } from "../src/core/execution/SpawnExecution";
import { setup } from "./util/Setup";
import { constructionExecution } from "./util/utils";
import { TransportShipExecution } from "../src/core/execution/TransportShipExecution";
import { TileRef } from "../src/core/game/GameMap";
import { AttackExecution } from "../src/core/execution/AttackExecution";
import { TestConfig } from "./util/TestConfig";
let game: Game;
let attacker: Player;
let defender: Player;
let defenderSpawn: TileRef;
let attackerSpawn: TileRef;
function sendBoat(target: TileRef, troops: number) {
game.addExecution(
new TransportShipExecution(defender.id(), null, target, troops),
);
}
describe("Attack", () => {
beforeEach(async () => {
game = await setup("ocean_and_land", {
infiniteGold: true,
instantBuild: true,
infiniteTroops: true,
});
const attackerInfo = new PlayerInfo(
"us",
"attacker dude",
PlayerType.Human,
null,
"attacker_id",
);
game.addPlayer(attackerInfo, 1000);
const defenderInfo = new PlayerInfo(
"us",
"defender dude",
PlayerType.Human,
null,
"defender_id",
);
game.addPlayer(defenderInfo, 1000);
defenderSpawn = game.ref(0, 15);
attackerSpawn = game.ref(0, 10);
game.addExecution(
new SpawnExecution(game.player(attackerInfo.id).info(), attackerSpawn),
new SpawnExecution(game.player(defenderInfo.id).info(), defenderSpawn),
);
while (game.inSpawnPhase()) {
game.executeNextTick();
}
attacker = game.player(attackerInfo.id);
defender = game.player(defenderInfo.id);
game.addExecution(new AttackExecution(100, defender.id(), null));
game.executeNextTick();
while (defender.outgoingAttacks().length > 0) {
game.executeNextTick();
}
(game.config() as TestConfig).setDefaultNukeSpeed(50);
});
test("Nuke reduce attacking troop counts", async () => {
// Not building exactly spawn to it's better protected from attacks (but still
// on defender territory)
constructionExecution(game, defender.id(), 1, 1, UnitType.MissileSilo);
expect(defender.units(UnitType.MissileSilo)).toHaveLength(1);
game.addExecution(new AttackExecution(100, attacker.id(), defender.id()));
constructionExecution(game, defender.id(), 0, 15, UnitType.AtomBomb, 3);
const nuke = defender.units(UnitType.AtomBomb)[0];
expect(nuke.isActive()).toBe(true);
expect(attacker.outgoingAttacks()).toHaveLength(1);
expect(attacker.outgoingAttacks()[0].troops()).toBe(98);
// Make the nuke go kaboom
game.executeNextTick();
expect(nuke.isActive()).toBe(false);
expect(attacker.outgoingAttacks()[0].troops()).not.toBe(97);
expect(attacker.outgoingAttacks()[0].troops()).toBeLessThan(90);
});
test("Nuke reduce attacking boat troop count", async () => {
constructionExecution(game, defender.id(), 1, 1, UnitType.MissileSilo);
expect(defender.units(UnitType.MissileSilo)).toHaveLength(1);
sendBoat(game.ref(15, 8), 100);
constructionExecution(game, defender.id(), 0, 15, UnitType.AtomBomb, 3);
const nuke = defender.units(UnitType.AtomBomb)[0];
expect(nuke.isActive()).toBe(true);
const ship = defender.units(UnitType.TransportShip)[0];
expect(ship.troops()).toBe(100);
game.executeNextTick();
expect(nuke.isActive()).toBe(false);
expect(defender.units(UnitType.TransportShip)[0].troops()).toBeLessThan(90);
});
});
+4 -10
View File
@@ -56,12 +56,11 @@ describe("MissileSilo", () => {
test("missilesilo should launch nuke", async () => {
attackerBuildsNuke(null, game.ref(7, 7));
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(1);
//because of initilization the nuke already moved so it should be at 204 when starting from 101
expect(attacker.units(UnitType.AtomBomb)[0].tile()).toBe(
game.map().ref(5, 1),
expect(attacker.units(UnitType.AtomBomb)[0].tile()).not.toBe(
game.map().ref(7, 7),
);
for (let i = 0; i < 3; i++) {
for (let i = 0; i < 5; i++) {
game.executeNextTick();
}
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(0);
@@ -79,12 +78,7 @@ describe("MissileSilo", () => {
attackerBuildsNuke(null, game.ref(50, 50));
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(1);
for (let i = 0; i < 24; i++) {
game.executeNextTick();
}
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(0);
for (let i = 0; i < game.config().SiloCooldown() - 25; i++) {
for (let i = 0; i < game.config().SiloCooldown() - 1; i++) {
game.executeNextTick();
expect(attacker.units(UnitType.MissileSilo)[0].isCooldown()).toBeTruthy();
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

+50 -1
View File
@@ -1,7 +1,17 @@
import { NukeMagnitude } from "../../src/core/configuration/Config";
import { DefaultConfig } from "../../src/core/configuration/DefaultConfig";
import {
Game,
Player,
TerraNullius,
Tick,
UnitType,
} from "../../src/core/game/Game";
import { TileRef } from "../../src/core/game/GameMap";
export class TestConfig extends DefaultConfig {
_proximityBonusPortsNb: number = 0;
private _proximityBonusPortsNb: number = 0;
private _defaultNukeSpeed: number = 4;
samHittingChance(): number {
return 1;
@@ -19,4 +29,43 @@ export class TestConfig extends DefaultConfig {
setProximityBonusPortsNb(nb: number): void {
this._proximityBonusPortsNb = nb;
}
nukeMagnitudes(_: UnitType): NukeMagnitude {
return { inner: 1, outer: 1 };
}
setDefaultNukeSpeed(speed: number): void {
this._defaultNukeSpeed = speed;
}
defaultNukeSpeed(): number {
return this._defaultNukeSpeed;
}
spawnImmunityDuration(): Tick {
return 0;
}
attackLogic(
gm: Game,
attackTroops: number,
attacker: Player,
defender: Player | TerraNullius,
tileToConquer: TileRef,
): {
attackerTroopLoss: number;
defenderTroopLoss: number;
tilesPerTickUsed: number;
} {
return { attackerTroopLoss: 1, defenderTroopLoss: 1, tilesPerTickUsed: 1 };
}
attackTilesPerTick(
attackTroops: number,
attacker: Player,
defender: Player | TerraNullius,
numAdjacentTilesWithEnemy: number,
): number {
return 1;
}
}
+11 -7
View File
@@ -13,14 +13,18 @@ export function constructionExecution(
x: number,
y: number,
unit: UnitType,
ticks = 4,
) {
game.addExecution(new ConstructionExecution(playerID, game.ref(x, y), unit));
// Init exec
game.executeNextTick();
// 4 ticks by default as it usually goes like this
// Init of construction execution
// Exec construction execution
game.executeNextTick();
// Add the execution related to the building
game.executeNextTick();
// First tick of the execution of the constructed structure/unit
game.executeNextTick();
// Tick of construction execution which adds the execution related to the building/unit
// First tick of the execution of the constructed building/unit
// (sometimes step 3 and 4 are merged in one)
for (let i = 0; i < ticks; i++) {
game.executeNextTick();
}
}