Advent of code 2018/23
Ajax Direct

Answer

Part 1 :
Part 2 :
$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();