Skip to content

Commit aa503de

Browse files
committed
Added solution for part 1 of day 21
1 parent 4e234c4 commit aa503de

File tree

3 files changed

+147
-1
lines changed

3 files changed

+147
-1
lines changed

advent2020/day21.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# MIT License
2+
#
3+
# Copyright (c) 2021 Andrew Krepps
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
24+
from . import util
25+
26+
27+
def parse_input(lines):
28+
foods = []
29+
for line in lines:
30+
overall_parts = line.split(" (contains ")
31+
ingredients = set(token.strip() for token in overall_parts[0].split())
32+
allergens = set(token.strip() for token in overall_parts[1].replace(')', '').split(", "))
33+
foods.append((ingredients, allergens))
34+
return foods
35+
36+
37+
def get_all_ingredients(foods):
38+
return set(ingredient for food in foods for ingredient in food[0])
39+
40+
41+
def get_all_allergens(foods):
42+
return set(allergen for food in foods for allergen in food[1])
43+
44+
45+
def get_ingredient_appearances_by_allergen(foods, all_allergens):
46+
ingredients_by_allergen = {allergen: set() for allergen in all_allergens}
47+
for ingredients, allergens in foods:
48+
for allergen in allergens:
49+
for ingredient in ingredients:
50+
ingredients_by_allergen[allergen].add(ingredient)
51+
return ingredients_by_allergen
52+
53+
54+
def get_allergens_by_ingredient(foods, all_ingredients, all_allergens):
55+
my_ingredients = set(all_ingredients)
56+
my_allergens = set(all_allergens)
57+
ingredient_allergens = {ingredient: None for ingredient in my_ingredients}
58+
searching = True
59+
while searching:
60+
searching = False
61+
for allergen in my_allergens:
62+
possible_ingredients = set(my_ingredients)
63+
for food_ingredients, food_allergens in foods:
64+
if allergen in food_allergens:
65+
possible_ingredients.intersection_update(food_ingredients)
66+
if len(possible_ingredients) == 1:
67+
ingredient = possible_ingredients.pop()
68+
ingredient_allergens[ingredient] = allergen
69+
my_ingredients.remove(ingredient)
70+
my_allergens.remove(allergen)
71+
searching = True
72+
break
73+
return ingredient_allergens
74+
75+
76+
def count_ingredient_appearances(foods, ingredients):
77+
count = 0
78+
for food_ingredients, _ in foods:
79+
for food_ingredient in food_ingredients:
80+
if food_ingredient in ingredients:
81+
count += 1
82+
return count
83+
84+
85+
def get_part1_answer(lines):
86+
foods = parse_input(lines)
87+
all_ingredients = get_all_ingredients(foods)
88+
all_allergens = get_all_allergens(foods)
89+
allergens_by_ingredient = get_allergens_by_ingredient(foods, all_ingredients, all_allergens)
90+
safe_ingredients = [ingredient for ingredient, allergen in allergens_by_ingredient.items() if allergen is None]
91+
return count_ingredient_appearances(foods, safe_ingredients)
92+
93+
94+
def get_part2_answer(lines):
95+
return None
96+
97+
98+
def run():
99+
lines = util.get_input_file_lines("day21.txt")
100+
print(f"The answer to part 1 is {get_part1_answer(lines)}")
101+
print(f"The answer to part 2 is {get_part2_answer(lines)}")

main.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from advent2020 import day18
4444
from advent2020 import day19
4545
from advent2020 import day20
46+
from advent2020 import day21
4647

4748

4849
day_runners = [
@@ -65,7 +66,8 @@
6566
lambda: day17.run(),
6667
lambda: day18.run(),
6768
lambda: day19.run(),
68-
lambda: day20.run()
69+
lambda: day20.run(),
70+
lambda: day21.run()
6971
]
7072

7173

test/test_day21.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# MIT License
2+
#
3+
# Copyright (c) 2021 Andrew Krepps
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
24+
import unittest
25+
26+
from advent2020.day21 import get_part1_answer
27+
from advent2020.day21 import get_part2_answer
28+
from advent2020.util import get_input_data_lines
29+
30+
31+
data = """
32+
mxmxvkd kfcds sqjhc nhms (contains dairy, fish)
33+
trh fvjkl sbzzf mxmxvkd (contains dairy)
34+
sqjhc fvjkl (contains soy)
35+
sqjhc mxmxvkd sbzzf (contains fish)
36+
"""
37+
38+
39+
class Day21Test(unittest.TestCase):
40+
def test_day21(self):
41+
lines = get_input_data_lines(data)
42+
self.assertEqual(get_part1_answer(lines), 5)
43+
self.assertEqual(get_part2_answer(lines), None)

0 commit comments

Comments
 (0)