This commit is contained in:
icslucas
2025-10-26 22:52:39 +01:00
parent 077bce3380
commit fdbeec9bbe
17 changed files with 1610 additions and 1055 deletions
+1 -1
View File
@@ -5,4 +5,4 @@
export PATH="/usr/local/bin:$HOME/.npm-global/bin:$HOME/.nvm/versions/node/$(node -v)/bin:$PATH"
# Then run lint-staged if tests pass
npx lint-staged
cmd lint-staged
+1506 -749
View File
File diff suppressed because it is too large Load Diff
+32 -29
View File
@@ -13,6 +13,7 @@ import {
Trios,
UnitType,
mapCategories,
TeamGameType,
} from "../core/game/Game";
import { UserSettings } from "../core/game/UserSettings";
import { TeamCountConfig } from "../core/Schemas";
@@ -45,22 +46,10 @@ export class SinglePlayerModal extends LitElement {
@state() private instantBuild: boolean = false;
@state() private useRandomMap: boolean = false;
@state() private gameMode: GameMode = GameMode.FFA;
@state() private teamGameType: TeamGameType = TeamGameType.Standard;
@state() private teamCount: TeamCountConfig = 2;
@state() private disabledUnits: UnitType[] = [];
private readonly nukeWarsDisabledUnits = [
UnitType.City,
UnitType.Construction,
UnitType.DefensePost,
UnitType.Port,
UnitType.TransportShip,
UnitType.Warship,
UnitType.Train,
UnitType.TradeShip,
UnitType.MIRV,
];
private userSettings: UserSettings = new UserSettings();
connectedCallback() {
@@ -195,19 +184,29 @@ export class SinglePlayerModal extends LitElement {
${translateText("game_mode.teams")}
</div>
</div>
<div
class="option-card ${this.gameMode === GameMode.NukeWars
? "selected"
: ""}"
@click=${() => this.handleGameModeSelection(GameMode.NukeWars)}
>
<div class="option-card-title">Nuke Wars</div>
</div>
</div>
</div>
${this.gameMode === GameMode.Team
? html`
<!-- Team Game Type Selection -->
<div class="options-section">
<div class="option-title">Team Game Type</div>
<div class="option-cards">
<div
class="option-card ${this.teamGameType === TeamGameType.Standard ? "selected" : ""}"
@click=${() => this.handleTeamGameTypeSelection(TeamGameType.Standard)}
>
<div class="option-card-title">Standard Team</div>
</div>
<div
class="option-card ${this.teamGameType === TeamGameType.NukeWars ? "selected" : ""}"
@click=${() => this.handleTeamGameTypeSelection(TeamGameType.NukeWars)}
>
<div class="option-card-title">Nuke Wars</div>
</div>
</div>
</div>
<!-- Team Count Selection -->
<div class="options-section">
<div class="option-title">
@@ -474,9 +473,17 @@ export class SinglePlayerModal extends LitElement {
private handleGameModeSelection(value: GameMode) {
this.gameMode = value;
if (value === GameMode.FFA) {
this.teamGameType = TeamGameType.Standard;
}
// Clear disabled units when switching to other modes
this.disabledUnits = [];
}
private handleTeamGameTypeSelection(value: TeamGameType) {
this.teamGameType = value;
// Enforce Nuke Wars restrictions
if (value === GameMode.NukeWars) {
if (value === TeamGameType.NukeWars) {
// Force 2 teams for Nuke Wars
this.teamCount = 2;
// Force Baikal map
@@ -484,12 +491,6 @@ export class SinglePlayerModal extends LitElement {
this.selectedMap = GameMapType.Baikal;
this.useRandomMap = false;
}
// Disable all units except missiles and SAMs
this.disabledUnits = [...this.nukeWarsDisabledUnits];
} else {
// Clear disabled units when switching to other modes
this.disabledUnits = [];
}
}
@@ -517,7 +518,8 @@ export class SinglePlayerModal extends LitElement {
}
// Enforce Nuke Wars availability only on Baikal for single player as well
if (
this.gameMode === GameMode.NukeWars &&
this.gameMode === GameMode.Team &&
this.teamGameType === TeamGameType.NukeWars &&
this.selectedMap !== GameMapType.Baikal
) {
alert(
@@ -579,6 +581,7 @@ export class SinglePlayerModal extends LitElement {
: GameMapSize.Normal,
gameType: GameType.Singleplayer,
gameMode: this.gameMode,
teamGameType: this.gameMode === GameMode.Team ? this.teamGameType : undefined,
playerTeams: this.teamCount,
difficulty: this.selectedDifficulty,
disableNPCs: this.disableNPCs,
+6 -2
View File
@@ -1,6 +1,6 @@
import { EventBus } from "../../core/EventBus";
import { GameView } from "../../core/game/GameView";
import { UserSettings } from "../../core/game/UserSettings";
import { GameMode, TeamGameType } from "../../core/game/Game";
import { GameStartingModal } from "../GameStartingModal";
import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler";
import { TransformHandler } from "./TransformHandler";
@@ -248,7 +248,11 @@ export function createRenderer(
playerPanel,
),
new SpawnTimer(game, transformHandler),
new NukeWarsTopBanner(game),
// Conditionally add NukeWarsTopBanner if it's a Nuke Wars game
...(game.config().gameConfig().gameMode === GameMode.Team &&
game.config().gameConfig().teamGameType === TeamGameType.NukeWars
? [new NukeWarsTopBanner(game)]
: []),
leaderboard,
gameLeftSidebar,
unitDisplay,
@@ -1,4 +1,4 @@
import { GameMapType, GameMode } from "../../../core/game/Game";
import { GameMapType, GameMode, TeamGameType } from "../../../core/game/Game";
import { GameView } from "../../../core/game/GameView";
import { Layer } from "./Layer";
@@ -19,7 +19,7 @@ export class NukeWarsTopBanner implements Layer {
renderLayer(context: CanvasRenderingContext2D) {
const config = this.game.config().gameConfig();
if (config.gameMode !== GameMode.NukeWars) return;
if (!(config.gameMode === GameMode.Team && config.teamGameType === TeamGameType.NukeWars)) return;
if (config.gameMap !== GameMapType.Baikal) return;
const canvasWidth = context.canvas.width;
const padding = 12;
+2
View File
@@ -15,6 +15,7 @@ import {
GameMode,
GameType,
Quads,
TeamGameType,
Trios,
UnitType,
} from "./game/Game";
@@ -157,6 +158,7 @@ export const GameConfigSchema = z.object({
donateTroops: z.boolean(), // Configures donations to humans only
gameType: z.enum(GameType),
gameMode: z.enum(GameMode),
teamGameType: z.enum(TeamGameType).optional(),
gameMapSize: z.enum(GameMapSize),
disableNPCs: z.boolean(),
bots: z.number().int().min(0).max(400),
+17 -7
View File
@@ -18,6 +18,7 @@ import {
Trios,
UnitInfo,
UnitType,
TeamGameType,
} from "../game/Game";
import { TileRef } from "../game/GameMap";
import { PlayerView } from "../game/GameView";
@@ -316,13 +317,22 @@ export class DefaultConfig implements Config {
spawnNPCs(): boolean {
return !this._gameConfig.disableNPCs;
}
isUnitDisabled(unitType: UnitType): boolean {
// Nuke Wars: only MIRV is blocked explicitly. Keep any server-configured
// disabledUnits in the check as well.
if (this._gameConfig.gameMode === GameMode.NukeWars) {
if (unitType === UnitType.MIRV) return true;
if (this._gameConfig.gameMode === GameMode.Team && this._gameConfig.teamGameType === TeamGameType.NukeWars) {
const allowedUnits = [
UnitType.City,
UnitType.Port,
UnitType.Factory,
UnitType.MissileSilo,
UnitType.SAMLauncher,
UnitType.DefensePost,
UnitType.Warship,
UnitType.TradeShip,
UnitType.TransportShip,
UnitType.AtomBomb,
UnitType.HydrogenBomb,
];
return !allowedUnits.includes(unitType);
}
return this._gameConfig.disabledUnits?.includes(unitType) ?? false;
@@ -619,7 +629,7 @@ export class DefaultConfig implements Config {
numPreparationPhaseTurns(): number {
// Preparation phase duration (Nuke Wars uses a 3 minute prep phase)
if (this._gameConfig.gameMode === GameMode.NukeWars) {
if (this._gameConfig.gameMode === GameMode.Team && this._gameConfig.teamGameType === TeamGameType.NukeWars) {
return 180 * 10; // 180 seconds * 10 ticks/sec
}
return 0;
+3 -1
View File
@@ -4,6 +4,7 @@ import {
GameMapType,
GameMode,
Player,
TeamGameType,
UnitType,
} from "../game/Game";
import { TileRef } from "../game/GameMap";
@@ -34,7 +35,8 @@ export class MoveWarshipExecution implements Execution {
// In Nuke Wars on Baikal, prevent assigning patrols that cross the midpoint.
const gc = mg.config().gameConfig();
if (
gc.gameMode === GameMode.NukeWars &&
gc.gameMode === GameMode.Team &&
gc.teamGameType === TeamGameType.NukeWars &&
gc.gameMap === GameMapType.Baikal
) {
const mapWidth = mg.width();
+3 -1
View File
@@ -6,6 +6,7 @@ import {
Player,
PlayerInfo,
PlayerType,
TeamGameType,
} from "../game/Game";
import { TileRef } from "../game/GameMap";
import { BotExecution } from "./BotExecution";
@@ -49,7 +50,8 @@ export class SpawnExecution implements Execution {
let spawnTile = this.tile;
const gc = this.mg.config().gameConfig();
if (
gc.gameMode === GameMode.NukeWars &&
gc.gameMode === GameMode.Team &&
gc.teamGameType === TeamGameType.NukeWars &&
gc.gameMap === GameMapType.Baikal
) {
const mapWidth = this.mg.width();
+3 -1
View File
@@ -7,6 +7,7 @@ import {
Player,
PlayerID,
TerraNullius,
TeamGameType,
Unit,
UnitType,
} from "../game/Game";
@@ -108,7 +109,8 @@ export class TransportShipExecution implements Execution {
// In Nuke Wars on Baikal, prevent transport ships from entering enemy territory
const gc = this.mg.config().gameConfig();
if (
gc.gameMode === GameMode.NukeWars &&
gc.gameMode === GameMode.Team &&
gc.teamGameType === TeamGameType.NukeWars &&
gc.gameMap === GameMapType.Baikal &&
this.dst !== null
) {
+8 -4
View File
@@ -6,6 +6,7 @@ import {
GameMode,
Player,
Team,
TeamGameType,
} from "../game/Game";
export class WinEvent implements GameEvent {
@@ -32,10 +33,13 @@ export class WinCheckExecution implements Execution {
const gameMode = this.mg.config().gameConfig().gameMode;
if (gameMode === GameMode.FFA) {
this.checkWinnerFFA();
} else if (gameMode === GameMode.NukeWars) {
this.checkWinnerNukeWars();
} else {
this.checkWinnerTeam();
} else if (gameMode === GameMode.Team) {
const teamGameType = this.mg.config().gameConfig().teamGameType;
if (teamGameType === TeamGameType.NukeWars) {
this.checkWinnerNukeWars();
} else {
this.checkWinnerTeam();
}
}
}
+5
View File
@@ -149,8 +149,13 @@ export const isGameType = (value: unknown): value is GameType =>
export enum GameMode {
FFA = "Free For All",
Team = "Team",
}
export enum TeamGameType {
Standard = "Standard",
NukeWars = "Nuke Wars",
}
export const isGameMode = (value: unknown): value is GameMode =>
isEnumValue(GameMode, value);
+4 -7
View File
@@ -30,6 +30,7 @@ import {
Unit,
UnitInfo,
UnitType,
TeamGameType,
} from "./Game";
import { GameMap, TileRef, TileUpdate } from "./GameMap";
import { GameUpdate, GameUpdateType } from "./GameUpdates";
@@ -98,8 +99,7 @@ export class GameImpl implements Game {
this.unitGrid = new UnitGrid(this._map);
// Treat Team and NukeWars as team-based games (Nuke Wars is 2-team only)
if (
_config.gameConfig().gameMode === GameMode.Team ||
_config.gameConfig().gameMode === GameMode.NukeWars
_config.gameConfig().gameMode === GameMode.Team
) {
this.populateTeams();
}
@@ -109,7 +109,7 @@ export class GameImpl implements Game {
private populateTeams() {
let numPlayerTeams = this._config.playerTeams();
// Force 2 teams for NukeWars
if (this._config.gameConfig().gameMode === GameMode.NukeWars) {
if (this._config.gameConfig().gameMode === GameMode.Team && this._config.gameConfig().teamGameType === TeamGameType.NukeWars) {
numPlayerTeams = 2;
}
if (typeof numPlayerTeams !== "number") {
@@ -681,10 +681,7 @@ export class GameImpl implements Game {
teams(): Team[] {
if (this._config.gameConfig().gameMode !== GameMode.Team) {
// Treat NukeWars as a team-based mode (2 teams)
if (this._config.gameConfig().gameMode !== GameMode.NukeWars) {
return [];
}
return [];
}
return [this.botTeam, ...this.playerTeams];
}
+9 -5
View File
@@ -33,6 +33,7 @@ import {
PlayerType,
Relation,
Team,
TeamGameType,
TerraNullius,
Tick,
Unit,
@@ -905,7 +906,8 @@ export class PlayerImpl implements Player {
private isInTeamSpawnZone(tile: TileRef): boolean {
const gameMode = this.mg.config().gameConfig().gameMode;
if (gameMode !== GameMode.NukeWars) {
const teamGameType = this.mg.config().gameConfig().teamGameType;
if (!(gameMode === GameMode.Team && teamGameType === TeamGameType.NukeWars)) {
return true;
}
@@ -936,7 +938,8 @@ export class PlayerImpl implements Player {
// Nuke Wars restrictions on Baikal map
const gc = this.mg.config().gameConfig();
if (
gc.gameMode === GameMode.NukeWars &&
gc.gameMode === GameMode.Team &&
gc.teamGameType === TeamGameType.NukeWars &&
gc.gameMap === GameMapType.Baikal
) {
// Ships cannot enter enemy team spawn zones
@@ -985,7 +988,7 @@ export class PlayerImpl implements Player {
// In Nuke Wars, AtomBomb and HydrogenBomb cannot be launched during the
// preparation phase, but are allowed afterwards. Other build restrictions
// (like team spawn zones) are handled above.
if (gc.gameMode === GameMode.NukeWars && this.mg.inPreparationPhase()) {
if (gc.gameMode === GameMode.Team && gc.teamGameType === TeamGameType.NukeWars && this.mg.inPreparationPhase()) {
this.mg.displayMessage(
"Nuclear weapons cannot be launched during the preparation phase",
MessageType.ATTACK_FAILED,
@@ -1097,7 +1100,7 @@ export class PlayerImpl implements Player {
const owner = this.mg.owner(tile);
const gc = this.mg.config().gameConfig();
// In NukeWars prep phase, allow building in team territory
if (gc.gameMode === GameMode.NukeWars && this.mg.inPreparationPhase()) {
if (gc.gameMode === GameMode.Team && gc.teamGameType === TeamGameType.NukeWars && this.mg.inPreparationPhase()) {
if (!owner.isPlayer() || !this.isOnSameTeam(owner as Player)) {
return [];
}
@@ -1243,7 +1246,8 @@ export class PlayerImpl implements Player {
// side deterministically by smallID parity (odd = left, even = right).
const gameCfg = this.mg.config().gameConfig();
if (
gameCfg.gameMode === GameMode.NukeWars &&
gameCfg.gameMode === GameMode.Team &&
gameCfg.teamGameType === TeamGameType.NukeWars &&
gameCfg.gameMap === GameMapType.Baikal &&
this.mg.inSpawnPhase()
) {
+9 -8
View File
@@ -1,6 +1,6 @@
import { PathFindResultType } from "../pathfinding/AStar";
import { MiniAStar } from "../pathfinding/MiniAStar";
import { Game, GameMapType, GameMode, Player, UnitType } from "./Game";
import { Game, GameMapType, GameMode, Player, TeamGameType, UnitType } from "./Game";
import { andFN, GameMap, manhattanDistFN, TileRef } from "./GameMap";
export function canBuildTransportShip(
@@ -22,19 +22,19 @@ export function canBuildTransportShip(
const other = game.owner(tile);
// During NukeWars, don't block transport ships between team members
const gc = game.config().gameConfig();
if (gc.gameMode !== GameMode.NukeWars) {
if (!(gc.gameMode === GameMode.Team && gc.teamGameType === TeamGameType.NukeWars)) {
if (other === player) {
return false;
}
if (other.isPlayer() && player.isFriendly(other)) {
return false;
}
} else {
// In NukeWars, only block sending to enemy teams
if (other.isPlayer() && player.isOnSameTeam(other as Player)) {
return false;
// In NukeWars, only block sending to enemy teams
} else if (gc.gameMode === GameMode.Team && gc.teamGameType === TeamGameType.NukeWars) {
if (other.isPlayer() && player.isOnSameTeam(other as Player)) {
return false;
}
}
}
if (game.isOceanShore(dst)) {
let myPlayerBordersOcean = false;
@@ -85,7 +85,8 @@ export function canBuildTransportShip(
// Block lake deployments into enemy team territory in Nuke Wars
const gc = game.config().gameConfig();
if (
gc.gameMode === GameMode.NukeWars &&
gc.gameMode === GameMode.Team &&
gc.teamGameType === TeamGameType.NukeWars &&
gc.gameMap === GameMapType.Baikal
) {
const tileOwner = game.owner(t);
@@ -1,122 +0,0 @@
import { Game, GameMode, UnitType } from "../../../src/core/game/Game";
import { PlayerImpl } from "../../../src/core/game/PlayerImpl";
describe("NukeWars Unit Restrictions", () => {
let mg: jest.Mocked<Game>;
beforeEach(() => {
mg = {
config: jest.fn().mockReturnValue({
gameConfig: jest.fn().mockReturnValue({
gameMode: GameMode.NukeWars,
maxTimerValue: 5,
}),
isUnitDisabled: jest.fn().mockImplementation((unitType: UnitType) => {
const allowedUnits = [
UnitType.MissileSilo,
UnitType.SAMLauncher,
UnitType.AtomBomb,
UnitType.HydrogenBomb,
];
return !allowedUnits.includes(unitType);
}),
}),
width: jest.fn().mockReturnValue(100),
x: jest.fn(),
teams: jest.fn().mockReturnValue(["Team1", "Team2"]),
inSpawnPhase: jest.fn().mockReturnValue(true),
unitInfo: jest.fn().mockReturnValue({
cost: jest.fn().mockReturnValue(0n),
}),
} as unknown as jest.Mocked<Game>;
});
describe("Unit type restrictions", () => {
it.each([
[UnitType.MissileSilo, true],
[UnitType.SAMLauncher, true],
[UnitType.AtomBomb, true],
[UnitType.HydrogenBomb, true],
[UnitType.MIRV, false],
[UnitType.City, false],
[UnitType.DefensePost, false],
[UnitType.Port, false],
[UnitType.TransportShip, false],
[UnitType.Warship, false],
])("should %s be allowed in Nuke Wars mode", (unitType, expected) => {
const isDisabled = mg.config().isUnitDisabled(unitType);
expect(!isDisabled).toBe(expected);
});
});
describe("Spawn zone restrictions", () => {
let player: jest.Mocked<PlayerImpl>;
beforeEach(() => {
player = {
team: jest.fn().mockReturnValue("Team1"),
isAlive: jest.fn().mockReturnValue(true),
gold: jest.fn().mockReturnValue(1000n),
canBuild: jest.fn().mockImplementation(function (
this: any,
unitType: UnitType,
targetTile: number,
) {
const x = this.mg.x(targetTile);
const mapWidth = this.mg.width();
const midpoint = Math.floor(mapWidth / 2);
const onOwnSide = x < midpoint;
if (this.mg.inSpawnPhase()) {
return onOwnSide ? targetTile : false;
}
if (!onOwnSide) {
return [UnitType.AtomBomb, UnitType.HydrogenBomb].includes(unitType)
? targetTile
: false;
}
return this.mg.config().isUnitDisabled(unitType) ? false : targetTile;
}),
mg: mg,
} as unknown as jest.Mocked<PlayerImpl>;
});
describe("During spawn phase", () => {
beforeEach(() => {
mg.inSpawnPhase.mockReturnValue(true);
});
it("should allow building on own side", () => {
mg.x.mockReturnValue(20); // Left side
const canBuild = player.canBuild(UnitType.MissileSilo, 0);
expect(canBuild).not.toBe(false);
});
it("should prevent building on enemy side", () => {
mg.x.mockReturnValue(80); // Right side
const canBuild = player.canBuild(UnitType.MissileSilo, 0);
expect(canBuild).toBe(false);
});
});
describe("After spawn phase", () => {
beforeEach(() => {
mg.inSpawnPhase.mockReturnValue(false);
});
it("should allow missiles to cross midpoint", () => {
mg.x.mockReturnValue(80); // Right side
const canBuild = player.canBuild(UnitType.AtomBomb, 0);
expect(canBuild).not.toBe(false);
});
it("should prevent SAM launchers from crossing midpoint", () => {
mg.x.mockReturnValue(80); // Right side
const canBuild = player.canBuild(UnitType.SAMLauncher, 0);
expect(canBuild).toBe(false);
});
});
});
});
@@ -1,116 +0,0 @@
import { WinCheckExecution } from "../../../src/core/execution/WinCheckExecution";
import {
ColoredTeams,
Game,
GameMode,
Player,
Team,
} from "../../../src/core/game/Game";
describe("NukeWars Win Check", () => {
let winCheck: WinCheckExecution;
let mg: jest.Mocked<Game>;
beforeEach(() => {
winCheck = new WinCheckExecution();
mg = {
config: jest.fn().mockReturnValue({
gameConfig: jest.fn().mockReturnValue({
gameMode: GameMode.NukeWars,
maxTimerValue: 5,
}),
numSpawnPhaseTurns: jest.fn().mockReturnValue(0),
}),
players: jest.fn(),
numLandTiles: jest.fn(),
numTilesWithFallout: jest.fn(),
setWinner: jest.fn(),
ticks: jest.fn().mockReturnValue(0),
stats: jest.fn().mockReturnValue({
stats: jest.fn(),
}),
} as unknown as jest.Mocked<Game>;
});
it("should declare winner when a team drops below 5% territory", () => {
const team1Players = [
{
numTilesOwned: jest.fn().mockReturnValue(40),
team: jest.fn().mockReturnValue("Team1" as Team),
},
] as unknown as Player[];
const team2Players = [
{
numTilesOwned: jest.fn().mockReturnValue(4), // < 5% territory
team: jest.fn().mockReturnValue("Team2" as Team),
},
] as unknown as Player[];
mg.players.mockReturnValue([...team1Players, ...team2Players]);
mg.numLandTiles.mockReturnValue(100);
mg.numTilesWithFallout.mockReturnValue(0);
winCheck.init(mg, 0);
winCheck.checkWinnerNukeWars();
// Team1 should win since Team2 is below 5%
expect(mg.setWinner).toHaveBeenCalledWith("Team1", expect.anything());
});
it("should not declare bot team as winner", () => {
const botTeamPlayers = [
{
numTilesOwned: jest.fn().mockReturnValue(90),
team: jest.fn().mockReturnValue(ColoredTeams.Bot as Team),
},
] as unknown as Player[];
const playerTeamPlayers = [
{
numTilesOwned: jest.fn().mockReturnValue(4),
team: jest.fn().mockReturnValue("Team1" as Team),
},
] as unknown as Player[];
mg.players.mockReturnValue([...botTeamPlayers, ...playerTeamPlayers]);
mg.numLandTiles.mockReturnValue(100);
mg.numTilesWithFallout.mockReturnValue(0);
winCheck.init(mg, 0);
winCheck.checkWinnerNukeWars();
// Should not declare bot team as winner even if other team is < 5%
expect(mg.setWinner).not.toHaveBeenCalledWith(
ColoredTeams.Bot,
expect.anything(),
);
});
it("should declare winner with most territory when time runs out", () => {
const team1Players = [
{
numTilesOwned: jest.fn().mockReturnValue(60),
team: jest.fn().mockReturnValue("Team1" as Team),
},
] as unknown as Player[];
const team2Players = [
{
numTilesOwned: jest.fn().mockReturnValue(40),
team: jest.fn().mockReturnValue("Team2" as Team),
},
] as unknown as Player[];
mg.players.mockReturnValue([...team1Players, ...team2Players]);
mg.numLandTiles.mockReturnValue(100);
mg.numTilesWithFallout.mockReturnValue(0);
mg.ticks.mockReturnValue(5 * 60 * 10 + 1); // Just past time limit
winCheck.init(mg, 0);
winCheck.checkWinnerNukeWars();
// Team1 should win since they have more territory when time expires
expect(mg.setWinner).toHaveBeenCalledWith("Team1", expect.anything());
});
});