/**
* Return the mutated grid.
* Take a callback as second argument, that specifies how many bugs are surounding a given position.
*
* @param array $grid
* @param callable $count_neighbors_bugs
* @return array
*/
function mutate($grid, $count_neighbors_bugs) {
$ngrid = [];
for ($y = 0; $y < 5; $y++) for ($x = 0; $x < 5; $x++) {
$nbugs = $count_neighbors_bugs([$x, $y]);
if ($grid[$y][$x] == "#") {
$ngrid[$y][$x] = $nbugs == 1 ? "#" : ".";
} else {
$ngrid[$y][$x] = $nbugs == 1 || $nbugs == 2 ? "#" : ".";
}
}
return $ngrid;
}
// ==================================================
// > PART 1
// ==================================================
$grid = array_map("str_split", explode("\n", (string) $input));
$seen = [];
while (true) {
$grid = mutate($grid, function ($pos) use ($grid) {
$nbugs = 0;
foreach (neighbors($pos) as [$x, $y]) {
if (($grid[$y][$x] ?? false) == "#") $nbugs++;
}
return $nbugs;
});
$state = implode("", array_merge(...$grid));
if (!empty($seen[$state])) {
$solution_1 = 0;
foreach (str_split($state) as $i => $c) {
if ($c == "#") $solution_1 += (2 ** $i);
}
break;
}
$seen[$state] = true;
}
// ==================================================
// > PART 2
// ==================================================
$grids = [array_map("str_split", explode("\n", (string) $input))];
$empty = array_chunk(array_fill(0, 25, "."), 5);
for ($m = 0; $m < 200; $m++) {
// Expand up if first is not empty
if (array_search("#", array_merge(...$grids[0])) !== false) {
array_unshift($grids, $empty);
}
// Expand down if last is not empty
if (array_search("#", array_merge(...$grids[count($grids) - 1])) !== false) {
$grids[] = $empty;
}
// Mutate each level
$ngrids = [];
foreach ($grids as $i=>$grid) {
$ngrids[$i] = mutate($grid, function ($pos) use ($grid, $i, $grids) {
$outer = $grids[$i - 1] ?? false;
$inner = $grids[$i + 1] ?? false;
$nbugs = 0;
foreach (neighbors($pos) as [$x, $y]) {
if (isset($grid[$y][$x]) && ($x != 2 || $y != 2)) {
if ($grid[$y][$x] == "#") $nbugs++;
} elseif ($x == 2 && $y == 2) {
if (empty($inner)) continue;
$nbugs = match (implode(";", $pos)) {
"2;1" => array_reduce(range(0, 4), fn ($n, $dx) => $n + ($inner[0][$dx] == "#" ? 1 : 0) , $nbugs),
"2;3" => array_reduce(range(0, 4), fn ($n, $dx) => $n + ($inner[4][$dx] == "#" ? 1 : 0) , $nbugs),
"1;2" => array_reduce(range(0, 4), fn ($n, $dy) => $n + ($inner[$dy][0] == "#" ? 1 : 0) , $nbugs),
"3;2" => array_reduce(range(0, 4), fn ($n, $dy) => $n + ($inner[$dy][4] == "#" ? 1 : 0) , $nbugs),
};
} else {
if (empty($outer)) continue;
if ($y == -1 && $outer[1][2] == "#") $nbugs++;
if ($y == 5 && $outer[3][2] == "#") $nbugs++;
if ($x == -1 && $outer[2][1] == "#") $nbugs++;
if ($x == 5 && $outer[2][3] == "#") $nbugs++;
}
}
return $nbugs;
});
$ngrids[$i][2][2] = "?";
}
$grids = $ngrids;
}
$solution_2 = count(array_diff(array_merge(...array_merge(...$grids)), ["?", "."]));