$opcodes = [
"addr" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a] + $r[$b],
"addi" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a] + $b,
"mulr" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a] * $r[$b],
"muli" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a] * $b,
"banr" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a] & $r[$b],
"bani" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a] & $b,
"borr" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a] | $r[$b],
"bori" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a] | $b,
"setr" => fn (&$r, $a, $b, $c) => $r[$c] = $r[$a],
"seti" => fn (&$r, $a, $b, $c) => $r[$c] = $a,
"gtir" => fn (&$r, $a, $b, $c) => $r[$c] = (int) ($a > $r[$b]),
"gtri" => fn (&$r, $a, $b, $c) => $r[$c] = (int) ($r[$a] > $b),
"gtrr" => fn (&$r, $a, $b, $c) => $r[$c] = (int) ($r[$a] > $r[$b]),
"eqir" => fn (&$r, $a, $b, $c) => $r[$c] = (int) ($a == $r[$b]),
"eqri" => fn (&$r, $a, $b, $c) => $r[$c] = (int) ($r[$a] == $b),
"eqrr" => fn (&$r, $a, $b, $c) => $r[$c] = (int) ($r[$a] == $r[$b]),
];
// ==================================================
// > PART 1 : EXECUTE TESTS
// ==================================================
$pass = array_fill_keys(array_keys($opcodes), []);
$solution_1 = 0;
foreach ($input->split("\n\n\n")[0]->lines->chunk(4) as $i=>$group) {
$nums = $group->join("")->numbers()->chunk(4)->map(fn ($a)=> (array) $a);
$matches = 0;
foreach ($opcodes as $code=>$op) {
$r = $nums[0];
$op($r, $nums[1][1], $nums[1][2], $nums[1][3]);
if ($r != $nums[2]) {
$pass[$code][$nums[1][0]] = false;
} else {
$matches++;
}
}
if ($matches >= 3) $solution_1++;
}
// ==================================================
// > PART 2 : FIND NUMBER-CODES AND EXECUTE PROGRAM
// ==================================================
$pass = array_map(fn ($pass) => array_values(array_diff(range(0, 15), array_keys($pass))), $pass);
$numbers = [];
while (true) {
foreach ($pass as $c=>$p) if (count($p) == 1) {
$numbers[$p[0]] = $c;
foreach ($pass as $rc=>$rp) $pass[$rc] = array_values(array_diff($rp, [$p[0]]));
continue 2;
}
break;
}
$r = array_fill_keys(range(0, 3), 0);
foreach ($input->split("\n\n\n\n")[1]->numbers()->chunk(4) as $line) {
$opcodes[$numbers[$line[0]]]($r, $line[1], $line[2], $line[3]);
}
$solution_2 = $r[0];