const std = @import("std"); const Names = std.ArrayList(u24); const Graph = std.AutoHashMap(u24, struct { count: usize, level: usize, outputs: Names, }); fn decode(s: []const u8) u24 { return ((@as(u24, s[0]) << 0) + (@as(u24, s[1]) << 8) + (@as(u24, s[2]) << 16)); } const YOU = decode("you"); const SVR = decode("svr"); const DAC = decode("dac"); const FFT = decode("fft"); const OUT = decode("out"); fn setLevels(allocator: std.mem.Allocator, graph: *Graph) !void { // TODO: we are assuming here that SVR is the root of the DAC var queue = std.ArrayList(struct { u24, usize }).empty; defer queue.deinit(allocator); try queue.append(allocator, .{ SVR, 1 }); while (queue.pop()) |item| { const name, const level = item; var value = graph.getPtr(name).?; if (level > value.level) { value.level = level; for (value.outputs.items) |output| { try queue.append(allocator, .{ output, level + 1 }); } } } } fn countPaths(graph: *Graph, start: u24, end: u24) !usize { var g = try graph.clone(); defer g.deinit(); var s = g.getPtr(start).?; const e = g.getPtr(end).?; s.count = 1; for (s.level..e.level) |level| { var iterator = g.iterator(); while (iterator.next()) |entry| { if (entry.value_ptr.level == level) { for (entry.value_ptr.outputs.items) |output| { var o = g.getPtr(output).?; o.count += entry.value_ptr.count; } } } } return e.count; } pub fn main() !void { const allocator = std.heap.smp_allocator; const path: [:0]const u8 = std.mem.span(std.os.argv[1]); var file = try std.fs.cwd().openFile(path, .{}); defer file.close(); var buffer: [1024]u8 = undefined; var reader = file.reader(&buffer); var graph = Graph.init(allocator); defer { var iterator = graph.iterator(); while (iterator.next()) |entry| { entry.value_ptr.outputs.deinit(allocator); } graph.deinit(); } while (reader.interface.peekDelimiterExclusive('\n')) |line| { reader.interface.toss(line.len + 1); const name = decode(line[0..3]); var outputs = Names.empty; var i: usize = 5; while (i < line.len) { try outputs.append(allocator, decode(line[i .. i + 3])); i += 4; } try graph.put(name, .{ .count = 0, .level = 0, .outputs = outputs, }); } else |err| switch (err) { error.EndOfStream => {}, else => |e| return e, } try graph.put(OUT, .{ .count = 0, .level = 0, .outputs = Names.empty, }); try setLevels(allocator, &graph); const part1 = try countPaths(&graph, YOU, OUT); std.debug.print("part1: {}\n", .{part1}); var part2: usize = 1; if (graph.get(DAC).?.level < graph.get(FFT).?.level) { part2 *= try countPaths(&graph, SVR, DAC); part2 *= try countPaths(&graph, DAC, FFT); part2 *= try countPaths(&graph, FFT, OUT); } else { part2 *= try countPaths(&graph, SVR, FFT); part2 *= try countPaths(&graph, FFT, DAC); part2 *= try countPaths(&graph, DAC, OUT); } std.debug.print("part2: {}\n", .{part2}); }