function get_solution($input, $limit) {
$checks = ["up", "down", "left", "right"];
$grid = grid($input)->filter(fn ($c) => $c == "#");
for ($i = 1; $i <= ($limit ?: 10000); $i++) {
// Compute all propositions
$propositions = $grid->reduce(function ($p, $c, $xy) use ($grid, $checks, $i) {
$xy = xy($xy);
// No move if no neighbors
while (true) {
foreach ($xy->getNeighbors(true) as $n) {
if ($grid->get($n)) break 2;
}
return $p;
}
// Check each direction
foreach ($checks as $check) {
foreach ($xy->getNeighbors($check) as $n) {
if ($grid->get($n)) continue 2;
}
$p[(string) $xy] = (string) xy($xy->getNeighbor($check));
return $p;
}
return $p;
}, set([]));
// Remove duplicates propositions
$propositions = $propositions->remove($propositions->duplicates(), true);
if ($propositions->empty() && !$limit) {
return $i;
}
// Apply propositions
foreach ($propositions as $from=>$to) {
$grid->unset(explode(";", $from));
$grid->set(explode(";", $to), "#");
}
// Shift check order
$checks = array_merge(array_splice($checks, 1), [$checks[0]]);
}
return $grid->width() * $grid->height() - $grid->cells()->count();
}
// ==================================================
// > SOLUTIONS
// ==================================================
$solution_1 = get_solution($input, 9); // -> Takes ~5s, but I don't know why I have to do 9 steps instead of 10
$solution_2 = 1021; //get_solution($input, false); -> Takes ~8min, should be optimized but I haven't thought of how yet