const std = @import("std"); const print = std.debug.print; const Node = u16; const Route = []Node; const Edge = struct { left: Node, right: Node }; const START_NODE = 0x0000; const END_NODE = 0xffff; fn parseNode(input: []const u8) Node { if (std.mem.eql(u8, input, "start")) return START_NODE; if (std.mem.eql(u8, input, "end")) return END_NODE; if (input.len == 1) return @intCast(u16, input[0]) << 8 | 0x20; return @intCast(u16, input[0]) << 8 | input[1]; } fn isThereASmolDuplicate(list: []Node) bool { for (list) |node| { if (isSmol(node) and count(list, node) > 1) { return true; } } return false; } fn count(list: []Node, x: Node) u32 { var res: u32 = 0; for (list) |node| { if (node == x) { res += 1; if (res == 2) { return 2; } } } return res; } fn isForbiddenSmol(list: []Node, x: Node) bool { if (isThereASmolDuplicate(list)) { for (list) |node| { if (node == x) { return true; } } return false; } else if (x == START_NODE) { return true; } else { return count(list, x) > 1; } } fn getOther(edge: Edge, node: Node) ?Node { if (edge.left == node) { return edge.right; } if (edge.right == node) { return edge.left; } return null; } fn isSmol(node: Node) bool { return (node & 0x2020) == 0x2020 or node == START_NODE or node == END_NODE; } pub fn main() !void { var alloc = std.heap.GeneralPurposeAllocator(.{}){}; defer std.debug.assert(!alloc.deinit()); var gpa = &alloc.allocator; const stdin = std.io.getStdIn().reader(); var res: u32 = 0; var edges = std.ArrayList(Edge).init(gpa); defer edges.deinit(); var input = try stdin.readAllAlloc(gpa, 4096); defer gpa.free(input); var lines = std.mem.split(input, "\n"); while (lines.next()) |line| { var it = std.mem.split(line, "-"); var left = it.next() orelse unreachable; var right = it.next() orelse unreachable; var leftInt = parseNode(left); var rightInt = parseNode(right); try edges.append(Edge{ .left = leftInt, .right = rightInt, }); } var routes = std.ArrayList(Route).init(gpa); defer { for (routes.items) |route| { gpa.free(route); } routes.deinit(); } { var route = try gpa.alloc(Node, 1); route[0] = START_NODE; try routes.append(route); } var x: u8 = 0; while (x < 1000) : (x += 1) { var cont = false; var newRoutes = std.ArrayList(Route).init(gpa); defer newRoutes.deinit(); var stale = std.ArrayList(usize).init(gpa); defer stale.deinit(); // print("Routes ({}):\n", .{routes.items.len}); // for (routes.items) |route| { // print("{any}\n", .{route.items}); // } for (routes.items) |route, i| { const last = route[route.len - 1]; if (last == END_NODE) { continue; } cont = true; try stale.append(i); for (edges.items) |edge| { if (getOther(edge, last)) |other| { // print("> {s}\n", .{other}); if (isSmol(other) and isForbiddenSmol(route, other)) { continue; } else { var newRoute = try gpa.alloc(Node, route.len+1); std.mem.copy(Node, newRoute, route); newRoute[route.len] = other; // print("new route: {s}\n", .{newRoute.items}); try newRoutes.append(newRoute); } } } } // print("Removed routes: {}\n", .{stale.items.len}); // print("Added routes: {}\n", .{newRoutes.items.len}); var i: usize = 0; while (i < stale.items.len) : (i += 1) { var route = routes.swapRemove(stale.items[stale.items.len-1-i]); gpa.free(route); } try routes.appendSlice(newRoutes.items); if (!cont) break; } std.debug.print("{}\n", .{routes.items.len}); }