I tried to leverage SIMD instructions here, but I’m stuck with the wrong result, damn. The idea is to move a 4x4 window around the grid, turn it into a @Vector(16,u8) and use SIMD to check for XMAS in the first line, first column, downward and upward diagonals.
Solution
const std = @import("std");
const u = @import("util.zig");
const data = @embedFile("data/day04.txt");
fn extract(line: usize, column: usize) @Vector(16, u8) {
u.assert(line < 140 - 3);
u.assert(column < 140 - 3);
var s: [16]u8 = undefined;
for (0..4) |i| {
const start = (line + i) * 141 + column;
@memcpy(
s[4 * i .. 4 * (i + 1)],
data[start .. start + 4],
);
}
return s;
}
const horizontal: @Vector(16, u8) = "XMASXMASXMASXMAS".*;
const horizontal_reverse: @Vector(16, u8) = "SAMXSAMXSAMXSAMX".*;
const vertical: @Vector(16, u8) = "XXXXMMMMAAAASSSS".*;
const vertical_reverse: @Vector(16, u8) = "SSSSAAAAMMMMXXXX".*;
const horizontal_mask: @Vector(16, bool) = .{
true, true, true, true,
false, false, false, false,
false, false, false, false,
false, false, false, false,
};
const vertical_mask: @Vector(16, bool) = .{
true, false, false, false,
true, false, false, false,
true, false, false, false,
true, false, false, false,
};
const downward_mask: @Vector(16, bool) = .{
true, false, false, false,
false, true, false, false,
false, false, true, false,
false, false, false, true,
};
const upward_mask: @Vector(16, bool) = .{
false, false, false, true,
false, false, true, false,
false, true, false, false,
true, false, false, false,
};
const allTrue: @Vector(16, bool) = @splat(true);
fn check(mask: @Vector(16, bool), direction: @Vector(16, u8), square: @Vector(16, u8)) bool {
return @reduce(.And, @select(bool, mask, (square == direction), allTrue));
}
fn count(s: @Vector(16, u8)) usize {
var total: usize = 0;
// horizontal
if (check(horizontal_mask, horizontal, s)) total += 1;
if (check(horizontal_mask, horizontal_reverse, s)) total += 1;
// vertical
if (check(vertical_mask, vertical, s)) total += 1;
if (check(vertical_mask, vertical_reverse, s)) total += 1;
// downward
if (check(downward_mask, horizontal, s)) total += 1;
if (check(downward_mask, horizontal_reverse, s)) total += 1;
// upward
if (check(upward_mask, horizontal, s)) total += 1;
if (check(upward_mask, horizontal_reverse, s)) total += 1;
return total;
}
fn printSquare(s: @Vector(16, u8)) void {
const a: [16]u8 = s;
u.print("{s}\n", .{a[0..4]});
u.print("{s}\n", .{a[4..8]});
u.print("{s}\n", .{a[8..12]});
u.print("{s}\n", .{a[12..16]});
u.print("{}\n\n", .{count(s)});
}
pub fn main() !void {
var total: usize = 0;
for (0..137) |line| {
for (0..137) |col| {
const e = extract(line, col);
printSquare(e);
total += count(e);
}
}
u.print("{any}\n", .{total});
// u.print("{any}\n", .{count("XMAS............".*)});
// u.print("{any}\n", .{count("SAMX............".*)});
// u.print("{any}\n", .{count("X...M...A...S...".*)});
// u.print("{any}\n", .{count("S...A...M...X...".*)});
// u.print("{any}\n", .{count("X....M....A....S".*)});
// u.print("{any}\n", .{count("S....A....M....X".*)});
// u.print("{any}\n", .{count("S...A...M...X...".*)});
// u.print("{any}\n", .{count("X...M...A...S...".*)});
}
EDIT: nevermind, that was silly - the SIMD part is working, I just forgot to add some padding at the end of each line and at the end of the file so I can scan the whole file.