function solve($input, $ultra = false) {
$grid = array_map("str_split", explode("\n", $input));
$goal = [count($grid[0]) - 1, count($grid) - 1];
return (new Graph(function ($graph) use ($grid, $ultra) {
[$x, $y, $cons, $dir] = explode(";", $graph->current);
$neighbors = neighbors([$x, $y]);
// Can't reverse
unset($neighbors[["up" => "down", "down" => "up", "left" => "right", "right" => "left"][$dir]??""]);
// Can't move 3 blocks in the same direction, or 10 for ultra
if ($cons >= 10 || (!$ultra && $cons >= 3)) unset($neighbors[$dir]);
// Ultra : Can't turn bellow 4
if ($ultra && $cons < 4 && $dir != "none") {
$neighbors = array_intersect_key($neighbors, [$dir => true]);
}
foreach ($neighbors as $n=>[$nx,$ny]) {
if (!isset($grid[$ny][$nx])) continue; // OOB
$next["$nx;$ny;".($n==$dir?$cons+1:1).";$n"] = $grid[$ny][$nx];
}
return $next ?? [];
}))
// End when end is reached
->defineEnd(function ($graph) use ($goal, $ultra) {
[$x, $y, $cons] = explode(";", $graph->current);
if ($ultra && $cons < 4) return false;
return [$x, $y] == $goal;
})
// A*
->definePriority(function ($graph, $state, $value) use ($goal) {
[$x, $y] = explode(";", $graph->current);
return $value + manhattan([$x, $y], $goal) * 2;
})
->explore("0;0;0;none")[1];
}
// ==================================================
// > SOLVE
// ==================================================
$solution_1 = solve($input, false);
$solution_2 = solve($input, true);