Solve 2018 day 24 and 25

This commit is contained in:
felixm 2024-10-11 18:33:04 -04:00
parent 7d1dc3f95e
commit 3991842ef0
3 changed files with 220 additions and 1 deletions

179
2018/d24.py Normal file
View File

@ -0,0 +1,179 @@
import re
from lib import get_data, ints
from collections import defaultdict
from copy import deepcopy
r = re.compile(r"(immune|weak) to ([\w ,]+)")
r_strength = re.compile(r"(\w+) damage")
data = """Immune System:
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
Infection:
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4
"""
data = get_data(__file__)
ARMY, ID, UNITS, HITS, DAMG, INIT = 0, 1, 2, 3, 4, 5
groups = []
unit_meta = {}
id = 1
for a, p in enumerate(data.split("\n\n")):
for line in p.splitlines():
xs = ints(line)
if len(xs) == 0:
continue
assert len(xs) == 4
xs = [a, id] + xs
(attack,) = r_strength.findall(line)
defense = defaultdict(list)
for attr, values in r.findall(line):
defense[attr] = values.split(", ")
unit_meta[id] = attack, defense
groups.append(xs)
id += 1
def effective_power(group):
return (group[UNITS] * group[DAMG], group[INIT])
def damage(g1, g2):
"""Damage g1 does to g2"""
g1_id, g2_id = g1[ID], g2[ID]
g1_attack_type = unit_meta[g1_id][0]
g2_defenses = unit_meta[g2_id][1]
g2_immune = True if g1_attack_type in g2_defenses["immune"] else False
g2_weak = True if g1_attack_type in g2_defenses["weak"] else False
assert (g2_immune and g2_weak) != True
damage = g1[UNITS] * g1[DAMG]
if g2_immune:
damage = 0
elif g2_weak:
damage *= 2
return damage
groups_orig = deepcopy(groups)
while True:
groups = sorted(groups, key=effective_power, reverse=True)
attacks = {}
attacked = set()
for g in groups:
target_max_stats = 0, 0, 0
target = None
for t in groups:
if g[ARMY] == t[ARMY]:
continue
if g[ID] == t[ID]:
continue
if t[ID] in attacked:
continue
target_stats = damage(g, t), effective_power(t)[0], t[INIT]
if target_stats[0] == 0:
continue
if target_stats > target_max_stats:
target_max_stats = target_stats
target = t
if target is not None:
attacked.add(target[ID])
attacks[g[ID]] = target
groups = sorted(groups, key=lambda g: g[INIT], reverse=True)
for g in groups:
if g[ID] not in attacks:
continue
if g[UNITS] <= 0:
continue
target = attacks[g[ID]]
cdamage = damage(g, target)
units_dead = cdamage // target[HITS]
units_dead = units_dead if units_dead <= target[UNITS] else target[UNITS]
# print("immune" if g[ARMY] == 0 else "infection", g[ID], "attacks", target[ID], "dead", units_dead)
target[UNITS] -= units_dead
groups = [g for g in groups if g[UNITS] > 0]
armies = set()
for g in groups:
armies.add(g[ARMY])
assert len(armies) >= 1
if len(armies) == 1:
break
t = sum(g[UNITS] for g in groups)
print(t)
for boost in range(0, 1800):
groups = deepcopy(groups_orig)
for g in groups:
if g[ARMY] == 0:
g[DAMG] += boost
while True:
groups = sorted(groups, key=effective_power, reverse=True)
attacks = {}
attacked = set()
for g in groups:
target_max_stats = 0, 0, 0
target = None
for t in groups:
if g[ARMY] == t[ARMY]:
continue
if g[ID] == t[ID]:
continue
if t[ID] in attacked:
continue
target_stats = damage(g, t), effective_power(t)[0], t[INIT]
if target_stats[0] == 0:
continue
if target_stats > target_max_stats:
target_max_stats = target_stats
target = t
if target is not None:
attacked.add(target[ID])
attacks[g[ID]] = target
groups = sorted(groups, key=lambda g: g[INIT], reverse=True)
max_dead = 0
for g in groups:
if g[ID] not in attacks:
continue
if g[UNITS] <= 0:
continue
target = attacks[g[ID]]
cdamage = damage(g, target)
units_dead = cdamage // target[HITS]
units_dead = units_dead if units_dead <= target[UNITS] else target[UNITS]
max_dead = max(units_dead, max_dead)
# print("immune" if g[ARMY] == 0 else "infection", g[ID], "attacks", target[ID], "dead", units_dead)
target[UNITS] -= units_dead
if max_dead == 0:
break
groups = [g for g in groups if g[UNITS] > 0]
armies = set()
for g in groups:
armies.add(g[ARMY])
assert len(armies) >= 1
if len(armies) == 1:
army = groups[0][ARMY]
t = sum(g[UNITS] for g in groups)
if army == 0:
print(t)
exit(0)
break

38
2018/d25.py Normal file
View File

@ -0,0 +1,38 @@
from lib import get_data, ints
def mdist(x, y):
return sum(abs(a - b) for a, b in zip(x, y))
data = get_data(__file__)
xss = [tuple(ints(line)) for line in data.splitlines()]
# check if we can use set (even though we didn't end up using sets)
assert len(xss) == len(set(xss))
unplaced = list(xss)
groups = [[unplaced.pop()]]
while unplaced:
# see if any start fits into the current group
current_group = groups[-1]
add = None
for u in unplaced:
for x in current_group:
if mdist(u, x) <= 3:
add = u
break
if add is not None:
break
if add is not None:
# if yes, add the start to that group and see if another one can be
# added to the same group
unplaced.remove(add)
current_group.append(add)
else:
# if no, create a new group with an unplaced start
groups.append([unplaced.pop()])
print(len(groups))

View File

@ -110,7 +110,9 @@ Solutions and utility script for Advent of Code challenges in Python.
- Day 20:
- Day 21: 28:40 (16th - brute force but still not so bad)
- Day 22: 185:00 (should not have been so slow but was fun)
- Day 23:
- Day 23: days
- Day 24: days
- Day 25:
## AoC 2019