diff options
Diffstat (limited to 'day03/solution.zig')
| -rw-r--r-- | day03/solution.zig | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/day03/solution.zig b/day03/solution.zig new file mode 100644 index 0000000..b98260a --- /dev/null +++ b/day03/solution.zig | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | const std = @import("std"); | ||
| 2 | const print = std.debug.print; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const ArrayList = std.ArrayList; | ||
| 5 | const mem = std.mem; | ||
| 6 | const isDigit = std.ascii.isDigit; | ||
| 7 | const Self = @This(); | ||
| 8 | |||
| 9 | const fin = mem.trim(u8, @embedFile("./input.txt"), &std.ascii.whitespace); | ||
| 10 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 11 | const allocator = gpa.allocator(); | ||
| 12 | const MapOfNumDesc = std.HashMap(NumDesc, void, NumDescCtx, 80); | ||
| 13 | |||
| 14 | const NumDesc = struct { | ||
| 15 | i: u64, | ||
| 16 | left: u64, | ||
| 17 | right: u64, | ||
| 18 | }; | ||
| 19 | |||
| 20 | const NumDescCtx = struct { | ||
| 21 | pub fn hash(self: @This(), K: NumDesc) u64 { | ||
| 22 | _ = self; | ||
| 23 | return K.left | (K.right << 32); | ||
| 24 | } | ||
| 25 | |||
| 26 | pub fn eql(self: @This(), K1: NumDesc, K2: NumDesc) bool { | ||
| 27 | _ = self; | ||
| 28 | // Checking .i and .left can uniquely identify the number in the grid | ||
| 29 | return K1.i == K2.i and K1.left == K2.left; | ||
| 30 | } | ||
| 31 | }; | ||
| 32 | |||
| 33 | fn isSymbol(c: u8) bool { | ||
| 34 | return c != '.' and !std.ascii.isDigit(c); | ||
| 35 | } | ||
| 36 | |||
| 37 | fn isGear(c: u8) bool { | ||
| 38 | return c == '*'; | ||
| 39 | } | ||
| 40 | |||
| 41 | fn getNumberAt(map: ArrayList([]const u8), i: u64, j: u64) NumDesc { | ||
| 42 | var left: u64 = j; | ||
| 43 | var right: u64 = j; | ||
| 44 | |||
| 45 | // Lookahead checking to avoid dropping below 0 | ||
| 46 | while (left > 0 and | ||
| 47 | isDigit(map.items[i][left - 1])) : (left -= 1) | ||
| 48 | {} | ||
| 49 | |||
| 50 | while (right < map.items.len and | ||
| 51 | isDigit(map.items[i][right])) : (right += 1) | ||
| 52 | {} | ||
| 53 | |||
| 54 | return NumDesc{ | ||
| 55 | .i = i, | ||
| 56 | .left = left, | ||
| 57 | .right = right, | ||
| 58 | }; | ||
| 59 | } | ||
| 60 | |||
| 61 | // Evaluated numbers are managed by the caller for more flexibility. | ||
| 62 | // P1: Each number is only evaluated once | ||
| 63 | // P2: Each number may be evaluated an arbitrary number of times | ||
| 64 | fn findAdjacent(map: ArrayList([]const u8), i: u64, j: u64, evaluated: *MapOfNumDesc) ArrayList(u64) { | ||
| 65 | var surroundNums = ArrayList(u64).init(allocator); | ||
| 66 | |||
| 67 | for (@max(i - 1, 0)..@min(i + 2, map.items.len)) |ai| { | ||
| 68 | for (@max(j - 1, 0)..@min(j + 2, map.items[0].len)) |aj| { | ||
| 69 | if (!isDigit(map.items[ai][aj])) { | ||
| 70 | continue; | ||
| 71 | } | ||
| 72 | const p = getNumberAt(map, ai, aj); | ||
| 73 | const s = map.items[p.i][p.left..p.right]; | ||
| 74 | const n = std.fmt.parseInt(u64, s, 10) catch unreachable; | ||
| 75 | |||
| 76 | if (evaluated.contains(p)) { | ||
| 77 | continue; | ||
| 78 | } | ||
| 79 | evaluated.put(p, undefined) catch unreachable; | ||
| 80 | surroundNums.append(n) catch unreachable; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | return surroundNums; | ||
| 85 | } | ||
| 86 | |||
| 87 | fn part1(map: ArrayList([]const u8)) void { | ||
| 88 | var ans: u64 = 0; | ||
| 89 | var evaluated = MapOfNumDesc.init(allocator); | ||
| 90 | defer evaluated.deinit(); | ||
| 91 | |||
| 92 | for (map.items, 0..) |line, i| { | ||
| 93 | for (line, 0..) |c, j| { | ||
| 94 | if (isSymbol(c)) { | ||
| 95 | const adj = findAdjacent(map, i, j, &evaluated); | ||
| 96 | |||
| 97 | for (adj.items) |adj_num| { | ||
| 98 | ans += adj_num; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | print("{d}\n", .{ans}); | ||
| 105 | } | ||
| 106 | |||
| 107 | fn part2(map: ArrayList([]const u8)) void { | ||
| 108 | var ans: u64 = 0; | ||
| 109 | var evaluated = MapOfNumDesc.init(allocator); | ||
| 110 | defer evaluated.deinit(); | ||
| 111 | |||
| 112 | for (map.items, 0..) |line, i| { | ||
| 113 | for (line, 0..) |c, j| { | ||
| 114 | if (isGear(c)) { | ||
| 115 | const adj = findAdjacent(map, i, j, &evaluated); | ||
| 116 | evaluated.clearRetainingCapacity(); | ||
| 117 | |||
| 118 | if (adj.items.len == 2) { | ||
| 119 | ans += adj.items[0] * adj.items[1]; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | print("{d}\n", .{ans}); | ||
| 126 | } | ||
| 127 | |||
| 128 | pub fn main() !void { | ||
| 129 | var splitLines = mem.splitScalar(u8, fin, '\n'); | ||
| 130 | var lines = ArrayList([]const u8).init(allocator); | ||
| 131 | |||
| 132 | while (splitLines.next()) |line| { | ||
| 133 | try lines.append(line); | ||
| 134 | } | ||
| 135 | |||
| 136 | part1(lines); | ||
| 137 | part2(lines); | ||
| 138 | } | ||
