Terrain::Forest]), units: [ new UnitState('alpha-1', 'alpha', new Position(0, 0), 12, 12, 3, 4, 2, 2, false, Archetype::Defender, ['buff'], 0, false), new UnitState('alpha-2', 'alpha', new Position(1, 0), 12, 12, 3, 4, 2, 2, false, Archetype::Defender, [], 0, false), new UnitState('alpha-3', 'alpha', new Position(2, 0), 12, 12, 3, 4, 2, 2, false, Archetype::Defender, [], 0, false), new UnitState('bravo-1', 'bravo', new Position(7, 7), 8, 8, 5, 2, 3, 2, false, Archetype::Striker, ['area_damage'], 0, false), new UnitState('bravo-2', 'bravo', new Position(6, 7), 8, 8, 5, 2, 3, 2, false, Archetype::Striker, [], 0, false), new UnitState('bravo-3', 'bravo', new Position(5, 7), 8, 8, 5, 2, 3, 2, false, Archetype::Striker, [], 0, false), ], deploymentZones: [ 'alpha' => new DeploymentZone('alpha', [new Position(0, 0), new Position(1, 0), new Position(2, 0)]), 'bravo' => new DeploymentZone('bravo', [new Position(7, 7), new Position(6, 7), new Position(5, 7)]), ], objectives: [], victoryCondition: VictoryCondition::EliminateAll, holdRoundsRequired: 1, ); $array = ScenarioSerializer::scenarioToArray($scenario); $reconstructed = ScenarioSerializer::scenarioFromArray($array); self::assertSame($scenario->id, $reconstructed->id); self::assertSame($scenario->name, $reconstructed->name); self::assertSame($scenario->battlefield->width, $reconstructed->battlefield->width); self::assertSame($scenario->battlefield->height, $reconstructed->battlefield->height); self::assertSame('forest', $reconstructed->battlefield->terrainAt(new Position(0, 0))->value); self::assertCount(6, $reconstructed->units); self::assertSame('alpha-1', $reconstructed->units[0]->id); self::assertSame(Archetype::Defender, $reconstructed->units[0]->archetype); self::assertSame(['buff'], $reconstructed->units[0]->abilities); self::assertSame(12, $reconstructed->units[0]->maxHealth); self::assertSame(3, $reconstructed->units[0]->attack); self::assertSame(4, $reconstructed->units[0]->defense); self::assertSame(2, $reconstructed->units[0]->speed); self::assertSame($scenario->victoryCondition, $reconstructed->victoryCondition); } public function testItRoundTripsAHoldObjectiveScenario(): void { $scenario = new Scenario( id: 'hold', name: 'Hold', battlefield: new Battlefield(8, 8), units: [ new UnitState('alpha-1', 'alpha', new Position(0, 0), 12, 12, 3, 4, 2, 2, false, Archetype::Defender, [], 0, false), new UnitState('alpha-2', 'alpha', new Position(1, 0), 12, 12, 3, 4, 2, 2, false, Archetype::Defender, [], 0, false), new UnitState('alpha-3', 'alpha', new Position(2, 0), 12, 12, 3, 4, 2, 2, false, Archetype::Defender, [], 0, false), new UnitState('bravo-1', 'bravo', new Position(7, 7), 8, 8, 5, 2, 3, 2, false, Archetype::Striker, [], 0, false), new UnitState('bravo-2', 'bravo', new Position(6, 7), 8, 8, 5, 2, 3, 2, false, Archetype::Striker, [], 0, false), new UnitState('bravo-3', 'bravo', new Position(5, 7), 8, 8, 5, 2, 3, 2, false, Archetype::Striker, [], 0, false), ], deploymentZones: [ 'alpha' => new DeploymentZone('alpha', [new Position(0, 0), new Position(1, 0), new Position(2, 0)]), 'bravo' => new DeploymentZone('bravo', [new Position(7, 7), new Position(6, 7), new Position(5, 7)]), ], objectives: ['objective-1' => new ObjectiveMarker('objective-1', new Position(4, 4))], victoryCondition: VictoryCondition::HoldObjective, holdRoundsRequired: 3, ); $reconstructed = ScenarioSerializer::scenarioFromArray(ScenarioSerializer::scenarioToArray($scenario)); self::assertSame(VictoryCondition::HoldObjective, $reconstructed->victoryCondition); self::assertSame(3, $reconstructed->holdRoundsRequired); self::assertArrayHasKey('objective-1', $reconstructed->objectives); self::assertSame('4:4', $reconstructed->objectives['objective-1']->position->key()); } public function testItRoundTripsAMatchState(): void { $scenario = new Scenario( id: 'demo', name: 'Demo', battlefield: new Battlefield(8, 8), units: [ new UnitState('alpha-1', 'alpha', new Position(0, 0), 6, 6, 4, 2, 4, 2, false, Archetype::Scout, [], 0, false), new UnitState('alpha-2', 'alpha', new Position(1, 0), 6, 6, 4, 2, 4, 2, false, Archetype::Scout, [], 0, false), new UnitState('alpha-3', 'alpha', new Position(2, 0), 6, 6, 4, 2, 4, 2, false, Archetype::Scout, [], 0, false), new UnitState('bravo-1', 'bravo', new Position(7, 7), 6, 6, 4, 2, 4, 2, false, Archetype::Scout, [], 0, false), new UnitState('bravo-2', 'bravo', new Position(6, 7), 6, 6, 4, 2, 4, 2, false, Archetype::Scout, [], 0, false), new UnitState('bravo-3', 'bravo', new Position(5, 7), 6, 6, 4, 2, 4, 2, false, Archetype::Scout, [], 0, false), ], deploymentZones: [ 'alpha' => new DeploymentZone('alpha', [new Position(0, 0), new Position(1, 0), new Position(2, 0)]), 'bravo' => new DeploymentZone('bravo', [new Position(7, 7), new Position(6, 7), new Position(5, 7)]), ], objectives: [], victoryCondition: VictoryCondition::EliminateAll, holdRoundsRequired: 1, ); $match = $scenario->startMatch('alpha'); $reconstructed = ScenarioSerializer::matchFromArray(ScenarioSerializer::matchToArray($match)); self::assertSame('alpha', $reconstructed->activeTeamId); self::assertSame(1, $reconstructed->round); self::assertCount(6, $reconstructed->units); self::assertSame(2, $reconstructed->units[0]->actionsRemaining); self::assertFalse($reconstructed->units[0]->hasAttacked); self::assertSame(0, $reconstructed->units[0]->attackBonus); } public function testItRejectsShapeErrors(): void { $this->expectException(\InvalidArgumentException::class); ScenarioSerializer::scenarioFromArray([ 'id' => 'demo', // missing 'name', 'battlefield', 'units', etc. ]); } }