1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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))
|