Skip to content

Instantly share code, notes, and snippets.

@inxilpro
Last active November 3, 2025 15:01
Show Gist options
  • Select an option

  • Save inxilpro/d7ed9507b5f4c00dfb5dd2c70ea5f9bd to your computer and use it in GitHub Desktop.

Select an option

Save inxilpro/d7ed9507b5f4c00dfb5dd2c70ea5f9bd to your computer and use it in GitHub Desktop.
<?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