Advent of code 2023/19
Ajax Direct

Answer

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