expectException(InvalidArgumentException::class); new Battlefield(7, 16); } public function testItRejectsNonCanonicalTerrainCoordinates(): void { $this->expectException(InvalidArgumentException::class); new Battlefield(8, 8, ['01:0' => Terrain::Forest]); } public function testItRejectsMalformedTerrainCoordinates(): void { $this->expectException(InvalidArgumentException::class); new Battlefield(8, 8, ['not-a-coordinate' => Terrain::Forest]); } public function testItRejectsInvalidTerrainValues(): void { $this->expectException(InvalidArgumentException::class); $terrain = ['0:0' => 'forest']; new Battlefield(8, 8, $terrain); } public function testReachableRejectsAStartOutsideTheBattlefield(): void { $battlefield = new Battlefield(8, 8); $this->expectException(InvalidArgumentException::class); $battlefield->reachable(new Position(-1, 0), 2, []); } public function testReachableRejectsANegativeBudget(): void { $battlefield = new Battlefield(8, 8); $this->expectException(InvalidArgumentException::class); $battlefield->reachable(new Position(0, 0), -1, []); } public function testReachableRejectsOccupiedPositionsOutsideTheBattlefield(): void { $battlefield = new Battlefield(8, 8); $this->expectException(InvalidArgumentException::class); $battlefield->reachable(new Position(0, 0), 2, [new Position(8, 0)]); } public function testItFindsReachableTilesUsingTerrainCostsAndObstacles(): void { $battlefield = new Battlefield(8, 8, [ '1:0' => Terrain::Forest, '0:1' => Terrain::Rough, '2:0' => Terrain::Water, '1:1' => Terrain::Blocking, ]); $reachable = $battlefield->reachable( new Position(0, 0), 2, [new Position(0, 2)], ); self::assertSame([ '0:0' => 0, '1:0' => 2, '0:1' => 2, ], $reachable); } }