$guards = [];
$guard = null;
// Parse input
foreach ($input->lines->sort()->values() as $line) {
$nums = $line->numbers;
// Switch guard
if (!empty($nums[5])) {
$guard = $nums[5];
continue;
}
// Add state switches
$date = $nums->slice(0, 3)->join("")->string;
$guards[$guard][$date] ??= [];
$guards[$guard][$date][] = $nums[4];
}
// Calc ranges
foreach ($guards as $id=>$guard) {
foreach ($guard as $date=>$events) {
$guards[$id][$date] = array_merge(
...array_map(fn ($range) => range($range[0], $range[1] - 1), array_chunk($events, 2))
);
}
}
// ==================================================
// > PART 1
// ==================================================
// Find guard with most minutes asleep
$most_asleep = set(array_map(
fn ($guard) => count(array_merge(...array_values($guard))),
$guards
))->sort()->keys()->last();
// Find the most common hour he was asleep
$most_common = set($guards[$most_asleep])->merge()->occurrences()->sort()->keys()->last();
// Part 1
$solution_1 = $most_asleep * $most_common;
// ==================================================
// > PART 2
// ==================================================
$solution_2 = set($guards)->mapAssoc(function ($id, $guard) {
$hours = set($guard)->merge()->occurrences()->sort();
return [$hours->keys()->last() * $id => $hours->values()->last()];
})->sort()->keys()->last();