const std = @import("std"); const Str = []const u8; const PATH = "input/day09.txt"; const CostType = u10; const RouteItem = struct { from: Str, to: Str, cost: CostType, }; const Routes = []RouteItem; const Visited = std.StringHashMap(void); pub fn first(allocator: std.mem.Allocator) !usize { var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); var routes = try parseRoutes(allocator, @embedFile(PATH)); defer allocator.free(routes); var min: usize = std.math.maxInt(usize); var i: usize = 0; while (i < 8) : (i += 1) { var visited = Visited.init(arena.allocator()); defer visited.deinit(); var sum: usize = 0; var next: Str = if (i == 0) routes[i].from else routes[i - 1].to; while (visited.count() != 7) { try visited.put(next, {}); var shortest: struct { city: Str, cost: CostType } = .{ .city = undefined, .cost = std.math.maxInt(CostType) }; for (routes) |route| { const dest = if (std.mem.eql(u8, next, route.from)) route.to else if (std.mem.eql(u8, next, route.to)) route.from else continue; if (visited.contains(dest)) continue; if (route.cost < shortest.cost) { shortest.cost = route.cost; shortest.city = try std.fmt.allocPrint(arena.allocator(), "{s}", .{dest}); } } sum += shortest.cost; next = shortest.city; } if (sum < min) min = sum; } return min; } pub fn second(allocator: std.mem.Allocator) !usize { var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); var routes = try parseRoutes(allocator, @embedFile(PATH)); defer allocator.free(routes); var max: usize = 0; var i: usize = 0; while (i < 8) : (i += 1) { var visited = Visited.init(arena.allocator()); defer visited.deinit(); var sum: usize = 0; var next: Str = if (i == 0) routes[i].from else routes[i - 1].to; while (visited.count() != 7) { try visited.put(next, {}); var longest: struct { city: Str, cost: CostType } = .{ .city = undefined, .cost = 0 }; for (routes) |route| { const dest = if (std.mem.eql(u8, next, route.from)) route.to else if (std.mem.eql(u8, next, route.to)) route.from else continue; if (visited.contains(dest)) continue; if (route.cost > longest.cost) { longest.cost = route.cost; longest.city = try std.fmt.allocPrint(arena.allocator(), "{s}", .{dest}); } } sum += longest.cost; next = longest.city; } if (sum > max) max = sum; } return max; } fn parseRoutes(allocator: std.mem.Allocator, input: Str) !Routes { var routes = std.ArrayList(RouteItem).init(allocator); defer routes.deinit(); var lines = std.mem.tokenize(u8, input, "\n"); while (lines.next()) |line| { var words = std.mem.tokenize(u8, line, " "); const from = words.next().?; _ = words.next(); // drop 'to' const to = words.next().?; _ = words.next(); // drop '=' const cost = try std.fmt.parseUnsigned(CostType, words.next().?, 10); try routes.append(RouteItem{ .from = from, .to = to, .cost = cost }); } return routes.toOwnedSlice(); } test "day09a" { try std.testing.expectEqual(@as(usize, 251), try first(std.testing.allocator)); } test "day09b" { try std.testing.expectEqual(@as(usize, 898), try second(std.testing.allocator)); }