Last active
November 3, 2025 15:01
-
-
Save inxilpro/d7ed9507b5f4c00dfb5dd2c70ea5f9bd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace InterNACHI\Msfh\Tests\Feature\Admin; | |
| class Maze | |
| { | |
| protected const array DIRECTIONS = [ | |
| [1, 0], // Right | |
| [-1, 0], // Left | |
| [0, 1], // Down | |
| [0, -1], // Up | |
| ]; | |
| protected int $width; | |
| protected int $height; | |
| protected array $next; | |
| protected array $maze; | |
| protected array $visited; | |
| protected array $stack = []; | |
| public array $steps = []; | |
| public function __construct( | |
| protected int $pixel_width = 40, | |
| protected int $pixel_height = 25, | |
| protected bool $border = true, | |
| ) { | |
| $this->width = ceil($this->pixel_width / 2) + 2; | |
| $this->height = ceil($this->pixel_height / 2) + 2; | |
| $this->reset(); | |
| } | |
| public function render(): void | |
| { | |
| for ($y = 0; $y < $this->pixel_height; $y++) { | |
| for ($x = 0; $x < $this->pixel_width; $x++) { | |
| // TODO: Move border to carve method | |
| if ( | |
| $this->border | |
| && (0 === $y || ($this->pixel_height - 1) === $y || 0 === $x || ($this->pixel_width - 1) === $x) | |
| ) { | |
| echo '🌾'; | |
| continue; | |
| } | |
| $rx = $this->border ? $x - 1 : $x; | |
| $ry = $this->border ? $y - 1 : $y; | |
| $value = $this->maze[$ry][$rx] ?? false; | |
| echo $value ? '🌾' : '⬜'; | |
| } | |
| echo "\n"; | |
| } | |
| } | |
| public function regenerate(): void | |
| { | |
| $this->reset(); | |
| do { | |
| $stepped = $this->step(); | |
| } while ($stepped); | |
| } | |
| public function step(): bool | |
| { | |
| $point = $this->visit($this->next); | |
| $candidates = collect(static::DIRECTIONS) | |
| ->map(fn($direction) => $this->add($point, $direction)) | |
| ->filter(fn($candidate) => $this->valid($candidate)); | |
| if ($candidates->isNotEmpty()) { | |
| $this->next = $candidates->random(); | |
| $this->carve($point, $this->next); | |
| $this->stack[] = $point; | |
| return true; | |
| } | |
| if (! empty($this->stack)) { | |
| $this->next = array_pop($this->stack); | |
| return true; | |
| } | |
| return false; | |
| } | |
| protected function reset(): void | |
| { | |
| $this->maze = array_fill(0, $this->pixel_height + 4, array_fill(0, $this->pixel_width + 4, true)); | |
| $this->stack = []; | |
| $this->visited = array_fill(0, $this->height, array_fill(0, $this->width, false)); | |
| $this->drawBorder(); | |
| $this->next = $this->randomPoint(); | |
| } | |
| protected function drawBorder(): void | |
| { | |
| if (! $this->border) { | |
| return; | |
| } | |
| // $this->maze[0] = array_fill(0, $this->pixel_width, true); | |
| // $this->maze[$this->pixel_height - 1] = array_fill(0, $this->pixel_width, true); | |
| // $this->visited[0] = array_fill(0, $this->pixel_width, true); | |
| // $this->visited[$this->pixel_height - 1] = array_fill(0, $this->pixel_width, true); | |
| // for ($y = 0; $y < $this->pixel_height; $y++) { | |
| // for ($x = 0; $x < $this->pixel_width; $x++) { | |
| // $value = $this->maze[$y][$x] ?? false; | |
| // echo $value ? '🌾' : '⬜'; | |
| // } | |
| // echo "\n"; | |
| // } | |
| } | |
| protected function randomPoint(): array | |
| { | |
| return [random_int(0, $this->width - 1), random_int(0, $this->height - 1)]; | |
| } | |
| protected function valid(array $point): bool | |
| { | |
| [$x, $y] = $point; | |
| return $x >= 0 | |
| && $x < $this->width | |
| && $y >= 0 | |
| && $y < $this->height | |
| && ! $this->visited[$y][$x]; | |
| } | |
| protected function carve(array $from, array $to): void | |
| { | |
| $diff = $this->subtract($to, $from); | |
| $pixels = [ | |
| [$this->x($from, real: true), $this->y($from, real: true)], // from | |
| [$this->x($from, real: true) + $this->x($diff), $this->y($from, real: true) + $this->y($diff)], // wall | |
| [$this->x($to, real: true), $this->y($to, real: true)], // to | |
| ]; | |
| foreach ($pixels as [$x, $y]) { | |
| if (true === $this->maze[$y][$x]) { | |
| $this->maze[$y][$x] = false; | |
| $this->steps[] = [$x, $y]; | |
| } | |
| } | |
| } | |
| protected function visit(array $point): array | |
| { | |
| $this->visited[$this->y($point)][$this->x($point)] = true; | |
| return $point; | |
| } | |
| protected function add(array $a, array $b): array | |
| { | |
| return [$this->x($a) + $this->x($b), $this->y($a) + $this->y($b)]; | |
| } | |
| protected function subtract(array $a, array $b): array | |
| { | |
| return [$this->x($a) - $this->x($b), $this->y($a) - $this->y($b)]; | |
| } | |
| protected function x(array $point, bool $real = false): int | |
| { | |
| return $real ? $point[0] * 2 : $point[0]; | |
| } | |
| protected function y(array $point, bool $real = false): int | |
| { | |
| return $real ? $point[1] * 2 : $point[1]; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment