summaryrefslogtreecommitdiffstats
path: root/15/solution.py
diff options
context:
space:
mode:
Diffstat (limited to '15/solution.py')
-rw-r--r--15/solution.py86
1 files changed, 86 insertions, 0 deletions
diff --git a/15/solution.py b/15/solution.py
new file mode 100644
index 0000000..21b4ec8
--- /dev/null
+++ b/15/solution.py
@@ -0,0 +1,86 @@
+from dataclasses import dataclass
+from functools import cache, cached_property
+from itertools import combinations_with_replacement
+
+@dataclass
+class Ingredient:
+ capacity: int
+ durability: int
+ flavor: int
+ texture: int
+ calories: int
+
+ @cached_property
+ def score(self):
+ return max(self.capacity, 0) * max(self.durability, 0) * max(self.flavor, 0) * max(self.texture, 0)
+
+ @cached_property
+ def calories_score(self):
+ return self.score if self.calories == 500 else 0
+
+ def __lt__(self, other):
+ return self.score < other.score
+ def __gt__(self, other):
+ return self.score > other.score
+ def __le__(self, other):
+ return self.score <= other.score
+ def __ge__(self, other):
+ return self.score >= other.score
+ def __add__(self, other):
+ return Ingredient(
+ self.capacity + other.capacity,
+ self.durability + other.durability,
+ self.flavor + other.flavor,
+ self.texture + other.texture,
+ self.calories + other.calories
+ )
+ def __mul__(self, other: int) -> 'Ingredient':
+ return Ingredient(
+ self.capacity * other,
+ self.durability * other,
+ self.flavor * other,
+ self.texture * other,
+ self.calories * other
+ )
+
+ @staticmethod
+ def from_string(string) -> 'Ingredient':
+ _, rest = string.split(': ')
+ capacity, durability, flavor, texture, calories = (int(p.split()[1]) for p in rest.split(', '))
+ return Ingredient(capacity, durability, flavor, texture, calories)
+
+
+# def part1(ingredients: list[Ingredient]) -> int:
+# @cache
+# def max_score(capacity: int) -> Properties:
+# if capacity == 0:
+# return Properties(0, 0, 0, 0, 0)
+# return max(max_score(capacity - 1) + ingredient.properties for ingredient in ingredients)
+# s = max_score(100).score
+# print(max_score.cache_info())
+# return s
+
+# def part1(ingredients: list[Ingredient]) -> int:
+# return max(sum(perm) for perm in combinations_with_replacement([i.properties for i in ingredients], 100)).score
+
+def splits(total: int, segments: int) -> int:
+ if segments <= 1:
+ yield [total]
+ else:
+ for i in range(total + 1):
+ for subsplit in splits(total - i, segments - 1):
+ yield [i] + subsplit
+
+def part1(ingredients: list[Ingredient]) -> int:
+ empty = Ingredient(0, 0, 0, 0, 0)
+ return max(sum((ingredients[i] * q for i, q in enumerate(split)), start=empty).score for split in splits(100, len(ingredients)))
+
+def part2(ingredients: list[Ingredient]) -> int:
+ empty = Ingredient(0, 0, 0, 0, 0)
+ return max(sum((ingredients[i] * q for i, q in enumerate(split)), start=empty).calories_score for split in splits(100, len(ingredients)))
+
+if __name__ == '__main__':
+ with open('input') as f:
+ ingredients = [Ingredient.from_string(line.strip()) for line in f]
+ print(part1(ingredients))
+ print(part2(ingredients))