aboutsummaryrefslogtreecommitdiffstats
path: root/day03/solution.zig
diff options
context:
space:
mode:
authorOrfeas <38209077+0xfea5@users.noreply.github.com>2023-12-03 09:43:22 +0200
committerOrfeas <38209077+0xfea5@users.noreply.github.com>2025-10-28 23:20:45 +0200
commit6c6abed6cb4f0b6de4fcd065af38027d86fc82c0 (patch)
treea884a38300ccc95bb8412b7c3020b8418ca80cf4 /day03/solution.zig
parentday2 + example files (diff)
downloadaoc23-6c6abed6cb4f0b6de4fcd065af38027d86fc82c0.tar.gz
aoc23-6c6abed6cb4f0b6de4fcd065af38027d86fc82c0.zip
day3
Diffstat (limited to 'day03/solution.zig')
-rw-r--r--day03/solution.zig138
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 @@
1const std = @import("std");
2const print = std.debug.print;
3const assert = std.debug.assert;
4const ArrayList = std.ArrayList;
5const mem = std.mem;
6const isDigit = std.ascii.isDigit;
7const Self = @This();
8
9const fin = mem.trim(u8, @embedFile("./input.txt"), &std.ascii.whitespace);
10var gpa = std.heap.GeneralPurposeAllocator(.{}){};
11const allocator = gpa.allocator();
12const MapOfNumDesc = std.HashMap(NumDesc, void, NumDescCtx, 80);
13
14const NumDesc = struct {
15 i: u64,
16 left: u64,
17 right: u64,
18};
19
20const 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
33fn isSymbol(c: u8) bool {
34 return c != '.' and !std.ascii.isDigit(c);
35}
36
37fn isGear(c: u8) bool {
38 return c == '*';
39}
40
41fn 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
64fn 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
87fn 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
107fn 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
128pub 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}