Skip to content

Commit 71c5050

Browse files
committed
Add (messy) Day 24
1 parent 453e2be commit 71c5050

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

day-24/day_24.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
with open("input.txt") as file:
2+
inp = file.read().strip().replace("points with","points () with")
3+
4+
types = ['slashing', 'fire', 'bludgeoning', 'radiation', 'cold']
5+
6+
def parse_dmg(ss):
7+
dtype = ss[ss.rfind(" ")+1:]
8+
dnum = int(ss[:ss.rfind(" ")])
9+
return [0 if ty != dtype else dnum for ty in types]
10+
11+
def parse_res(ss):
12+
tp = [1, 1, 1, 1, 1]
13+
for p in ss.split("; "):
14+
if len(p) == 0:
15+
continue
16+
mul = 1
17+
if p[:4] == "weak":
18+
mul = 2
19+
p = p[8:]
20+
elif p[:6] == "immune":
21+
mul = 0
22+
p = p[10:]
23+
for dt in p.split("&"):
24+
tp[types.index(dt)] = mul
25+
return tp
26+
27+
vals = inp.split("\n\n")
28+
immune = vals[0]
29+
infect = vals[1]
30+
immune = [s.replace(", ","&").replace(" units each with ",",").replace(" hit points (",",").replace(") with an attack that does ",",").replace(" damage at initiative ",",") for s in immune.split("\n")[1:]]
31+
infect = [s.replace(", ","&").replace(" units each with ",",").replace(" hit points (",",").replace(") with an attack that does ",",").replace(" damage at initiative ",",") for s in infect.split("\n")[1:]]
32+
33+
def info(v):
34+
v = v.split(",")
35+
dmg = parse_dmg(v[3])
36+
# return [int(v[0]),int(v[1]),parse_res(v[2]),dmg,int(v[4]),int(v[0])*max(dmg)]
37+
return [int(v[0]),int(v[1]),parse_res(v[2]),dmg,int(v[4]),0]
38+
39+
immune = list(map(info, immune))
40+
infect = list(map(info, infect))
41+
42+
43+
def calc_dmg(ak,df):
44+
return sum(a*b for a,b in zip(ak[3],df[2]))
45+
46+
##DEBUG
47+
#immune.append([935, 10231, [0, 1, 1, 1, 0], [0, 0, 103, 0, 0], 50, 0])
48+
49+
def run_combat(immune,infect):
50+
51+
itc = 0
52+
while len(immune) > 0 and len(infect) > 0:
53+
#if itc % 10 == 0:
54+
# print(itc, len(immune),len(infect))
55+
itc += 1
56+
# targeting
57+
for i in immune:
58+
i[-1] = i[0] * max(i[3])
59+
for i in infect:
60+
i[-1] = i[0] * max(i[3])
61+
immune.sort(key=lambda v : 1000*(-v[-1])-v[-2])
62+
infect.sort(key=lambda v : 1000*(-v[-1])-v[-2])
63+
64+
im_tgs = []
65+
for ak in immune:
66+
best_choice = (0, 100000000, 0, None) # damage we'd do, eff power of target, tg_init, target_idx
67+
#best_choice = (-10000, 0, 0, None) # damage we'd do, eff power of target, tg_init, target_idx
68+
for idx, df in enumerate(infect):
69+
if idx in im_tgs:
70+
continue
71+
tc = (calc_dmg(ak, df), df[-1], df[-2], idx)
72+
if tc > best_choice:
73+
best_choice = tc
74+
im_tgs.append(best_choice[3])
75+
76+
if_tgs = []
77+
for ak in infect:
78+
best_choice = (0, 100000000, 0, None) # damage we'd do, eff power of target, tg_init, target_idx
79+
#best_choice = (-10000, 0, 0, None) # damage we'd do, eff power of target, tg_init, target_idx
80+
for idx, df in enumerate(immune):
81+
if idx in if_tgs:
82+
continue
83+
tc = (calc_dmg(ak, df), df[-1], df[-2], idx)
84+
if tc > best_choice:
85+
best_choice = tc
86+
if_tgs.append(best_choice[3])
87+
88+
all_units = []
89+
for i,v in enumerate(immune):
90+
all_units.append([0, i, v])
91+
for i,v in enumerate(infect):
92+
all_units.append([1, i, v])
93+
94+
all_units.sort(key=lambda v : -v[2][-2])
95+
96+
alive_immune = immune[:]
97+
alive_infect = infect[:]
98+
99+
#print(infect[0])
100+
101+
total_deathtoll = 0
102+
103+
for unit in all_units:
104+
if unit[0] == 0: # it's immune
105+
if unit[2] not in alive_immune:
106+
continue
107+
if im_tgs[unit[1]] is None:
108+
continue
109+
taken_damage = unit[2][0] * calc_dmg(unit[2],infect[im_tgs[unit[1]]])
110+
death_toll = (taken_damage)//infect[im_tgs[unit[1]]][1]
111+
#print("immune dealing dmg =",taken_damage,"killing",death_toll)
112+
infect[im_tgs[unit[1]]][0] -= death_toll
113+
total_deathtoll += death_toll
114+
if infect[im_tgs[unit[1]]][0] <= 0:
115+
alive_infect.remove(infect[im_tgs[unit[1]]])
116+
else: # it's infect
117+
if unit[2] not in alive_infect:
118+
continue
119+
if if_tgs[unit[1]] is None:
120+
continue
121+
taken_damage = unit[2][0] * calc_dmg(unit[2],immune[if_tgs[unit[1]]])
122+
death_toll = (taken_damage)//immune[if_tgs[unit[1]]][1]
123+
#print("infect dealing dmg =",taken_damage,"killing",death_toll)
124+
immune[if_tgs[unit[1]]][0] -= death_toll
125+
total_deathtoll += death_toll
126+
if immune[if_tgs[unit[1]]][0] <= 0:
127+
alive_immune.remove(immune[if_tgs[unit[1]]])
128+
129+
## Stalemate
130+
if total_deathtoll == 0:
131+
return False
132+
133+
immune = alive_immune
134+
infect = alive_infect
135+
136+
return tuple(map(lambda w : sum(v[0] for v in w), [infect,immune]))
137+
138+
def dcopy(m):
139+
if type(m) is list:
140+
return [dcopy(d) for d in m]
141+
else:
142+
return m
143+
144+
def rboost(b):
145+
im_copy = dcopy(immune) #[ccopy(c) for c in immune]
146+
if_copy = dcopy(infect) #[ccopy(c) for c in infect]
147+
for i in im_copy:
148+
i[3][max(enumerate(i[3]),key=lambda v : v[1])[0]] += b
149+
return run_combat(im_copy, if_copy)
150+
151+
print("Part 1:",run_combat(dcopy(immune),dcopy(infect))[0])
152+
153+
low = 1
154+
high = 100
155+
while high > low:
156+
mid = (high+low)//2
157+
res = rboost(mid)
158+
if res == False or res[1] == 0:
159+
low = mid + 1
160+
else:
161+
high = mid
162+
163+
print("Part 2:",rboost(high)[1])

day-24/input.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Immune System:
2+
1432 units each with 7061 hit points (immune to cold; weak to bludgeoning) with an attack that does 41 slashing damage at initiative 17
3+
3387 units each with 9488 hit points (weak to bludgeoning) with an attack that does 27 slashing damage at initiative 20
4+
254 units each with 3249 hit points (immune to fire) with an attack that does 89 cold damage at initiative 1
5+
1950 units each with 8201 hit points with an attack that does 39 fire damage at initiative 15
6+
8137 units each with 3973 hit points (weak to slashing; immune to radiation) with an attack that does 4 radiation damage at initiative 6
7+
4519 units each with 7585 hit points (weak to fire) with an attack that does 15 radiation damage at initiative 8
8+
763 units each with 7834 hit points (immune to radiation, slashing, cold; weak to fire) with an attack that does 91 radiation damage at initiative 18
9+
935 units each with 10231 hit points (immune to slashing, cold) with an attack that does 103 bludgeoning damage at initiative 12
10+
4557 units each with 7860 hit points (immune to slashing) with an attack that does 15 slashing damage at initiative 11
11+
510 units each with 7363 hit points (weak to fire, radiation) with an attack that does 143 fire damage at initiative 5
12+
13+
Infection:
14+
290 units each with 29776 hit points (weak to cold, radiation) with an attack that does 204 bludgeoning damage at initiative 16
15+
7268 units each with 14114 hit points (immune to radiation; weak to bludgeoning) with an attack that does 3 bludgeoning damage at initiative 19
16+
801 units each with 5393 hit points with an attack that does 13 slashing damage at initiative 13
17+
700 units each with 12182 hit points with an attack that does 29 cold damage at initiative 4
18+
531 units each with 16607 hit points (immune to slashing) with an attack that does 53 bludgeoning damage at initiative 10
19+
23 units each with 24482 hit points (weak to cold, fire) with an attack that does 2095 bludgeoning damage at initiative 7
20+
8025 units each with 43789 hit points (weak to cold; immune to radiation) with an attack that does 8 radiation damage at initiative 9
21+
1405 units each with 53896 hit points with an attack that does 70 slashing damage at initiative 14
22+
566 units each with 7820 hit points (immune to cold) with an attack that does 26 cold damage at initiative 2
23+
1641 units each with 7807 hit points (weak to fire; immune to slashing, bludgeoning) with an attack that does 7 radiation damage at initiative 3

0 commit comments

Comments
 (0)