Advent of code 2019/17
Ajax Direct

Answer 445ms

[VISUALISATION] Part 1 : 8084 Part 2 : 1119775
$bot = new Intcode($input);
$bot->run();
$grid = new Grid(
    trim($bot->output->map(fn ($c) => chr($c))->join(""))
);

// ==================================================
// > PART 1
// ==================================================
$solution_1 = 0;
foreach ($grid->searchAll("#") as $pos) {
    if ($grid->getNeighbors($pos)->keep("#")->count() == 4) {
        $solution_1 += $pos[0] * $pos[1];
    }
}

// ==================================================
// > PART 2
// ==================================================
// Step 1 : Find the full route
$pos  = $grid->search("^");
$dir  = 0;
$path = set([]);

while (true) {
    $neig = $grid->getNeighbors($pos);
    $ndir = null;

    if ($neig->values()[$dir] == "#") {
        $path[count($path) - 1]++;
        $ndir = $dir;
    }
    elseif ($neig->values()[($dir + 1) % 4] == "#") {
        $path->insert(["R", 1]);
        $ndir = ($dir + 1) % 4;
    }
    elseif ($neig->values()[($dir + 3) % 4] == "#") {
        $path->insert(["L", 1]);
        $ndir = ($dir + 3) % 4;
    }

    if (is_null($ndir)) break;

    $pos = explode(";", $neig->keys()[$ndir]);
    $dir = $ndir;
}

// Step 2 : Find the patterns A, B, C
$path = (string) $path->join(",");
$func = [];

while (true) {
    $i = 1;
    $f = false;
    while (true) {
        if (preg_match("/.*((?:(?:R|L),[0-9]+,?){".$i."}).*\\1.*\\1.*/", $path, $match)) {
            $f = trim($match[1], ",");
            $i++;
        } else break;
    }

    if (!$f) break;

    $func[["A", "B", "C"][count($func)]] = $f;
    $path = str_replace($f, array_keys($func)[count($func) - 1], $path);
}

// Step 3 : run with functions
$bot = (new Intcode($input->numbers()->set(0, 2)));
$bot->input = set(array_map("ord", str_split(implode("\n", array_merge([$path], $func, ["n\n"])))));
$solution_2 = $bot->run();