Advent of Code 2020 solutions in Zig
const std = @import("std");

const PATH = "input/day02.txt";

pub fn first(allocator: ?std.mem.Allocator) anyerror!usize {
    _ = allocator;

    const file = @embedFile(PATH);
    var lines = std.mem.tokenize(u8, file, "\n");

    var good_pw: usize = 0;

    while (lines.next()) |line| {
        var pw = std.mem.tokenize(u8, line, ": ");

        const pw_minmax = pw.next().?;
        var mm = std.mem.tokenize(u8, pw_minmax, "-");
        const min = try std.fmt.parseUnsigned(usize, mm.next().?, 0);
        const max = try std.fmt.parseUnsigned(usize, mm.next().?, 0);

        const pw_char = pw.next().?[0];
        const pw_str = pw.next().?;

        var counter: usize = 0;
        for (pw_str) |ch| {
            if (ch == pw_char) counter += 1;
        }

        if (counter >= min and counter <= max) good_pw += 1;
    }

    return good_pw;
}

pub fn second(allocator: ?std.mem.Allocator) anyerror!usize {
    _ = allocator;

    const file = @embedFile(PATH);
    var lines = std.mem.tokenize(u8, file, "\n");

    var good_pw: usize = 0;

    while (lines.next()) |line| {
        var pw = std.mem.tokenize(u8, line, ": ");

        const pw_minmax = pw.next().?;
        var mm = std.mem.tokenize(u8, pw_minmax, "-");
        const pos1 = try std.fmt.parseUnsigned(usize, mm.next().?, 0);
        const pos2 = try std.fmt.parseUnsigned(usize, mm.next().?, 0);

        const pw_char = pw.next().?[0];
        const pw_str = pw.next().?;

        if ((pw_str[pos1 - 1] == pw_char and pw_str[pos2 - 1] != pw_char) or
            (pw_str[pos1 - 1] != pw_char and pw_str[pos2 - 1] == pw_char)) good_pw += 1;
    }

    return good_pw;
}

test "day02a" {
    try std.testing.expectEqual(@as(usize, 572), try first(std.testing.allocator));
}

test "day02b" {
    try std.testing.expectEqual(@as(usize, 306), try second(std.testing.allocator));
}