main.zig (6732B)
1const std = @import("std"); 2const aoc = @import("aoc"); 3 4const hasher = std.hash.CityHash32; 5const IngredientInfo = struct { 6 count: u32, 7}; 8const Pair = struct { ingredient: []const u8, allergen: []const u8 }; 9 10fn freeListmap(listmap: *std.AutoHashMap(u32, std.ArrayList(u32))) void { 11 var mapit = listmap.iterator(); 12 while (mapit.next()) |kv| { 13 kv.value_ptr.deinit(); 14 } 15 listmap.deinit(); 16} 17 18fn getAllergen(allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), ingredient: u32) ?u32 { 19 var allergenit = allergens.iterator(); 20 while (allergenit.next()) |alg| { 21 if (alg.value_ptr.items.len == 1 and alg.value_ptr.items[0] == ingredient) 22 return alg.key_ptr.*; 23 } 24 return null; 25} 26 27fn updateAllergens(allocator: std.mem.Allocator, allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), allergen: u32) !void { 28 var next = std.ArrayList(u32).init(allocator); 29 defer next.deinit(); 30 31 try next.append(allergen); 32 33 while (next.items.len > 0) { 34 const key = next.items[0]; 35 var ingredient = allergens.getEntry(key).?.value_ptr.items[0]; 36 var mapit = allergens.iterator(); 37 while (mapit.next()) |alg| { 38 if (alg.key_ptr.* == key) continue; 39 const ind = std.mem.indexOfScalar(u32, alg.value_ptr.items, ingredient); 40 if (ind != null) { 41 _ = alg.value_ptr.swapRemove(ind.?); 42 if (alg.value_ptr.items.len == 1) { 43 try next.append(alg.key_ptr.*); 44 } 45 } 46 } 47 _ = next.swapRemove(0); 48 } 49} 50 51fn parseAllergens(allocator: std.mem.Allocator, allergens: *std.AutoHashMap(u32, std.ArrayList(u32)), unhash: *std.AutoHashMap(u32, []const u8), ingredient_counts: *std.AutoHashMap(u32, u32), input: []const u8) !void { 52 var ingredients = std.ArrayList(u32).init(allocator); 53 defer ingredients.deinit(); 54 55 var lineit = std.mem.tokenize(u8, input, "\n"); 56 while (lineit.next()) |line| { 57 var allergen = false; 58 59 try ingredients.resize(0); 60 61 var spaceit = std.mem.tokenize(u8, line, " "); 62 while (spaceit.next()) |word| { 63 if (std.mem.eql(u8, word, "(contains")) { 64 allergen = true; 65 continue; 66 } 67 68 if (allergen) { 69 const allergen_name = if (word[word.len - 1] == ')' or word[word.len - 1] == ',') word[0 .. word.len - 1] else word; 70 const allergen_hash = hasher.hash(allergen_name); 71 try unhash.put(allergen_hash, allergen_name); 72 var algentry = allergens.getEntry(allergen_hash); 73 if (algentry) |algent| { 74 var i: u32 = 0; 75 while (i < algent.value_ptr.items.len) { 76 if (std.mem.indexOfScalar(u32, ingredients.items, algent.value_ptr.items[i]) == null) { 77 _ = algent.value_ptr.swapRemove(i); 78 } else { 79 i += 1; 80 } 81 } 82 83 if (algent.value_ptr.items.len == 1) { 84 try updateAllergens(allocator, allergens, algent.key_ptr.*); 85 } 86 } else { 87 var copy = std.ArrayList(u32).init(allocator); 88 errdefer copy.deinit(); 89 for (ingredients.items) |ing| { 90 if (getAllergen(allergens, ing) == null) { 91 try copy.append(ing); 92 } 93 } 94 try allergens.put(allergen_hash, copy); 95 } 96 } else { 97 const ingredient_hash = hasher.hash(word); 98 try unhash.put(ingredient_hash, word); 99 try ingredients.append(ingredient_hash); 100 const entry = ingredient_counts.getEntry(ingredient_hash); 101 if (entry == null) { 102 try ingredient_counts.put(ingredient_hash, 1); 103 } else { 104 entry.?.value_ptr.* += 1; 105 } 106 } 107 } 108 } 109} 110 111fn ascendingAllergen(ctx: void, p1: Pair, p2: Pair) bool { 112 _ = ctx; 113 var i: u32 = 0; 114 while (i < std.math.min(p1.allergen.len, p2.allergen.len) and p1.allergen[i] == p2.allergen[i]) : (i += 1) {} 115 return p1.allergen[i] < p2.allergen[i]; 116} 117 118fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { 119 _ = args; 120 121 var allergens = std.AutoHashMap(u32, std.ArrayList(u32)).init(allocator); 122 defer freeListmap(&allergens); 123 124 var ingredient_counts = std.AutoHashMap(u32, u32).init(allocator); 125 defer ingredient_counts.deinit(); 126 127 var unhash = std.AutoHashMap(u32, []const u8).init(allocator); 128 defer unhash.deinit(); 129 130 try parseAllergens(allocator, &allergens, &unhash, &ingredient_counts, input); 131 132 var answer: u32 = 0; 133 134 var algit = allergens.iterator(); 135 while (algit.next()) |alg| { 136 aoc.debugfmt("{} - ", .{alg.key_ptr.*}); 137 for (alg.value_ptr.items) |v| { 138 aoc.debugfmt("{} ", .{v}); 139 } 140 aoc.debugfmt("\n", .{}); 141 } 142 143 var mapit = ingredient_counts.iterator(); 144 while (mapit.next()) |kv| { 145 if (getAllergen(&allergens, kv.key_ptr.*) == null) answer += kv.value_ptr.*; 146 } 147 148 return try std.fmt.allocPrint(allocator, "{}", .{answer}); 149} 150 151fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { 152 _ = args; 153 154 var allergens = std.AutoHashMap(u32, std.ArrayList(u32)).init(allocator); 155 defer freeListmap(&allergens); 156 157 var ingredient_counts = std.AutoHashMap(u32, u32).init(allocator); 158 defer ingredient_counts.deinit(); 159 160 var unhash = std.AutoHashMap(u32, []const u8).init(allocator); 161 defer unhash.deinit(); 162 163 try parseAllergens(allocator, &allergens, &unhash, &ingredient_counts, input); 164 165 var pairs = std.ArrayList(Pair).init(allocator); 166 defer pairs.deinit(); 167 168 var mapit = ingredient_counts.iterator(); 169 while (mapit.next()) |kv| { 170 const alg = getAllergen(&allergens, kv.key_ptr.*); 171 if (alg != null) { 172 try pairs.append(Pair{ .ingredient = unhash.get(kv.key_ptr.*).?, .allergen = unhash.get(alg.?).? }); 173 } 174 } 175 176 std.sort.sort(Pair, pairs.items, {}, ascendingAllergen); 177 178 var parts = std.ArrayList([]const u8).init(allocator); 179 defer parts.deinit(); 180 for (pairs.items) |p, i| { 181 if (i > 0) try parts.append(","); 182 try parts.append(p.ingredient); 183 } 184 return try std.mem.join(allocator, "", parts.items); 185} 186 187const sol2 = "fqhpsl,zxncg,clzpsl,zbbnj,jkgbvlxh,dzqc,ppj,glzb"; 188pub const main = aoc.main(part1, part2, .{ "2584", sol2 });