const std = @import("std"); const PATH = "input/day16.txt"; const Str = []const u8; pub fn first(allocator: std.mem.Allocator) !usize { var sues = SueList.init(allocator); defer sues.deinit(); try parseInput(&sues, @embedFile(PATH)); std.debug.assert(sues.items.len == 500); inline for (@typeInfo(Sue).Struct.fields) |fld| { comptime if (std.mem.eql(u8, "id", fld.name)) continue; var next_sues = SueList.init(allocator); for (sues.items) |sue| { if (@field(sue, fld.name)) |val| { if (val == @field(target_sue, fld.name)) { try next_sues.append(sue); } } else { try next_sues.append(sue); } } sues.deinit(); sues = next_sues; } std.debug.assert(sues.items.len == 1); return sues.items[0].id; } pub fn second(allocator: std.mem.Allocator) !usize { var sues = SueList.init(allocator); defer sues.deinit(); try parseInput(&sues, @embedFile(PATH)); std.debug.assert(sues.items.len == 500); inline for (@typeInfo(Sue).Struct.fields) |fld| { comptime if (std.mem.eql(u8, "id", fld.name)) continue; var next_sues = SueList.init(allocator); for (sues.items) |sue| { if (@field(sue, fld.name)) |val| { if (std.mem.eql(u8, "cats", fld.name) or std.mem.eql(u8, "trees", fld.name)) { if (val > @field(target_sue, fld.name).?) try next_sues.append(sue); } else if (std.mem.eql(u8, "pomeranians", fld.name) or std.mem.eql(u8, "goldfish", fld.name)) { if (val < @field(target_sue, fld.name).?) try next_sues.append(sue); } else { if (val == @field(target_sue, fld.name)) { try next_sues.append(sue); } } } else { try next_sues.append(sue); } } sues.deinit(); sues = next_sues; } std.debug.assert(sues.items.len == 1); return sues.items[0].id; } test "day16a" { try std.testing.expectEqual(@as(usize, 373), try first(std.testing.allocator)); } test "day16b" { try std.testing.expectEqual(@as(usize, 260), try second(std.testing.allocator)); } const target_sue: Sue = .{ .id = 0, .children = 3, .cats = 7, .samoyeds = 2, .pomeranians = 3, .akitas = 0, .vizslas = 0, .goldfish = 5, .trees = 3, .cars = 2, .perfumes = 1, }; const SueValue = u4; const Sue = struct { id: u9, children: ?SueValue = null, cats: ?SueValue = null, samoyeds: ?SueValue = null, pomeranians: ?SueValue = null, akitas: ?SueValue = null, vizslas: ?SueValue = null, goldfish: ?SueValue = null, trees: ?SueValue = null, cars: ?SueValue = null, perfumes: ?SueValue = null, }; const SueList = std.ArrayList(Sue); fn parseInput(sl: *SueList, input: Str) !void { var lines = std.mem.tokenize(u8, input, "\n"); while (lines.next()) |line| { var s: Sue = .{ .id = 0 }; const pivot = std.mem.indexOf(u8, line, ":").?; s.id = try std.fmt.parseUnsigned(u9, line[4..pivot], 10); var tags = std.mem.tokenize(u8, line[pivot + 2 ..], ", "); while (tags.next()) |tag| { // std.debug.print("{s}\n", .{tag}); switch (tag[0]) { 'a'...'z' => { const val = try std.fmt.parseUnsigned(SueValue, tags.next().?, 10); switch (tag[0]) { 'a' => s.akitas = val, 'c' => { switch (tag[1]) { 'a' => { switch (tag[2]) { 'r' => s.cars = val, 't' => s.cats = val, else => unreachable, } }, 'h' => s.children = val, else => unreachable, } }, 'g' => s.goldfish = val, 'p' => { switch (tag[1]) { 'e' => s.perfumes = val, 'o' => s.pomeranians = val, else => unreachable, } }, 's' => s.samoyeds = val, 't' => s.trees = val, 'v' => s.vizslas = val, else => unreachable, } }, else => unreachable, } } try sl.append(s); } }