Advent of code 2018/16
Ajax Direct

Answer

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