// Build the map
$grid = grid();
$grid->set([500, 0], "+");
foreach ($input->lines as $line) {
    $corners = $line->numbers()->chunk(2);
    $c[] = $corners;
    for ($i = 0; $i < $corners->count() - 1; $i++) {
        $grid->drawLine((array) $corners[$i], (array) $corners[$i + 1], "#");
    }
}
function get_solution($grid, $with_floor = false)
{
    $floor = $grid->rows()->keys()->max() + ($with_floor ? 2 : 0);
    $rest  = 0;
    // Draw floor
    if ($with_floor) foreach (xy([-1000, $floor])->pathTo(xy([1000, $floor])) as $cell) {
        $grid->set($cell, "#");
    }
    // Start moving sand
    while (true) {
        // Source is covered in sand, stop
        if ($with_floor && $grid->get([500, 0]) === "o") break;
        // Emit a new sand and move it one step at a time
        $sand = xy([500, 0]);
        while (true) {
            $sand->move([0, 1]);
            // Reached the bottom and has no floor, stop there
            if (!$with_floor && $sand[1] > $floor) break 2;
            // Nothing, keep falling
            if (!$grid->get($sand)) continue;
            // Hit something, try to go left
            $sand->move([-1, 0]);
            if (!$grid->get($sand)) continue;
            // Hit something, try to go right
            $sand->move([2, 0]);
            if (!$grid->get($sand)) continue;
            // Nowhere to go, stack on top
            $sand->move([-1, -1]);
            $grid->set($sand, "o");
            $rest++;
            break;
        }
    }
    return [$rest, $grid];
}
// ==================================================
// > SOLUTIONS
// ==================================================
$solution_1 = get_solution(clone $grid)[0];
$solution_2 = get_solution($grid, true)[0];