<?phpnamespace Tests;use App\Sim\Autoplay;use App\Sim\LevelBuilder;use App\Sim\Levels;use App\Sim\Lumi;use App\Sim\Terrain;use App\Sim\World;use PHPUnit\Framework\TestCase;/** The rules, without a window: a flat world, one Lumikin, one skill at a time. */final class WorldTest extends TestCase{ private function flat(int $width = 300): World { $t = new Terrain($width, 200); $t->fillRect(0, 150, $width, 50, Terrain::EARTH); $w = new World($t); $w->total = 1; $w->need = 1; $w->ticksLeft = 100000; $w->hatch = [40, 140]; $w->exit = [9999, 150]; foreach (World::SKILLS as $s) { $w->skills[$s] = 5; } return $w; } private function steps(World $w, int $ticks): Lumi { for ($i = 0; $i < $ticks; $i++) { $w->step(); } return $w->lumis[0]; } public function testALumikinComesOutAndWalksToTheRight(): void { $w = $this->flat(); $l = $this->steps($w, 100); self::assertSame(Lumi::WALK, $l->state); self::assertGreaterThan(60, $l->x); self::assertSame(150, $l->y); } public function testItTurnsAroundAtAWall(): void { $w = $this->flat(); $w->terrain->fillRect(100, 100, 10, 50, Terrain::EARTH); $l = $this->steps($w, 160); self::assertSame(-1, $l->dir); self::assertLessThan(100, $l->x); } public function testADiggerGoesStraightDown(): void { $w = $this->flat(); $l = $this->steps($w, 60); self::assertTrue($w->assign($l, 'dig')); $x = $l->x; $this->steps($w, 60); self::assertSame($x, $l->x); self::assertGreaterThan(165, $l->y); self::assertFalse($w->terrain->solid($x, 152)); self::assertSame(4, $w->skills['dig']); } public function testABasherOpensATunnelThroughAWall(): void { $w = $this->flat(); $w->terrain->fillRect(100, 100, 30, 50, Terrain::EARTH); $l = $this->steps($w, 98); self::assertTrue($w->assign($l, 'bash')); $this->steps($w, 200); self::assertGreaterThan(135, $l->x); self::assertFalse($w->terrain->solid(115, 145)); self::assertTrue($w->terrain->solid(115, 120)); } public function testSteelStopsABasher(): void { $w = $this->flat(); $w->terrain->fillRect(100, 100, 30, 50, Terrain::STEEL); $l = $this->steps($w, 98); $w->assign($l, 'bash'); $this->steps($w, 60); self::assertTrue($w->terrain->solid(115, 145)); self::assertLessThan(100, $l->x); } public function testABuilderLaysAStairOfTwelveBricks(): void { $w = $this->flat(); $l = $this->steps($w, 60); $w->assign($l, 'bridge'); $x = $l->x; $this->steps($w, 9 * 12 + 2); self::assertSame(World::MAX_BRICKS, $l->bricks); self::assertSame(150 - 12, $l->y); self::assertSame($x + 24, $l->x); self::assertSame(Terrain::BRICK, $w->terrain->at($x + 10, 150 - 5)); } public function testABlockerTurnsTheOthersAround(): void { $w = $this->flat(); $w->total = 2; $w->rate = 99; $this->steps($w, 70); self::assertCount(2, $w->lumis); self::assertTrue($w->assign($w->lumis[0], 'stop')); $this->steps($w, 20); self::assertSame(-1, $w->lumis[1]->dir); self::assertLessThan($w->lumis[0]->x, $w->lumis[1]->x); } public function testALongFallIsDeadlyUnlessItGlides(): void { foreach ([false, true] as $glider) { $w = $this->flat(); $w->hatch = [40, 20]; $l = $this->steps($w, 42); if ($glider) { $w->assign($l, 'glide'); } $this->steps($w, 200); self::assertSame($glider, $l->alive); } } public function testAClimberGoesOverAWall(): void { $w = $this->flat(); $w->terrain->fillRect(100, 110, 10, 40, Terrain::EARTH); $l = $this->steps($w, 60); $w->assign($l, 'climb'); $this->steps($w, 200); self::assertGreaterThan(110, $l->x); self::assertTrue($l->alive); } public function testAFirecrackerBurstsAfterThreeSecondsAndDigsAHole(): void { $w = $this->flat(); $l = $this->steps($w, 60); $w->assign($l, 'boom'); $this->steps($w, World::FUSE_TICKS - 2); self::assertTrue($l->alive); $this->steps($w, 4); self::assertFalse($l->alive); self::assertSame(1, $w->lost); self::assertFalse($w->terrain->solid($l->x, 152)); } public function testReachingTheExitSavesIt(): void { $w = $this->flat(); $w->exit = [120, 150]; $this->steps($w, 160); self::assertSame(1, $w->saved); self::assertTrue($w->isOver()); self::assertTrue($w->isWon()); } public function testEveryLevelCanBeWonByItsSolution(): void { foreach (Levels::all() as $i => $def) { $w = LevelBuilder::build($def); $auto = new Autoplay($w, $def['solution']); while (!$w->isOver()) { $w->step(); $auto->step(); $w->events = []; } self::assertTrue($w->isWon(), 'level ' . ($i + 1) . " ({$def['name']}): saved {$w->saved}, needs {$w->need}"); } }}