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

const PATH = "input/day03.txt";
const ROWS = 323;
const COLS = 31;

const Map = std.StaticBitSet(ROWS * COLS);

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

    const map = parseInput();

    const move = [2]usize{ 1, 3 };

    var row: usize = 0;
    var col: usize = 0;
    var trees: usize = 0;
    while (row < ROWS) : ({
        row += move[0];
        col += move[1];
    }) {
        // std.debug.print("{d} {d} {}\n", .{ row, col % COLS, map.isSet(row * COLS + col % COLS) });
        if (map.isSet(row * COLS + col % COLS)) trees += 1;
    }

    return trees;
}

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

    const map = parseInput();

    const moves = [_][2]usize{
        .{ 1, 3 },
        .{ 1, 1 },
        .{ 1, 5 },
        .{ 1, 7 },
        .{ 2, 1 },
    };

    var ret: usize = 1;
    for (moves) |move| {
        var row: usize = 0;
        var col: usize = 0;
        var trees: usize = 0;
        while (row < ROWS) : ({
            row += move[0];
            col += move[1];
        }) {
            // std.debug.print("{d} {d} {}\n", .{ row, col % COLS, map.isSet(row * COLS + col % COLS) });
            if (map.isSet(row * COLS + col % COLS)) trees += 1;
        }
        ret *= trees;
    }

    return ret;
}

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

    var map = Map.initEmpty();

    var row: usize = 0;
    while (lines.next()) |line| : (row += 1) {
        for (line) |ch, col| {
            // std.debug.print("{d} {d} {s}\n", .{row, col, line});
            if (ch == '#') map.set(row * COLS + col);
        }
    }

    return map;
}

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

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