$bots = $input->numbers->chunk(4)->sort(fn ($a, $b) => $b[3] <=> $a[3])->values();
// ==================================================
// > PART 1
// ==================================================
$strongest = $bots[0];
$solution_1 = 0;
foreach ($bots as $bot) {
if (manhattan($bot->slice(0, 3), $strongest->slice(0, 3)) <= $strongest[3]) $solution_1++;
}
// ==================================================
// > PART 2
// ==================================================
/**
* Get the number of bots who can reach that box
*
* @param array $box
* @param Set $bots
* @return int
*/
function get_box_count($box, $bots) {
return $bots->filter(function ($bot) use ($box) {
$d = 0;
foreach ([0, 1, 2] as $i) {
$d += abs($bot[$i] - $box[$i][0])
+ abs($bot[$i] - $box[$i][1])
- ($box[$i][1] - $box[$i][0]);
}
return $d <= $bot[3] * 2;
})->count();
}
// Binary search, start with big box that has everything inside
$max = $bots->map(fn ($bot) => [
$bot[0] - $bot[3], $bot[0] + $bot[3],
$bot[1] - $bot[3], $bot[1] + $bot[3],
$bot[2] - $bot[3], $bot[2] + $bot[3]
])->merge()->map("abs")->max();
$s = 1; while (($s *= 2) < $max + 1); $s--;
$queue = new PriorityQueue;
$queue->insert([[-$s, $s], [-$s, $s], [-$s, $s]], 0);
while ($queue->count()) {
$box = $queue->extract();
// Stop as soon as we have a 1x1x1 box
if (!array_sum(get_box_size($box))) break;
// Divide box in 8 sub-boxes, that we add to the queue of boxes to divide.
// Use priority to explore boxes with most potential first, or bigger boxes in case of equality.
foreach (divide_box($box) as $i=>$subox) {
$queue->insert($subox, array_sum([
get_box_count($subox, $bots) * 1_000_000,
array_sum(get_box_size($subox)),
]));
}
}
$solution_2 = set($box)->column(0)->sum();