2015: d21: ex1: add solution
This commit is contained in:
parent
ae67526c40
commit
d9406a4258
1 changed files with 104 additions and 0 deletions
104
2015/d21/ex1/ex1.py
Executable file
104
2015/d21/ex1/ex1.py
Executable file
|
|
@ -0,0 +1,104 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
import sys
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
|
||||||
|
class Ennemy(NamedTuple):
|
||||||
|
hp: int
|
||||||
|
damage: int
|
||||||
|
armor: int
|
||||||
|
|
||||||
|
def attack(self, other: "Ennemy") -> "Ennemy":
|
||||||
|
return Ennemy(
|
||||||
|
other.hp - max(1, self.damage - other.armor),
|
||||||
|
other.damage,
|
||||||
|
other.armor,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Item(NamedTuple):
|
||||||
|
cost: int
|
||||||
|
damage: int
|
||||||
|
armor: int
|
||||||
|
|
||||||
|
|
||||||
|
WEAPONS = {
|
||||||
|
"Dagger": Item(8, 4, 0),
|
||||||
|
"Shortsword": Item(10, 5, 0),
|
||||||
|
"Warhammer": Item(25, 6, 0),
|
||||||
|
"Longsword": Item(40, 7, 0),
|
||||||
|
"Greataxe": Item(74, 8, 0),
|
||||||
|
}
|
||||||
|
ARMORS = {
|
||||||
|
"Leather": Item(13, 0, 1),
|
||||||
|
"Chainmail": Item(31, 0, 2),
|
||||||
|
"Splintmail": Item(53, 0, 3),
|
||||||
|
"Bandedmail": Item(75, 0, 4),
|
||||||
|
"Platemail": Item(102, 0, 5),
|
||||||
|
}
|
||||||
|
RINGS = {
|
||||||
|
"Damage +1": Item(25, 1, 0),
|
||||||
|
"Damage +2": Item(50, 2, 0),
|
||||||
|
"Damage +3": Item(100, 3, 0),
|
||||||
|
"Defense +1": Item(20, 0, 1),
|
||||||
|
"Defense +2": Item(40, 0, 2),
|
||||||
|
"Defense +3": Item(80, 0, 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def solve(input: str) -> int:
|
||||||
|
def parse(input: str) -> Ennemy:
|
||||||
|
return Ennemy(*map(int, (line.split(": ")[1] for line in input.splitlines())))
|
||||||
|
|
||||||
|
def choose_items() -> Iterator[list[Item]]:
|
||||||
|
allowed_weapons = [1]
|
||||||
|
allowed_armors = [0, 1]
|
||||||
|
allowed_rings = [0, 1, 2]
|
||||||
|
|
||||||
|
for weapons, armors, rings in itertools.product(
|
||||||
|
itertools.chain.from_iterable(
|
||||||
|
itertools.combinations(WEAPONS.values(), i) for i in allowed_weapons
|
||||||
|
),
|
||||||
|
itertools.chain.from_iterable(
|
||||||
|
itertools.combinations(ARMORS.values(), i) for i in allowed_armors
|
||||||
|
),
|
||||||
|
itertools.chain.from_iterable(
|
||||||
|
itertools.combinations(RINGS.values(), i) for i in allowed_rings
|
||||||
|
),
|
||||||
|
):
|
||||||
|
yield list(itertools.chain(weapons, armors, rings))
|
||||||
|
|
||||||
|
def assemble_inventory(items: list[Item]) -> Ennemy:
|
||||||
|
return Ennemy(
|
||||||
|
hp=100,
|
||||||
|
damage=sum(item.damage for item in items),
|
||||||
|
armor=sum(item.armor for item in items),
|
||||||
|
)
|
||||||
|
|
||||||
|
def battle(us: Ennemy, ennemy: Ennemy) -> bool:
|
||||||
|
while True:
|
||||||
|
ennemy = us.attack(ennemy)
|
||||||
|
if ennemy.hp <= 0:
|
||||||
|
return True
|
||||||
|
us = ennemy.attack(us)
|
||||||
|
if us.hp <= 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
ennemy = parse(input)
|
||||||
|
return min(
|
||||||
|
sum(item.cost for item in items)
|
||||||
|
for items in choose_items()
|
||||||
|
if battle(assemble_inventory(items), ennemy)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
input = sys.stdin.read()
|
||||||
|
print(solve(input))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue