2020-12-21 - Allergen Assessment
(original .ipynb)
Day 21 input is a sequence of ingredients in an unfamiliar language followed by a list of allergens (mine is here). Each allergen maps to a single ingredient. Part 1 involves finding the number of appearances of each ingredient which is not an allergen. Part 2 involves finding the allergen/ingredient mappings and producing a comma-separated string of ingredients sorted by the alphabetic order of their allergen.
from functools import reduce from collections import defaultdict puzzle_input_str = open("puzzle_input/day21.txt", "r").read() test_input_str = """mxmxvkd kfcds sqjhc nhms (contains dairy, fish) trh fvjkl sbzzf mxmxvkd (contains dairy) sqjhc fvjkl (contains soy) sqjhc mxmxvkd sbzzf (contains fish)""" def parse_food(line): ingredients_raw, allergens_raw = line.split("(contains") ingredients = ingredients_raw.strip().split(" ") allergens = allergens_raw[:-1].strip().split(", ") return set(ingredients), set(allergens) def ingredients_for_allergen(allergen, ingredients_by_allergen): ingredients = reduce(lambda a,b: a.intersection(b), ingredients_by_allergen[allergen]) return set(ingredients) def process_foods(foods): ingredients_by_allergen = defaultdict(list) ingredient_appearances = defaultdict(lambda:0) for ingredients, allergens in foods: for ingredient in ingredients: ingredient_appearances[ingredient] += 1 for allergen in allergens: ingredients_by_allergen[allergen].append(set(ingredients)) return ingredients_by_allergen, ingredient_appearances def part1(input_str): input_lines = input_str.split("\n") foods = [parse_food(line) for line in input_lines] # lmao did this mf just say "foods" ingredients_by_allergen, ingredient_appearances = process_foods(foods) maybe_contains_allergen = set() for allergen in ingredients_by_allergen: maybe_contains_allergen.update(ingredients_for_allergen(allergen, ingredients_by_allergen)) total = 0 for ingredient in ingredient_appearances: if not (ingredient in maybe_contains_allergen): total += ingredient_appearances[ingredient] return total assert 5 == part1(test_input_str) part1(puzzle_input_str)
2635
def part2(input_str): input_lines = input_str.split("\n") foods = [parse_food(line) for line in input_lines] ingredients_by_allergen, ingredient_appearances = process_foods(foods) allergen_mapping = {} while ingredients_by_allergen.keys(): newly_identified_allergens = {} for allergen in ingredients_by_allergen: ingredient_candidates = reduce( lambda a,b: a.intersection(b), ingredients_by_allergen[allergen] ) if len(ingredient_candidates) == 1: ingredient = ingredient_candidates.pop() newly_identified_allergens[allergen] = ingredient for allergen in newly_identified_allergens: allergen_mapping[allergen] = newly_identified_allergens[allergen] del ingredients_by_allergen[allergen] for allergen in ingredients_by_allergen: new_foods = [] for food in ingredients_by_allergen[allergen]: new_ingredients = set() for ingredient in food: if not (ingredient in newly_identified_allergens.values()): new_ingredients.add(ingredient) new_foods.append(new_ingredients) ingredients_by_allergen[allergen] = new_foods sorted_allergens = sorted(allergen_mapping.keys()) return ",".join([allergen_mapping[allergen] for allergen in sorted_allergens]) assert "mxmxvkd,sqjhc,fvjkl" == part2(test_input_str) print(part2(puzzle_input_str))
xncgqbcp,frkmp,qhqs,qnhjhn,dhsnxr,rzrktx,ntflq,lgnhmx