summaryrefslogtreecommitdiffstats
path: root/15
diff options
context:
space:
mode:
authorTomasz Kramkowski <tk@the-tk.com>2021-11-24 22:25:42 +0000
committerTomasz Kramkowski <tk@the-tk.com>2021-11-24 22:25:42 +0000
commita7a6b86002b595bc167af72606b14c67ed1bdf8f (patch)
treebff94329cf969bd9df68d3b9782fee2107db56c2 /15
downloadaoc2015-a7a6b86002b595bc167af72606b14c67ed1bdf8f.tar.gz
aoc2015-a7a6b86002b595bc167af72606b14c67ed1bdf8f.tar.xz
aoc2015-a7a6b86002b595bc167af72606b14c67ed1bdf8f.zip
init commit
Diffstat (limited to '15')
-rw-r--r--15/input4
-rw-r--r--15/solution.py86
2 files changed, 90 insertions, 0 deletions
diff --git a/15/input b/15/input
new file mode 100644
index 0000000..634c6a5
--- /dev/null
+++ b/15/input
@@ -0,0 +1,4 @@
+Frosting: capacity 4, durability -2, flavor 0, texture 0, calories 5
+Candy: capacity 0, durability 5, flavor -1, texture 0, calories 8
+Butterscotch: capacity -1, durability 0, flavor 5, texture 0, calories 6
+Sugar: capacity 0, durability 0, flavor -2, texture 2, calories 1
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))