aoc-2020-zig

Advent of Code 2020 Solutions in Zig
git clone https://git.sinitax.com/sinitax/aoc-2020-zig
Log | Files | Refs | README | sfeed.txt

main.zig (5617B)


      1// Own twist on https://en.wikipedia.org/wiki/Shunting-yard_algorithm
      2
      3const std = @import("std");
      4const aoc = @import("aoc");
      5
      6const OpType = enum { ADD, MULT, COUNT };
      7const Op = struct { prio: u32, optype: OpType };
      8const Item = union(enum) { num: i64, op: OpType };
      9
     10const op_names = [_][]const u8{ "ADD", "MULT" };
     11
     12fn printStack(stack: []Item) void {
     13    for (stack) |item| {
     14        switch (item) {
     15            .op => aoc.debugfmt("{s} ", .{op_names[@enumToInt(item.op)]}),
     16            .num => aoc.debugfmt("{} ", .{item.num}),
     17        }
     18    }
     19    aoc.debugfmt("\n", .{});
     20}
     21
     22fn parseToken(line: []const u8, i: *usize, depth: *u32, op_prio: []const u32, mathstack: *std.ArrayList(Item), opstack: *std.ArrayList(Op)) !bool {
     23    if (i.* > line.len) {
     24        return false;
     25    } else if (i.* == line.len) {
     26        while (opstack.items.len > 0)
     27            try mathstack.append(Item{ .op = opstack.pop().optype });
     28    } else {
     29        const c = line[i.*];
     30        switch (c) {
     31            '+', '*' => {
     32                const optype = switch (c) {
     33                    '+' => OpType.ADD,
     34                    '*' => OpType.MULT,
     35                    else => unreachable,
     36                };
     37                const prio = depth.* * @enumToInt(OpType.COUNT) + op_prio[@enumToInt(optype)];
     38                while (opstack.items.len > 0 and
     39                    opstack.items[opstack.items.len - 1].prio >= prio)
     40                {
     41                    try mathstack.append(Item{ .op = opstack.pop().optype });
     42                }
     43                try opstack.append(Op{ .optype = optype, .prio = prio });
     44            },
     45            '(' => depth.* += 1,
     46            ')' => depth.* -= 1,
     47            ' ' => {},
     48            else => {
     49                // in this case we know that every number is one digit..
     50                // crappy zig parseInt requires span of exact int size
     51                // before we even know the format >:(((
     52                const val = try std.fmt.parseInt(i64, line[i.* .. i.* + 1], 10);
     53                try mathstack.append(Item{ .num = val });
     54            },
     55        }
     56    }
     57    i.* += 1;
     58    return true;
     59}
     60
     61fn reduceStack(mathstack: *std.ArrayList(Item), opstack: *std.ArrayList(Op), tmpstack: *std.ArrayList(Item)) !void {
     62    _ = opstack;
     63
     64    while (mathstack.items.len > 0) {
     65        while (mathstack.items[mathstack.items.len - 1] != .op and
     66            tmpstack.items.len > 0)
     67        {
     68            try mathstack.append(tmpstack.pop());
     69        }
     70        if (mathstack.items.len < 3 or mathstack.items[mathstack.items.len - 1] != .op)
     71            break;
     72        const op = mathstack.pop();
     73
     74        // get first arg
     75        if (mathstack.items[mathstack.items.len - 1] != .num) {
     76            try tmpstack.append(op);
     77            continue;
     78        }
     79        const a = mathstack.pop();
     80
     81        // get second arg
     82        if (mathstack.items[mathstack.items.len - 1] != .num) {
     83            try tmpstack.append(op);
     84            try tmpstack.append(a);
     85            continue;
     86        }
     87        const b = mathstack.pop();
     88
     89        const result: i64 = switch (op.op) {
     90            OpType.ADD => a.num + b.num,
     91            OpType.MULT => a.num * b.num,
     92            else => unreachable,
     93        };
     94        try mathstack.append(Item{ .num = result });
     95    }
     96}
     97
     98fn printRPN(allocator: std.mem.Allocator, input: []u8, prios: []const u32) !void {
     99    var mathstack = std.ArrayList(Item).init(allocator);
    100    defer mathstack.deinit();
    101
    102    var opstack = std.ArrayList(Op).init(allocator);
    103    defer opstack.deinit();
    104
    105    var lineit = std.mem.tokenize(u8, input, "\n");
    106    while (lineit.next()) |line| {
    107        var depth: u32 = 0;
    108        var i: usize = 0;
    109        while (try parseToken(line, &i, &depth, prios, &mathstack, &opstack)) {}
    110    }
    111
    112    printStack(mathstack.items);
    113}
    114
    115fn partProto(allocator: std.mem.Allocator, input: []u8, prios: []const u32) !?[]u8 {
    116    var answer: i64 = 0;
    117
    118    if (aoc.debug) {
    119        aoc.debugfmt("Reverse Polish Notation:\n", .{});
    120        try printRPN(allocator, input, prios);
    121    }
    122
    123    var mathstack = std.ArrayList(Item).init(allocator);
    124    defer mathstack.deinit();
    125
    126    var opstack = std.ArrayList(Op).init(allocator);
    127    defer opstack.deinit();
    128
    129    var tmpstack = std.ArrayList(Item).init(allocator);
    130    defer tmpstack.deinit();
    131
    132    var lineit = std.mem.tokenize(u8, input, "\n");
    133    while (lineit.next()) |line| {
    134        var depth: u32 = 0;
    135        var i: usize = 0;
    136        while (try parseToken(line, &i, &depth, prios, &mathstack, &opstack)) {
    137            try reduceStack(&mathstack, &opstack, &tmpstack);
    138        }
    139
    140        if (mathstack.items.len != 1) return aoc.Error.InvalidInput;
    141        const last = mathstack.items[mathstack.items.len - 1];
    142        if (last != .num) return aoc.Error.InvalidInput;
    143        answer += last.num;
    144
    145        try mathstack.resize(0);
    146        try opstack.resize(0);
    147        try tmpstack.resize(0);
    148    }
    149
    150    return try std.fmt.allocPrint(allocator, "{}", .{answer});
    151}
    152
    153fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 {
    154    _ = args;
    155
    156    const prios = [_]u32{ 1, 1 };
    157
    158    return try partProto(allocator, input, prios[0..]);
    159}
    160
    161fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 {
    162    _ = args;
    163
    164    const prios = [_]u32{ 2, 1 };
    165
    166    return try partProto(allocator, input, prios[0..]);
    167}
    168
    169test "unions" {
    170    const item1 = Item{ .op = OpType.MULT };
    171    const item2 = Item{ .num = 1000 };
    172    const expect = std.testing.expect;
    173    expect(item1 == .op);
    174    expect(item2 == .num);
    175}
    176
    177pub const main = aoc.main(part1, part2, .{ "5783053349377", "74821486966872" });