-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path20.zig
243 lines (206 loc) · 8 KB
/
20.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
// I cheated btw. If anyone was wondering. https://www.youtube.com/watch?v=VaKpsB9n6rk
const std = @import("std");
pub fn main() !void {
var prod: u128 = 1;
var wires = try Wires.new(@embedFile("20.txt"), std.heap.page_allocator);
defer wires.deinit();
for (try wires.get_parents("rx", 3)) |in| {
wires.reset();
while (wires.cycle.get(in) == null) {
try wires.press_button();
}
// std.debug.print("{s} {d}\n", .{ in, wires.cycle.get(in).? });
prod = lcm(prod, wires.cycle.get(in).?);
}
std.debug.print("{d}\n", .{prod});
}
pub fn lcm(a: u128, b: u128) u128 {
return a * b / std.math.gcd(a, b);
}
const Wires = struct {
module_type: std.StringHashMap(Module),
inputs: std.StringHashMap(std.ArrayList([]const u8)),
outputs: std.StringHashMap(std.ArrayList([]const u8)),
memory: std.StringHashMap(PulseType),
cycle: std.StringHashMap(usize),
queue: Queue,
presses: usize,
pub fn new(input: []const u8, allocator: std.mem.Allocator) !@This() {
const queue = Queue.new(allocator);
var memory = std.StringHashMap(PulseType).init(allocator);
var module_type = std.StringHashMap(Module).init(allocator);
var inputs = std.StringHashMap(std.ArrayList([]const u8)).init(allocator);
var outputs = std.StringHashMap(std.ArrayList([]const u8)).init(allocator);
const cycle = std.StringHashMap(usize).init(allocator);
var lines = std.mem.splitScalar(u8, input, '\n');
while (lines.next()) |line| {
if (line.len == 0) continue;
var arrow_split = std.mem.splitSequence(u8, line, " -> ");
const label, const module_type_ = switch (line[0]) {
'b' => .{ arrow_split.next().?, Module.Broadcaster },
'%' => .{ arrow_split.next().?[1..], Module.FlipFlop },
'&' => .{ arrow_split.next().?[1..], Module.Conjunction },
else => .{ arrow_split.next().?, Module.Unknown },
};
try outputs.put(label, std.ArrayList([]const u8).init(allocator));
var outputs_ = std.mem.splitSequence(u8, arrow_split.next().?, ", ");
while (outputs_.next()) |output| {
try outputs.getPtr(label).?.append(output);
// For modules not listed, such as rx
if (module_type.get(output) == null) {
try module_type.put(output, Module.Unknown);
try outputs.put(output, std.ArrayList([]const u8).init(allocator));
}
// Adding as input to the receiving end
if (inputs.getPtr(output)) |in| {
try in.append(label);
} else {
try inputs.put(output, std.ArrayList([]const u8).init(allocator));
try inputs.getPtr(output).?.append(label);
}
}
try module_type.put(label, module_type_);
try memory.put(label, low);
// If it has no outputs/inputs, add an empty list. For simplicities sake
if (outputs.get(label) == null) try outputs.put(label, std.ArrayList([]const u8).init(allocator));
if (inputs.get(label) == null) try inputs.put(label, std.ArrayList([]const u8).init(allocator));
}
return @This(){ .module_type = module_type, .inputs = inputs, .outputs = outputs, .memory = memory, .cycle = cycle, .queue = queue, .presses = 0 };
}
pub fn get_parents(self: *@This(), child: []const u8, depth: usize) ![][]const u8 {
var children = std.ArrayList([]const u8).init(self.module_type.allocator);
var parents = std.ArrayList([]const u8).init(self.module_type.allocator);
defer children.deinit();
try children.append(child);
for (0..depth) |_| {
parents.clearAndFree();
while (children.popOrNull()) |c| {
try parents.appendSlice(self.inputs.get(c).?.items);
}
children.clearAndFree();
try children.appendSlice(parents.items);
}
return parents.items;
}
pub fn reset(self: *@This()) void {
self.queue.clear();
self.cycle.clearAndFree();
var it = self.memory.keyIterator();
while (it.next()) |label| {
self.memory.getPtr(label.*).?.* = low;
}
self.presses = 0;
}
pub fn deinit(self: *@This()) void {
self.module_type.deinit();
self.cycle.deinit();
self.inputs.deinit();
self.outputs.deinit();
self.memory.deinit();
self.queue.deinit();
}
pub fn press_button(self: *@This()) !void {
try self.queue.list.append(Pulse{ .to = "broadcaster", .type = low });
self.presses += 1;
while (self.queue.items().len != 0) {
for (self.queue.items()) |pulse| {
// if (pulse.type == high) std.debug.print("-high-> {s}\n", .{pulse.to}) else std.debug.print("-low-> {s}\n", .{pulse.to});
try self.process_pulse(pulse);
}
try self.queue.update();
}
}
pub fn response(self: *@This(), pulse: Pulse) !?PulseType {
switch (self.module_type.get(pulse.to).?) {
.Broadcaster => return pulse.type,
.FlipFlop => {
if (pulse.type == low) {
return self.memory.getPtr(pulse.to).?.*.opposite();
} else {
return null;
}
},
.Conjunction => {
for (self.inputs.get(pulse.to).?.items) |in| {
if (self.memory.get(in).? == low) {
return high;
}
}
try self.cycle.put(pulse.to, self.presses);
return low;
},
.Unknown => return null,
}
}
pub fn process_pulse(self: *@This(), pulse: Pulse) !void {
if (try self.response(pulse)) |pulse_type| {
self.memory.getPtr(pulse.to).?.* = pulse_type;
for (self.outputs.get(pulse.to).?.items) |output| {
try self.queue.append(Pulse{ .to = output, .type = pulse_type });
}
}
}
pub fn print(self: @This()) !void {
var labels = self.module_type.keyIterator();
while (labels.next()) |label_| {
const label = label_.*;
std.debug.print("{s} \x1b[1m-> {s} ->\x1b[0m {s}\n", .{
try std.mem.join(self.module_type.allocator, ", ", self.inputs.get(label).?.items),
label,
try std.mem.join(self.module_type.allocator, ", ", self.outputs.get(label).?.items),
});
}
}
};
const Queue = struct {
list: std.ArrayList(Pulse),
next: std.ArrayList(Pulse),
pub fn new(allocator: std.mem.Allocator) @This() {
return @This(){
.list = std.ArrayList(Pulse).init(allocator),
.next = std.ArrayList(Pulse).init(allocator),
};
}
// Add pulses to send, use update for this to take effect
pub fn append(self: *@This(), pulse: Pulse) !void {
try self.next.append(pulse);
}
// Add the new pulses and remove the old ones.
pub fn update(self: *@This()) !void {
self.list = try self.next.clone();
self.next.clearAndFree();
}
pub fn items(self: @This()) []Pulse {
return self.list.items;
}
pub fn clear(self: *@This()) void {
self.list.clearAndFree();
self.next.clearAndFree();
}
pub fn deinit(self: *@This()) void {
self.list.deinit();
self.next.deinit();
}
};
const Pulse = struct {
to: []const u8,
type: PulseType,
};
const high = PulseType.high;
const low = PulseType.low;
const PulseType = enum {
low,
high,
pub fn opposite(self: @This()) @This() {
return switch (self) {
.low => PulseType.high,
.high => PulseType.low,
};
}
};
const Module = enum {
Unknown,
Broadcaster,
FlipFlop,
Conjunction,
};