summaryrefslogtreecommitdiff
path: root/aoc24/src/day4.zig
blob: 13df02dd1d5276535dbc648acf5f7fbb3264f641 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const std = @import("std");

fn cast(n: usize) i32 {
    return @intCast(n);
}

const Input = struct {
    buf: []const u8,
    w: usize,
    h: usize,

    pub fn at(self: Input, x: i32, y: i32) ?u8 {
        if (x < 0 or x >= self.w or y < 0 or y >= self.h) return null;
        return self.buf[@intCast(y * (@as(i32, @intCast(self.w)) + 1) + x)];
    }
};

pub fn parse(_: std.mem.Allocator, data: []const u8) !Input {
    const width = std.mem.indexOf(u8, data, "\n") orelse return error.InvalidInput;
    const height = data.len / (width + 1);
    std.debug.assert((width + 1) * height == data.len);
    return .{ .buf = data, .w = width, .h = height };
}

test "part1" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();

    const output = try part1(arena.allocator(), try parse(arena.allocator(),
        \\MMMSXXMASM
        \\MSAMXMSMSA
        \\AMXSXMAAMM
        \\MSAMASMSMX
        \\XMASAMXAMM
        \\XXAMMXXAMA
        \\SMSMSASXSS
        \\SAXAMASAAA
        \\MAMMMXMMMM
        \\MXMXAXMASX
        \\
    ));
    std.debug.print("got {}\n", .{output});
    std.debug.assert(output == 18);
}

pub fn part1(_: std.mem.Allocator, input: Input) !u32 {
    var count: u32 = 0;
    for (0..input.h) |y| for (0..input.w) |x| for ([_]@Vector(2, i32){
        .{ 1, 0 },
        .{ 1, -1 },
        .{ 0, -1 },
        .{ -1, -1 },
        .{ -1, 0 },
        .{ -1, 1 },
        .{ 0, 1 },
        .{ 1, 1 },
    }) |dir| {
        for (0.., "XMAS") |dist, c| {
            if (input.at(cast(x) + dir[0] * cast(dist), cast(y) + cast(dist) * dir[1]) != c) {
                break;
            }
        } else {
            count += 1;
        }
    };

    return count;
}

test "part2" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();

    const output = try part2(arena.allocator(), try parse(arena.allocator(),
        \\MMMSXXMASM
        \\MSAMXMSMSA
        \\AMXSXMAAMM
        \\MSAMASMSMX
        \\XMASAMXAMM
        \\XXAMMXXAMA
        \\SMSMSASXSS
        \\SAXAMASAAA
        \\MAMMMXMMMM
        \\MXMXAXMASX
        \\
    ));
    std.debug.print("got {}\n", .{output});
    std.debug.assert(output == 9);
}

fn isOther(a: ?u8, b: ?u8) bool {
    return switch (a orelse return false) {
        'M' => b == 'S',
        'S' => b == 'M',
        else => false,
    };
}

pub fn part2(_: std.mem.Allocator, input: Input) !u32 {
    var count: u32 = 0;
    for (0..input.h) |y| for (0..input.w) |x| {
        if (input.at(cast(x), cast(y)) != 'A') continue;
        if (!isOther(input.at(cast(x) - 1, cast(y) - 1), input.at(cast(x) + 1, cast(y) + 1))) continue;
        if (!isOther(input.at(cast(x) + 1, cast(y) - 1), input.at(cast(x) - 1, cast(y) + 1))) continue;
        count += 1;
    };

    return count;
}