// Parse the grid
$grid = grid($input);
// =============================================================================
// > GRAPH DEFINITION
// =============================================================================
$start = xy([1, 0]);
$end = xy([$grid->width() - 2, $grid->height() - 1]);
$map = new Graph(function ($graph) use ($end, $grid) {
[$x, $y, $t] = explode(";", $graph->current);
$edges = [];
foreach ([[$x, $y], [$x + 1, $y], [$x - 1, $y], [$x, $y - 1], [$x, $y + 1]] as [$nx, $ny]) {
// Don't visit if it's outside the grid
if ($nx < 1 || $nx > $end[0] || $ny < 0 || $ny > $end[1]) continue;
// Don't visit if it's a wall or a blizzard
if ($grid[$ny][$nx] == "#") continue;
// Don't visit if in a blizzard at that moment
if ($grid[1 + Math::mod(($ny - $t - 2), ($grid->height() - 2))][$nx] == "v") continue;
if ($grid[1 + Math::mod(($ny + $t), ($grid->height() - 2))][$nx] == "^") continue;
if ($grid[$ny][1 + Math::mod(($nx - $t - 2), ($grid->width() - 2))] == ">") continue;
if ($grid[$ny][1 + Math::mod(($nx + $t), ($grid->width() - 2))] == "<") continue;
$edges[implode(";", [$nx, $ny, ($t + 1) % 600])] = 1;
}
return $edges;
});
$map->defineEnd(function ($graph) {
[$x, $y, $t] = explode(";", $graph->current);
return $graph->end == implode(";", [$x, $y]);
});
// Define an heuristic to visit the node closest to the end first
$map->definePriority(function ($graph, $to, $distance) use ($end) {
[$x, $y, $t] = explode(";", $graph->current);
return $distance + xy([$x, $y])->manhattan($end);
});
// =============================================================================
// > SOLUTIONS
// =============================================================================
$solution_1 = $map->explore(((string) $start).";0", (string) $end)[1];
$a = $solution_1;
$b = $map->explore(((string) $end).";" . $a, (string) $start)[1];
$c = $map->explore(((string) $start).";" . ($a + $b), (string) $end)[1];
$solution_2 = $a + $b + $c;