#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);