Answer ⏱
[VISUALISATION]
Part 1 :
Part 2 :
// Build map
$grid = [];
foreach ($input->lines as $line) {
$nums = $line->numbers();
foreach (range($nums[1], $nums[2]) as $num) {
$line[0] == "x"
? ($grid[$num][$nums[0]] = "#")
: ($grid[$nums[0]][$num] = "#");
}
}
// Simulate waterfall
$ys = array_keys($grid); sort($ys);
$floor = max($ys);
$ceiling = $ys[0];
$drops = [[500, 0]];
$sources = [];
$d = 0;
while ($drops) {
$drop = array_shift($drops);
$log[] = $drop;
$d++;
[$x, $y] = $drop;
$grid[$y][$x] = 0;
if (!empty($sources[$y][$x])) continue;
$sources[$y][$x] = true;
// Go down as far as possible
while ((empty($grid[++$y][$x])) && $y <= $floor) $grid[$y][$x] = 0;
if ($y > $floor) continue;
$y--;
// Go left and right as far as possible
[$l, $r] = [$x, $x];
$grid[$y][$x] = 0;
while ((empty($grid[$y][--$l])) && !empty($grid[$y+1][$l])) $grid[$y][$l] = 0;
while ((empty($grid[$y][++$r])) && !empty($grid[$y+1][$r])) $grid[$y][$r] = 0;
// Add drop if we reached a border
if (empty($grid[$y][$l])) $drops[] = [$l, $y];
if (empty($grid[$y][$r])) $drops[] = [$r, $y];
// Add drop above if water is traped, and change water state
if (!empty($grid[$y][$l]) && !empty($grid[$y][$r])) {
$sources[$y-1][$x] = false;
$drops[] = [$x, $y-1];
foreach (range($l + 1, $r - 1) as $x) {
$grid[$y][$x] = "~";
}
}
}
$tiles = set($grid)->merge();
// ==================================================
// > PART 1
// ==================================================
$solution_1 = $tiles->remove("#")->count() - $ceiling;
// ==================================================
// > PART 2
// ==================================================
$solution_2 = $tiles->keep("~")->count();