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

const Str = []const u8;

const PATH = "input/day04.txt";

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

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

    var counter: usize = 0;

    var passport_bits: u7 = 0;
    while (lines.next()) |line| {
        if (std.mem.eql(u8, line, "")) {
            if (passport_bits == 0b1111111) counter += 1;
            passport_bits = 0; // new passport, zeroing checked bits
        } else {
            var items = std.mem.tokenize(u8, line, " ");
            while (items.next()) |item| {
                var parts = std.mem.tokenize(u8, item, ":");
                const field = parts.next().?;
                if (std.mem.eql(u8, field, "byr")) {
                    passport_bits |= 1 << 0;
                } else if (std.mem.eql(u8, field, "iyr")) {
                    passport_bits |= 1 << 1;
                } else if (std.mem.eql(u8, field, "eyr")) {
                    passport_bits |= 1 << 2;
                } else if (std.mem.eql(u8, field, "hgt")) {
                    passport_bits |= 1 << 3;
                } else if (std.mem.eql(u8, field, "hcl")) {
                    passport_bits |= 1 << 4;
                } else if (std.mem.eql(u8, field, "ecl")) {
                    passport_bits |= 1 << 5;
                } else if (std.mem.eql(u8, field, "pid")) {
                    passport_bits |= 1 << 6;
                } else if (std.mem.eql(u8, field, "cid")) {
                    // does not matter
                } else {
                    unreachable;
                }
            }
        }
    }

    return counter;
}

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

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

    var counter: usize = 0;

    var passport_bits: u7 = 0;
    while (lines.next()) |line| {
        if (std.mem.eql(u8, line, "")) {
            if (passport_bits == 0b1111111) counter += 1;
            passport_bits = 0; // new passport, zeroing checked bits
        } else {
            var items = std.mem.tokenize(u8, line, " ");
            items: while (items.next()) |item| {
                var parts = std.mem.tokenize(u8, item, ":");
                const field = parts.next().?;
                if (std.mem.eql(u8, field, "byr")) {
                    const byr = try std.fmt.parseUnsigned(usize, parts.next().?, 0);
                    if (byr >= 1920 and byr <= 2002) passport_bits |= 1 << 0;
                } else if (std.mem.eql(u8, field, "iyr")) {
                    const iyr = try std.fmt.parseUnsigned(usize, parts.next().?, 0);
                    if (iyr >= 2010 and iyr <= 2020) passport_bits |= 1 << 1;
                } else if (std.mem.eql(u8, field, "eyr")) {
                    const eyr = try std.fmt.parseUnsigned(usize, parts.next().?, 0);
                    if (eyr >= 2020 and eyr <= 2030) passport_bits |= 1 << 2;
                } else if (std.mem.eql(u8, field, "hgt")) {
                    var hgt = parts.next().?;
                    if (std.mem.endsWith(u8, hgt, "cm")) {
                        hgt = hgt[0 .. hgt.len - 2];
                        const val = try std.fmt.parseUnsigned(usize, hgt, 0);
                        if (val >= 150 and val <= 193) passport_bits |= 1 << 3;
                    } else if (std.mem.endsWith(u8, hgt, "in")) {
                        hgt = hgt[0 .. hgt.len - 2];
                        const val = try std.fmt.parseUnsigned(usize, hgt, 0);
                        if (val >= 59 and val <= 76) passport_bits |= 1 << 3;
                    } else {
                        // do nothing => invalid
                    }
                } else if (std.mem.eql(u8, field, "hcl")) {
                    var hcl = parts.next().?;
                    if (hcl[0] == '#') hcl = hcl[1..] else continue;
                    if (hcl.len != 6) continue;
                    for (hcl) |ch| {
                        if (!(ch >= 48 and ch <= 57) and !(ch >= 97 and ch <= 102)) continue :items;
                    }
                    passport_bits |= 1 << 4;
                } else if (std.mem.eql(u8, field, "ecl")) {
                    const ecl = parts.next().?;
                    for ([_]Str{ "amb", "blu", "brn", "gry", "grn", "hzl", "oth" }) |eye_color| {
                        if (std.mem.eql(u8, ecl, eye_color)) {
                            passport_bits |= 1 << 5;
                            continue :items;
                        }
                    }
                } else if (std.mem.eql(u8, field, "pid")) {
                    const pid = parts.next().?;
                    if (pid.len != 9) continue;
                    for (pid) |ch| {
                        if (ch < 48 or ch > 57) continue :items;
                    }
                    passport_bits |= 1 << 6;
                } else if (std.mem.eql(u8, field, "cid")) {
                    // does not matter
                } else {
                    unreachable;
                }
            }
        }
    }

    return counter;
}

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

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