[$workflows, $parts] = $input->split("\n\n");
$workflows = $workflows->lines->mapAssoc(function ($i, $line) {
[$name, $rules] = $line->sub(0, -1)->split("{");
return [(string) $name => $rules->split(",")->map(fn ($rule) =>
$rule->match("/([a-zA-Z]+)(<|>)?([0-9]*):?([a-zA-Z]*)/")
)];
});
// ==================================================
// > PART 1
// ==================================================
$parts = $parts->lines->map(function ($line) {
$m = $line->matchAll("/([a-z])=([0-9]+)/");
return array_combine($m[1], array_map("intval", $m[2]));
});
foreach ($parts as $part) {
$w = "in";
while ($w != "R" && $w != "A") foreach ($workflows[$w] as $rule) {
$w = match ($rule[2]) {
"<" => $part[$rule[1]] < $rule[3] ? $rule[4] : false,
">" => $part[$rule[1]] > $rule[3] ? $rule[4] : false,
"" => $rule[1],
};
if ($w) break;
}
$sorted[$w][] = $part;
}
$solution_1 = set($sorted["A"])->map(fn ($p) => array_sum($p))->sum();
// ==================================================
// > PART 2
// ==================================================
// 1. Build paths to all workflows and wins (A)
$paths = [];
foreach ($workflows as $w=>$flow) {
$else = [];
foreach ($flow as $i=>$rule) {
if ($rule[4]) {
$paths[$rule[4]][] = [$w, array_merge($else, [[$rule[1], $rule[2], $rule[3]]])];
} else {
$paths[$rule[1]][] = [$w, $else];
}
$else[] = [$rule[1], $rule[2] == ">" ? "<=" : ">=", $rule[3]];
}
}
// 2. Link conditions to reach all wins (A)
$wins = [];
foreach ($paths["A"] as $path) {
[$source, $rules] = $path;
while (true) {
[$source, $nrules] = $paths[$source][0];
$rules = array_merge($nrules, $rules);
if ($source == "in") break;
}
$wins[] = $rules;
}
// 3. Build ranges for all these conditions
$ranges = [];
foreach ($wins as $win) {
$range = ["x" => [1, 4000], "m" => [1, 4000], "a" => [1, 4000], "s" => [1, 4000]];
foreach ($win as $cond) {
match ($cond[1]) {
">" => $range[$cond[0]][0] = $cond[2] + 1,
">=" => $range[$cond[0]][0] = $cond[2],
"<" => $range[$cond[0]][1] = $cond[2] - 1,
"<=" => $range[$cond[0]][1] = $cond[2],
};
}
$ranges[] = $range;
}
// 4. Compute solution
$solution_2 = array_sum(array_map(fn ($range) =>
array_product(array_map(fn ($r) =>
$r[1] - $r[0] + 1
, $range))
, $ranges));