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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
#advent of code 2018
#day 24
#part 1 and part 2
#this was very fun to do
#easier version of 2018 day 15 due to no pathfinding
#part 1 no probs, but it took me way more time to finish part 2 than it should have
#at first I didn't account for a possible stalemate, but it was late at night
#(I read the input and didn't see any stalemate scenarios based on weaknesses
#but now that I think about it I was reading the testinput from the example lol)
#so I was trying to debug whats wrong with my current code
#instead of adding the few lines to resolve the stalemates
#the groups were simply refusing to fight
#but to be fair, there definitely were some bugs in a previous version,
#because I still wasn't getting the answer even when milk boost value was getting huge
import copy; #library required for the deepcopy function
f = open("input.txt",'r');
Testing = False;
#Testing = True;
if Testing:
f.close();
f = open("testinput.txt",'r');
def readinput(group):
if group.find("(") != -1:
immu_weak = group[group.find("("):group.find(")")+1];
group = group.replace(immu_weak, "");
immu_weak = immu_weak.replace("(","");
immu_weak = immu_weak.replace(")","");
immu_weak = immu_weak.replace(", ",",");
if immu_weak.find(";") != -1:
if (immu_weak[0] == "i"):
i_i = 0;
w_i = 1;
elif (immu_weak[0] == "w"):
i_i = 1;
w_i = 0;
immu_weak = immu_weak.replace("weak to ","");
immu_weak = immu_weak.replace("immune to ","");
immu_weak = immu_weak.replace(" ","");
iw = immu_weak.split(";");
immu = iw[i_i].split(",");
weak = iw[w_i].split(",");
elif immu_weak[0] == "w":
immu_weak = immu_weak.replace("weak to ","");
immu = [];
weak = immu_weak.split(",");
else:
weak = [];
immu_weak = immu_weak.replace("immune to ","");
immu = immu_weak.split(",");
else:
immu = [];
weak = [];
group = group.replace("units each with", " ");
group = group.replace("hit points", " ");
group = group.replace("with an attack that does", " ");
group = group.replace("damage at initiative", " ");
g = group.split();
gr = [int(x) for x in g[:-2]];
gr.append(g[-2]);
gr.append(int(g[-1]));
gr.append(immu);
gr.append(weak);
return gr;
class army:
def __init__(self, side, ID, inputlist):
self.side = side;
self.id = ID;
self.units = inputlist[0];
self.hp = inputlist[1];
self.dmgVal = inputlist[2];
self.dmgType = inputlist[3];
self.initiative = inputlist[4];
self.immune = inputlist[5];
self.weak = inputlist[6];
self.IsDead = False;
self.IsTargeted = False;
self.Target = None;
def __str__(self):
return f'<{self.side}>: {self.units} units each with {self.hp} hit points (immune to {self.immune}; weak to {self.weak}) with an attack that does {self.dmgVal} {self.dmgType} damage at initiative {self.initiative}';
def EffectivePower(self,TargetWeak,TargetImmune):
DamageDealt = self.units*self.dmgVal;
if self.dmgType in TargetWeak:
DamageDealt = DamageDealt*2;
elif self.dmgType in TargetImmune:
DamageDealt = 0;
return DamageDealt;
def TakeDamage(self, DamageReceived):
UnitsKilled = DamageReceived//self.hp;
self.units -= UnitsKilled;
if self.units <= 0:
self.IsDead = True;
self.units = 0;
############
#parse input
groups = {};
side = "imm";
currentid = 11;
f.readline();
for l in f:
if(l=="\n"): break;
groups.update({currentid:army(side,currentid,readinput(l))});
currentid += 1;
f.readline();
side = "inf";
for l in f:
if(l=="\n"): break;
groups.update({currentid:army(side,currentid,readinput(l))});
currentid += 1;
f.close();
immsys = 0;
infect = 0;
for g in groups:
if groups[g].side == "imm": immsys +=1;
if groups[g].side == "inf": infect +=1;
milk = 0;
groupcopy = copy.deepcopy(groups); #deepcopy of groups to reset them
winner = "inf"; #just to initiate the algo
#keep doing battles until immune system wins
while winner == "inf":
groups = copy.deepcopy(groupcopy);
#count the initial number of groups for immune system and infection
#apply milk booster to immune system
immsys = 0;
infect = 0;
for g in groups:
if groups[g].side == "imm": immsys +=1;
if groups[g].side == "inf": infect +=1;
if groups[g].side == "imm": groups[g].dmgVal += milk;
groups[g].IsDead = False;
#battle algorythm
while immsys > 0 and infect > 0:
kills = 0;
UnitsBefore = 0;
for g in groups:
UnitsBefore += groups[g].units;
#phase 1 target selection
TSqueue = [g for g in groups if groups[g].IsDead == False];
TSqueue = sorted(TSqueue, key=lambda g: (groups[g].EffectivePower([],[]),groups[g].initiative), reverse = True);
for a in TSqueue:
groups[a].Target = None;
PossibleTargets = [g for g in groups if groups[g].IsDead == False and groups[g].side != groups[a].side];
PossibleTargets = sorted(PossibleTargets, key=lambda g: (groups[a].EffectivePower(groups[g].weak,groups[g].immune),groups[g].EffectivePower([],[]),groups[g].initiative), reverse=True);
for t in PossibleTargets:
if groups[t].IsTargeted == False and groups[a].EffectivePower(groups[t].weak,groups[t].immune) > 0:
groups[a].Target = int(t);
groups[t].IsTargeted = True;
break;
#phase 2 attacking
ATqueue = TSqueue.copy();
ATqueue = sorted(ATqueue, key=lambda g: groups[g].initiative, reverse = True);
for a in ATqueue:
if groups[a].IsDead or groups[a].Target == None: continue;
t = groups[a].Target;
groups[t].TakeDamage( groups[a].EffectivePower(groups[t].weak,groups[t].immune) );
#reset the IsTargeted parameter for the next iteration of the battle
#count the groups of each side and calculate kills
immsys = 0;
infect = 0;
UnitsAfter = 0;
for g in groups:
groups[g].IsTargeted = False;
if groups[g].side == "imm" and groups[g].IsDead == False: immsys +=1;
if groups[g].side == "inf" and groups[g].IsDead == False: infect +=1;
UnitsAfter += groups[g].units;
#if there were no kills in the current iteration of battle - finish the battle
kills = UnitsBefore - UnitsAfter;
if kills == 0:
break;
#declare the winner (infection wins in case of a stalemate)
if kills == 0:
winner = "inf";
elif immsys == 0:
winner = "inf";
else:
winner = "imm";
#calculate part 1
if milk == 0:
part1 = 0;
for g in groups:
if groups[g].side == winner and groups[g].IsDead == False:
part1 += groups[g].units;
milk += 1; #raise milk booster for upcoming new battle
#calculate part 2
part2 = 0;
for g in groups:
if groups[g].side == winner and groups[g].IsDead == False:
part2 += groups[g].units;
print("part 1 = ", part1);
print("part 2 = ", part2);
|