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" });