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

const PATH = "input/day13.txt";

const Str = []const u8;

pub fn first(allocator: ?std.mem.Allocator) anyerror!usize {
    const shuttle = try parseInput1(allocator.?, @embedFile(PATH));
    defer allocator.?.free(shuttle.buses);

    var min: usize = std.math.maxInt(usize);
    var min_bus: usize = undefined;

    for (shuttle.buses) |bus| {
        const departures: usize = shuttle.dep / bus;
        const diff = bus * (departures + 1) - shuttle.dep;
        if (diff < min) {
            min = diff;
            min_bus = bus;
        }
    }

    return min * min_bus;
}

pub fn second(allocator: ?std.mem.Allocator) anyerror!usize {
    const buses = try parseInput2(allocator.?, @embedFile(PATH));
    defer allocator.?.free(buses);

    var diff: usize = buses[0].num;
    var candidate: usize = 0;
    for (buses[1..]) |bus| {
        while ((candidate + bus.offset) % bus.num != 0) candidate += diff;
        diff *= bus.num;
    }

    return candidate;
}

const Shuttle = struct {
    dep: usize,
    buses: []usize,
};

fn parseInput1(allocator: std.mem.Allocator, input: Str) !Shuttle {
    var ret: Shuttle = undefined;

    var lines = std.mem.tokenize(u8, input, "\n");

    // parse departure time
    ret.dep = try std.fmt.parseUnsigned(usize, lines.next().?, 0);

    // parse buses
    var bus_array = std.ArrayList(usize).init(allocator);
    var buses = std.mem.tokenize(u8, lines.next().?, ",");
    while (buses.next()) |bus| {
        if (bus[0] == 'x') continue;
        try bus_array.append(try std.fmt.parseUnsigned(usize, bus, 0));
    }
    ret.buses = try bus_array.toOwnedSlice();

    return ret;
}

const Bus = struct {
    offset: usize,
    num: usize,
};

fn parseInput2(allocator: std.mem.Allocator, input: Str) ![]Bus {
    var lines = std.mem.tokenize(u8, input, "\n");
    _ = lines.next();

    var bus_array = std.ArrayList(Bus).init(allocator);
    var buses = std.mem.tokenize(u8, lines.next().?, ",");
    var idx: usize = 0;
    while (buses.next()) |bus| : (idx += 1) {
        if (bus[0] == 'x') continue;
        const b: Bus = .{ .offset = idx, .num = try std.fmt.parseUnsigned(usize, bus, 0) };
        try bus_array.append(b);
    }

    return bus_array.toOwnedSlice();
}

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

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