From 40b7fe983329f07923ed9752f9f52f577a22086b Mon Sep 17 00:00:00 2001 From: Ilan Schemoul Date: Wed, 19 Mar 2025 04:35:31 +0100 Subject: [PATCH] feat: add warship tests (#291) --- src/scripts/TerrainMapGenerator.ts | 7 +- tests/Warship.test.ts | 109 ++++++++++++++++++++++++ tests/testdata/half_land_half_ocean.png | Bin 0 -> 96 bytes tests/util/Setup.ts | 6 +- tests/util/utils.ts | 23 +++++ 5 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 tests/Warship.test.ts create mode 100755 tests/testdata/half_land_half_ocean.png create mode 100644 tests/util/utils.ts diff --git a/src/scripts/TerrainMapGenerator.ts b/src/scripts/TerrainMapGenerator.ts index 2827d96dc..53f50f5dd 100644 --- a/src/scripts/TerrainMapGenerator.ts +++ b/src/scripts/TerrainMapGenerator.ts @@ -25,6 +25,7 @@ class Terrain { export async function generateMap( imageBuffer: Buffer, + removeSmall = true, ): Promise<{ map: Uint8Array; miniMap: Uint8Array }> { const stream = Readable.from(imageBuffer); const img = await decodePNGFromStream(stream); @@ -56,8 +57,10 @@ export async function generateMap( } } - removeSmallIslands(terrain); - removeSmallLakes(terrain); + if (removeSmall) { + removeSmallIslands(terrain); + removeSmallLakes(terrain); + } const shorelineWaters = processShore(terrain); processDistToLand(shorelineWaters, terrain); processOcean(terrain); diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts new file mode 100644 index 000000000..aa6d23b6e --- /dev/null +++ b/tests/Warship.test.ts @@ -0,0 +1,109 @@ +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"; + +let game: Game; +let player1: Player; +let player2: Player; + +describe("Warship", () => { + beforeEach(async () => { + game = await setup("half_land_half_ocean", { + infiniteGold: true, + instantBuild: true, + }); + const player_1_info = new PlayerInfo( + "us", + "boat dude", + PlayerType.Human, + null, + "player_1_id", + ); + game.addPlayer(player_1_info, 1000); + const player_2_info = new PlayerInfo( + "us", + "boat dude", + PlayerType.Human, + null, + "player_2_id", + ); + game.addPlayer(player_2_info, 1000); + + const spawnTile = game.map().ref(0, 0); + game.addExecution( + new SpawnExecution(game.player(player_1_info.id).info(), spawnTile), + new SpawnExecution(game.player(player_2_info.id).info(), spawnTile), + ); + + while (game.inSpawnPhase()) { + game.executeNextTick(); + } + + player1 = game.player(player_1_info.id); + player2 = game.player(player_2_info.id); + }); + + test("Warship heals only if player has port", async () => { + const maxHealth = game.config().unitInfo(UnitType.Warship).maxHealth; + + const port = player1.buildUnit(UnitType.Port, 0, game.ref(0, 0)); + const warship = player1.buildUnit(UnitType.Warship, 0, game.ref(7, 7)); + + game.executeNextTick(); + + expect(warship.health()).toBe(maxHealth); + warship.modifyHealth(-10); + expect(warship.health()).toBe(maxHealth - 10); + game.executeNextTick(); + expect(warship.health()).toBe(maxHealth - 9); + + port.delete(); + + game.executeNextTick(); + expect(warship.health()).toBe(maxHealth - 9); + }); + + test("Warship captures trade if player has port", async () => { + constructionExecution(game, player1.id(), 0, 0, UnitType.Port); + constructionExecution(game, player1.id(), 7, 7, UnitType.Warship); + // Warship need one more tick (for warship exec to actually build warship) + game.executeNextTick(); + expect(player1.units(UnitType.Warship)).toHaveLength(1); + + // Cannot buildExec with trade ship as it's not buildable (but + // we can obviously directly add it to the player) + const tradeShip = player2.buildUnit(UnitType.TradeShip, 0, game.ref(6, 6)); + + expect(tradeShip.owner().id()).toBe(player2.id()); + // Let plenty of time for A* to execute + for (let i = 0; i < 10; i++) { + game.executeNextTick(); + } + expect(tradeShip.owner().id()).toBe(player1.id()); + }); + + test("Warship do not capture trade if player has no port", async () => { + constructionExecution(game, player1.id(), 0, 0, UnitType.Port); + constructionExecution(game, player1.id(), 7, 7, UnitType.Warship); + expect(player1.units(UnitType.Warship)).toHaveLength(1); + + player1.units(UnitType.Port)[0].delete(); + // Cannot buildExec with trade ship as it's not buildable (but + // we can obviously directly add it to the player) + const tradeShip = player2.buildUnit(UnitType.TradeShip, 0, game.ref(6, 6)); + + expect(tradeShip.owner().id()).toBe(player2.id()); + // Let plenty of time for A* to execute + for (let i = 0; i < 10; i++) { + game.executeNextTick(); + } + expect(tradeShip.owner().id()).toBe(player2.id()); + }); +}); diff --git a/tests/testdata/half_land_half_ocean.png b/tests/testdata/half_land_half_ocean.png new file mode 100755 index 0000000000000000000000000000000000000000..059596dfa08e46fbd24289dd21041a8a9133993a GIT binary patch literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|Bt2amLo|Yu rOMadIFrSh0O>#