Advent of code 2020/24
Ajax Direct

Answer 796ms

Part 1 : 523 Part 2 : 4225
$dir = [
    "w"  => [-1,  0],
    "e"  => [ 1,  0],
    "nw" => [-1, -1],
    "ne" => [ 0, -1],
    "sw" => [ 0,  1],
    "se" => [ 1,  1],
];

// ==================================================
// > PART 1
// ==================================================
$grid = grid();
foreach ($input->lines as $line) {
    $xy = xy([0, 0]);

    foreach ($line->matchAll("/w|e|nw|ne|sw|se/")[0] as $d) {
        $xy->move($dir[(string) $d]);
    }

    $grid->set($xy, !$grid->get($xy));
}

$solution_1 = $grid->cells()->filter()->count();

// ==================================================
// > PART 2
// ==================================================
$g = (array) $grid->rows->map(fn ($r) => (array) $r->filter());

for ($i = 0; $i < 100; $i++) {

    $g2 = [];
    foreach (array_keys($g) as $y) foreach (array_keys($g[$y]) as $x) {
        $nei = [];
        foreach ($dir as [$dx, $dy]) $nei[] = [$x + $dx, $y + $dy];

        foreach ($nei as [$nx, $ny]) {
            if (isset($g2[$ny][$nx])) continue;

            $count = 0;
            foreach ($dir as [$dx, $dy]) {
                if (!empty($g[$ny + $dy][$nx + $dx])) $count++;
            };

            $cell = $g[$ny][$nx] ?? false;
            if ($cell && (!$count || $count > 2)) $cell = false;
            if (!$cell && $count == 2) $cell = true;
            $g2[$ny][$nx] = $cell;
        }
    }
    $g = array_map("array_filter", $g2);
}

$solution_2 = set($g)->merge()->count();