diff --git a/src/core/execution/ConstructionExecution.ts b/src/core/execution/ConstructionExecution.ts index 39deab1ef..49ed2e89f 100644 --- a/src/core/execution/ConstructionExecution.ts +++ b/src/core/execution/ConstructionExecution.ts @@ -104,7 +104,9 @@ export class ConstructionExecution implements Execution { this.mg.addExecution(new MirvExecution(player.id(), this.tile)); break; case UnitType.Warship: - this.mg.addExecution(new WarshipExecution(player.id(), this.tile)); + this.mg.addExecution( + new WarshipExecution({ owner: player, patrolTile: this.tile }), + ); break; case UnitType.Port: this.mg.addExecution(new PortExecution(player.id(), this.tile)); diff --git a/src/core/execution/MoveWarshipExecution.ts b/src/core/execution/MoveWarshipExecution.ts index 1d3290692..87036c3e8 100644 --- a/src/core/execution/MoveWarshipExecution.ts +++ b/src/core/execution/MoveWarshipExecution.ts @@ -24,7 +24,8 @@ export class MoveWarshipExecution implements Execution { console.log("MoveWarshipExecution: warship is already dead"); return; } - warship.setTargetTile(this.position); + warship.setPatrolTile(this.position); + warship.setTargetTile(undefined); this.active = false; } diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index 50854707f..4bf039072 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -46,17 +46,12 @@ export class PlayerExecution implements Execution { throw new Error("Not initialized"); } this.player.decayRelations(); - const hasPort = this.player.units(UnitType.Port).length > 0; this.player.units().forEach((u) => { - if (hasPort && u.type() === UnitType.Warship) { - u.modifyHealth(1); - } - if (this.mg === null) return; - const tileOwner = this.mg.owner(u.tile()); + const tileOwner = this.mg!.owner(u.tile()); if (u.info().territoryBound) { if (tileOwner.isPlayer()) { if (tileOwner !== this.player) { - this.mg.player(tileOwner.id()).captureUnit(u); + this.mg!.player(tileOwner.id()).captureUnit(u); } } else { u.delete(); diff --git a/src/core/execution/TradeShipExecution.ts b/src/core/execution/TradeShipExecution.ts index b31d34d3c..3272d2a2c 100644 --- a/src/core/execution/TradeShipExecution.ts +++ b/src/core/execution/TradeShipExecution.ts @@ -50,7 +50,7 @@ export class TradeShipExecution implements Execution { return; } this.tradeShip = this.origOwner.buildUnit(UnitType.TradeShip, spawn, { - dstPort: this._dstPort, + targetUnit: this._dstPort, lastSetSafeFromPirates: ticks, }); diff --git a/src/core/execution/WarshipExecution.ts b/src/core/execution/WarshipExecution.ts index 3188d9476..f5400c74d 100644 --- a/src/core/execution/WarshipExecution.ts +++ b/src/core/execution/WarshipExecution.ts @@ -2,9 +2,10 @@ import { consolex } from "../Consolex"; import { Execution, Game, - Player, - PlayerID, + isUnit, + OwnerComp, Unit, + UnitParams, UnitType, } from "../game/Game"; import { TileRef } from "../game/GameMap"; @@ -15,178 +16,91 @@ import { ShellExecution } from "./ShellExecution"; export class WarshipExecution implements Execution { private random: PseudoRandom; - - private _owner: Player; - private active = true; - private warship: Unit | null = null; + private warship: Unit; private mg: Game; - - private target: Unit | undefined = undefined; - private pathfinder: PathFinder | null = null; - - private patrolTile: TileRef | undefined; - + private pathfinder: PathFinder; private lastShellAttack = 0; private alreadySentShell = new Set(); constructor( - private playerID: PlayerID, - private patrolCenterTile: TileRef, + private input: (UnitParams & OwnerComp) | Unit, ) {} init(mg: Game, ticks: number): void { this.mg = mg; - if (!mg.hasPlayer(this.playerID)) { - console.log(`WarshipExecution: player ${this.playerID} not found`); - this.active = false; - return; - } this.pathfinder = PathFinder.Mini(mg, 5000); - this._owner = mg.player(this.playerID); - this.patrolTile = this.patrolCenterTile; this.random = new PseudoRandom(mg.ticks()); - } - - // Only for warships with "moveTarget" set - goToMoveTarget(target: TileRef) { - if (this.warship === null || this.pathfinder === null) { - throw new Error("Warship not initialized"); - } - // Patrol unless we are hunting down a tradeship - const result = this.pathfinder.nextTile(this.warship.tile(), target); - switch (result.type) { - case PathFindResultType.Completed: - this.warship.setTargetTile(undefined); - this.warship.touch(); - return; - case PathFindResultType.NextTile: - this.warship.move(result.tile); - break; - case PathFindResultType.Pending: - this.warship.touch(); - break; - case PathFindResultType.PathNotFound: - consolex.log(`path not found to target`); - break; - } - } - - private shoot() { - if ( - this.mg === null || - this.warship === null || - this.target === undefined - ) { - throw new Error("Warship not initialized"); - } - const shellAttackRate = this.mg.config().warshipShellAttackRate(); - if (this.mg.ticks() - this.lastShellAttack > shellAttackRate) { - this.lastShellAttack = this.mg.ticks(); - this.mg.addExecution( - new ShellExecution( - this.warship.tile(), - this.warship.owner(), - this.warship, - this.target, - ), + if (isUnit(this.input)) { + this.warship = this.input; + } else { + const spawn = this.input.owner.canBuild( + UnitType.Warship, + this.input.patrolTile, ); - if (!this.target.hasHealth()) { - // Don't send multiple shells to target that can be oneshotted - this.alreadySentShell.add(this.target); - this.target = undefined; + if (spawn === false) { + console.warn( + `Failed to spawn warship for ${this.input.owner.name()} at ${this.input.patrolTile}`, + ); return; } - } - } - - private patrol() { - if (this.warship === null || this.pathfinder === null) { - throw new Error("Warship not initialized"); - } - if (this.patrolTile === undefined) { - this.patrolTile = this.randomTile(); - if (this.patrolTile === undefined) { - return; - } - } - this.warship.setTargetUnit(this.target); - if ( - this.target === undefined || - this.target.type() !== UnitType.TradeShip - ) { - // Patrol unless we are hunting down a tradeship - const result = this.pathfinder.nextTile( - this.warship.tile(), - this.patrolTile, + this.warship = this.input.owner.buildUnit( + UnitType.Warship, + spawn, + this.input, ); - switch (result.type) { - case PathFindResultType.Completed: - this.patrolTile = undefined; - this.warship.touch(); - break; - case PathFindResultType.NextTile: - this.warship.move(result.tile); - break; - case PathFindResultType.Pending: - this.warship.touch(); - return; - case PathFindResultType.PathNotFound: - consolex.log(`path not found to patrol tile`); - this.patrolTile = undefined; - break; - } } } tick(ticks: number): void { - if (this.pathfinder === null) throw new Error("Warship not initialized"); - if (this.warship === null) { - if (this.patrolTile === undefined) { - console.log( - `WarshipExecution: no patrol tile for ${this._owner.name()}`, - ); - this.active = false; - return; - } - const spawn = this._owner.canBuild(UnitType.Warship, this.patrolTile); - if (spawn === false) { - this.active = false; - return; - } - this.warship = this._owner.buildUnit(UnitType.Warship, spawn, {}); + if (this.warship.health() <= 0) { + this.warship.delete(); return; } - if (!this.warship.isActive()) { - this.active = false; + const hasPort = this.warship.owner().units(UnitType.Port).length > 0; + if (hasPort) { + this.warship.modifyHealth(1); + } + + this.warship.setTargetUnit(this.findTargetUnit()); + if (this.warship.targetUnit()?.type() === UnitType.TradeShip) { + this.huntDownTradeShip(); return; } - if (this.target !== undefined && !this.target.isActive()) { - this.target = undefined; + + this.patrol(); + + if (this.warship.targetUnit() !== undefined) { + this.shootTarget(); + return; } - const hasPort = this._owner.units(UnitType.Port).length > 0; - const warship = this.warship; - if (warship === undefined) throw new Error("Warship not initialized"); + } + + private findTargetUnit(): Unit | undefined { + const hasPort = this.warship.owner().units(UnitType.Port).length > 0; + const patrolRangeSquared = this.mg.config().warshipPatrolRange() ** 2; + const ships = this.mg .nearbyUnits( - this.warship.tile(), + this.warship.patrolTile()!, this.mg.config().warshipTargettingRange(), [UnitType.TransportShip, UnitType.Warship, UnitType.TradeShip], ) .filter( ({ unit }) => - unit.owner() !== warship.owner() && - unit !== warship && - !unit.owner().isFriendly(warship.owner()) && + unit.owner() !== this.warship.owner() && + unit !== this.warship && + !unit.owner().isFriendly(this.warship.owner()) && !this.alreadySentShell.has(unit) && (unit.type() !== UnitType.TradeShip || (hasPort && - this.warship !== null && + this.mg.euclideanDistSquared(this.warship.tile(), unit.tile()) <= + patrolRangeSquared && unit.targetUnit()?.owner() !== this.warship.owner() && !unit.targetUnit()?.owner().isFriendly(this.warship.owner()) && unit.isSafeFromPirates() !== true)), ); - this.target = ships.sort((a, b) => { + return ships.sort((a, b) => { const { unit: unitA, distSquared: distA } = a; const { unit: unitB, distSquared: distB } = b; @@ -217,60 +131,48 @@ export class WarshipExecution implements Execution { // If both are the same type, sort by distance (lower `distSquared` means closer) return distA - distB; })[0]?.unit; + } - const moveTarget = this.warship.targetTile(); - if (moveTarget) { - this.goToMoveTarget(moveTarget); - // If we have a "move target" then we cannot target trade ships as it - // requires moving. - if (this.target && this.target.type() === UnitType.TradeShip) { - this.target = undefined; + private shootTarget() { + const shellAttackRate = this.mg.config().warshipShellAttackRate(); + if (this.mg.ticks() - this.lastShellAttack > shellAttackRate) { + this.lastShellAttack = this.mg.ticks(); + this.mg.addExecution( + new ShellExecution( + this.warship.tile(), + this.warship.owner(), + this.warship, + this.warship.targetUnit()!, + ), + ); + if (!this.warship.targetUnit()!.hasHealth()) { + // Don't send multiple shells to target that can be oneshotted + this.alreadySentShell.add(this.warship.targetUnit()!); + this.warship.setTargetUnit(undefined); + return; } - } else if (!this.target || this.target.type() !== UnitType.TradeShip) { - this.patrol(); - } - - if ( - this.target === undefined || - !this.target.isActive() || - this.target.owner() === this._owner || - this.target.isSafeFromPirates() === true - ) { - // In case another warship captured or destroyed target, or the target escaped into safe waters - this.target = undefined; - return; - } - - this.warship.setTargetUnit(this.target); - - // If we have a move target we do not want to go after trading ships - if (!this.target) { - return; - } - - if (this.target.type() !== UnitType.TradeShip) { - this.shoot(); - return; } + } + private huntDownTradeShip() { for (let i = 0; i < 2; i++) { // target is trade ship so capture it. const result = this.pathfinder.nextTile( this.warship.tile(), - this.target.tile(), + this.warship.targetUnit()!.tile(), 5, ); switch (result.type) { case PathFindResultType.Completed: - this._owner.captureUnit(this.target); - this.target = undefined; + this.warship.owner().captureUnit(this.warship.targetUnit()!); + this.warship.setTargetUnit(undefined); this.warship.move(this.warship.tile()); return; case PathFindResultType.NextTile: this.warship.move(result.tile); break; case PathFindResultType.Pending: - this.warship.move(this.warship.tile()); + this.warship.touch(); break; case PathFindResultType.PathNotFound: consolex.log(`path not found to target`); @@ -279,8 +181,38 @@ export class WarshipExecution implements Execution { } } + private patrol() { + if (this.warship.targetTile() === undefined) { + this.warship.setTargetTile(this.randomTile()); + if (this.warship.targetTile() === undefined) { + return; + } + } + + const result = this.pathfinder.nextTile( + this.warship.tile(), + this.warship.targetTile()!, + ); + switch (result.type) { + case PathFindResultType.Completed: + this.warship.setTargetTile(undefined); + this.warship.move(result.tile); + break; + case PathFindResultType.NextTile: + this.warship.move(result.tile); + break; + case PathFindResultType.Pending: + this.warship.touch(); + return; + case PathFindResultType.PathNotFound: + consolex.warn(`path not found to target tile`); + this.warship.setTargetTile(undefined); + break; + } + } + isActive(): boolean { - return this.active; + return this.warship?.isActive(); } activeDuringSpawnPhase(): boolean { @@ -288,19 +220,16 @@ export class WarshipExecution implements Execution { } randomTile(allowShoreline: boolean = false): TileRef | undefined { - if (this.mg === null) { - throw new Error("Warship not initialized"); - } let warshipPatrolRange = this.mg.config().warshipPatrolRange(); const maxAttemptBeforeExpand: number = 500; let attempts: number = 0; let expandCount: number = 0; while (expandCount < 3) { const x = - this.mg.x(this.patrolCenterTile) + + this.mg.x(this.warship.patrolTile()!) + this.random.nextInt(-warshipPatrolRange / 2, warshipPatrolRange / 2); const y = - this.mg.y(this.patrolCenterTile) + + this.mg.y(this.warship.patrolTile()!) + this.random.nextInt(-warshipPatrolRange / 2, warshipPatrolRange / 2); if (!this.mg.isValidCoord(x, y)) { continue; @@ -322,7 +251,7 @@ export class WarshipExecution implements Execution { return tile; } console.warn( - `Failed to find random tile for warship for ${this._owner.name()}`, + `Failed to find random tile for warship for ${this.warship.owner().name()}`, ); if (!allowShoreline) { // If we failed to find a tile on the ocean, try again but allow shoreline diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index cab325ddc..d82862b21 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -152,13 +152,19 @@ export enum UnitType { Construction = "Construction", } +export interface OwnerComp { + owner: Player; +} + export interface UnitParamsMap { [UnitType.TransportShip]: { troops?: number; destination?: TileRef; }; - [UnitType.Warship]: {}; + [UnitType.Warship]: { + patrolTile: TileRef; + }; [UnitType.Shell]: {}; @@ -175,7 +181,7 @@ export interface UnitParamsMap { }; [UnitType.TradeShip]: { - dstPort: Unit; + targetUnit: Unit; lastSetSafeFromPirates?: number; }; @@ -334,8 +340,12 @@ export class PlayerInfo { } } +export function isUnit(unit: Unit | UnitParams): unit is Unit { + return "isUnit" in unit && typeof unit.isUnit === "function" && unit.isUnit(); +} + export interface Unit { - hash(): number; + isUnit(): this is Unit; // Common properties. id(): number; @@ -349,6 +359,7 @@ export interface Unit { isActive(): boolean; setOwner(owner: Player): void; touch(): void; + hash(): number; toUpdate(): UnitUpdate; // Targeting @@ -387,6 +398,10 @@ export interface Unit { constructionType(): UnitType | null; setConstructionType(type: UnitType): void; + // Warships + setPatrolTile(tile: TileRef): void; + patrolTile(): TileRef | undefined; + // Ports cachePut(from: TileRef, to: TileRef): void; cacheGet(from: TileRef): TileRef | undefined; diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 1e244ae9f..b54618bca 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -286,8 +286,8 @@ export class GameImpl implements Game { this.updates = createGameUpdatesMap(); this.execs.forEach((e) => { if ( - e.isActive() && - (!this.inSpawnPhase() || e.activeDuringSpawnPhase()) + (!this.inSpawnPhase() || e.activeDuringSpawnPhase()) && + e.isActive() ) { e.tick(this._ticks); } diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 9c9f965e6..86ec9680b 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -28,7 +28,7 @@ export class UnitImpl implements Unit { private _troops: number; private _cooldownStartTick: Tick | null = null; private _pathCache: Map = new Map(); - + private _patrolTile: TileRef | undefined; constructor( private _type: UnitType, private mg: GameImpl, @@ -46,6 +46,10 @@ export class UnitImpl implements Unit { "lastSetSafeFromPirates" in params ? (params.lastSetSafeFromPirates ?? 0) : 0; + this._patrolTile = + "patrolTile" in params ? (params.patrolTile ?? undefined) : undefined; + this._targetUnit = + "targetUnit" in params ? (params.targetUnit ?? undefined) : undefined; switch (this._type) { case UnitType.Warship: @@ -57,6 +61,19 @@ export class UnitImpl implements Unit { this.mg.stats().unitBuild(_owner, this._type); } } + + setPatrolTile(tile: TileRef): void { + this._patrolTile = tile; + } + + patrolTile(): TileRef | undefined { + return this._patrolTile; + } + + isUnit(): this is Unit { + return true; + } + touch(): void { this.mg.addUpdate(this.toUpdate()); } diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts index 346e8eb58..bbd8ad48d 100644 --- a/tests/Warship.test.ts +++ b/tests/Warship.test.ts @@ -1,4 +1,5 @@ -import { SpawnExecution } from "../src/core/execution/SpawnExecution"; +import { MoveWarshipExecution } from "../src/core/execution/MoveWarshipExecution"; +import { WarshipExecution } from "../src/core/execution/WarshipExecution"; import { Game, Player, @@ -7,7 +8,7 @@ import { UnitType, } from "../src/core/game/Game"; import { setup } from "./util/Setup"; -import { constructionExecution } from "./util/utils"; +import { executeTicks } from "./util/utils"; const coastX = 7; let game: Game; @@ -16,44 +17,36 @@ 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); - const player_2_info = new PlayerInfo( - "us", - "boat dude", - PlayerType.Human, - null, - "player_2_id", - ); - game.addPlayer(player_2_info); - - game.addExecution( - new SpawnExecution( - game.player(player_1_info.id).info(), - game.ref(coastX, 10), - ), - new SpawnExecution( - game.player(player_2_info.id).info(), - game.ref(coastX, 15), - ), + game = await setup( + "half_land_half_ocean", + { + infiniteGold: true, + instantBuild: true, + }, + [ + new PlayerInfo( + "us", + "boat dude", + PlayerType.Human, + null, + "player_1_id", + ), + new PlayerInfo( + "us", + "boat dude", + PlayerType.Human, + null, + "player_2_id", + ), + ], ); while (game.inSpawnPhase()) { game.executeNextTick(); } - player1 = game.player(player_1_info.id); - player2 = game.player(player_2_info.id); + player1 = game.player("player_1_id"); + player2 = game.player("player_2_id"); }); test("Warship heals only if player has port", async () => { @@ -67,8 +60,11 @@ describe("Warship", () => { const warship = player1.buildUnit( UnitType.Warship, game.ref(coastX + 1, 10), - {}, + { + patrolTile: game.ref(coastX + 1, 10), + }, ); + game.addExecution(new WarshipExecution(warship)); game.executeNextTick(); @@ -85,26 +81,21 @@ describe("Warship", () => { }); test("Warship captures trade if player has port", async () => { - constructionExecution(game, player1.id(), coastX, 10, UnitType.Port); - constructionExecution(game, player1.id(), coastX + 1, 10, UnitType.Warship); - // Warship need one more tick (for warship exec to actually build warship) - game.executeNextTick(); - expect(player1.units(UnitType.Warship)).toHaveLength(1); - expect(player1.units(UnitType.Port)).toHaveLength(1); - - const dstPort = player2.buildUnit( - UnitType.Port, - game.ref(coastX + 2, 10), - {}, + const portTile = game.ref(coastX, 10); + player1.buildUnit(UnitType.Port, portTile, {}); + game.addExecution( + new WarshipExecution( + player1.buildUnit(UnitType.Warship, portTile, { + patrolTile: portTile, + }), + ), ); - // 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, game.ref(coastX + 1, 7), { - dstPort, + targetUnit: player2.buildUnit(UnitType.Port, game.ref(coastX, 10), {}), }, ); @@ -113,32 +104,111 @@ describe("Warship", () => { for (let i = 0; i < 10; i++) { game.executeNextTick(); } - expect(tradeShip.owner().id()).toBe(player1.id()); + expect(tradeShip.owner()).toBe(player1); }); test("Warship do not capture trade if player has no port", async () => { - constructionExecution(game, player1.id(), coastX, 10, UnitType.Port); - constructionExecution(game, player1.id(), coastX + 1, 10, UnitType.Warship); - expect(player1.units(UnitType.Warship)).toHaveLength(1); + game.addExecution( + new WarshipExecution( + player1.buildUnit(UnitType.Warship, game.ref(coastX + 1, 11), { + patrolTile: game.ref(coastX + 1, 11), + }), + ), + ); - const [dstPort] = player1.units(UnitType.Port); - - 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, game.ref(coastX + 1, 11), { - dstPort, + targetUnit: player1.buildUnit(UnitType.Port, game.ref(coastX, 11), {}), }, ); expect(tradeShip.owner().id()).toBe(player2.id()); - // Let plenty of time for A* to execute + // Let plenty of time for warship to potentially capture trade ship for (let i = 0; i < 10; i++) { game.executeNextTick(); } + + expect(tradeShip.owner().id()).toBe(player2.id()); + }); + + test("Warship does not target trade ships that are safe from pirates", async () => { + // build port so warship can target trade ships + player1.buildUnit(UnitType.Port, game.ref(coastX, 10), {}); + + const warship = player1.buildUnit( + UnitType.Warship, + game.ref(coastX + 1, 10), + { + patrolTile: game.ref(coastX + 1, 10), + }, + ); + game.addExecution(new WarshipExecution(warship)); + + const tradeShip = player2.buildUnit( + UnitType.TradeShip, + game.ref(coastX + 1, 10), + { + targetUnit: player2.buildUnit(UnitType.Port, game.ref(coastX, 10), {}), + }, + ); + + tradeShip.setSafeFromPirates(); + + executeTicks(game, 10); + + expect(tradeShip.owner().id()).toBe(player2.id()); + }); + + test("Warship moves to new patrol tile", async () => { + game.config().warshipTargettingRange = () => 1; + + const warship = player1.buildUnit( + UnitType.Warship, + game.ref(coastX + 1, 10), + { + patrolTile: game.ref(coastX + 1, 10), + }, + ); + + game.addExecution(new WarshipExecution(warship)); + + game.addExecution( + new MoveWarshipExecution(warship.id(), game.ref(coastX + 5, 15)), + ); + + executeTicks(game, 10); + + expect(warship.patrolTile()).toBe(game.ref(coastX + 5, 15)); + }); + + test("Warship does not not target trade ships outside of patrol range", async () => { + game.config().warshipTargettingRange = () => 3; + + // build port so warship can target trade ships + player1.buildUnit(UnitType.Port, game.ref(coastX, 10), {}); + + const warship = player1.buildUnit( + UnitType.Warship, + game.ref(coastX + 1, 10), + { + patrolTile: game.ref(coastX + 1, 10), + }, + ); + game.addExecution(new WarshipExecution(warship)); + + const tradeShip = player2.buildUnit( + UnitType.TradeShip, + game.ref(coastX + 1, 15), + { + targetUnit: player2.buildUnit(UnitType.Port, game.ref(coastX, 10), {}), + }, + ); + + executeTicks(game, 10); + + // Trade ship should not be captured expect(tradeShip.owner().id()).toBe(player2.id()); }); }); diff --git a/tests/util/Setup.ts b/tests/util/Setup.ts index 4a8d0fbb3..9f657bffc 100644 --- a/tests/util/Setup.ts +++ b/tests/util/Setup.ts @@ -55,7 +55,6 @@ export async function setup( false, ); - // Create and return the game return createGame(humans, [], gameMap, miniGameMap, config); }