My Advent of Code solutions in Python. kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code python
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add solution for 2018/24

+236
+211
2018/day24.py
··· 1 + import re 2 + import copy 3 + import fileinput 4 + 5 + from utils import parse_line, parse_nums 6 + 7 + 8 + class Group: 9 + def __init__(self, is_infection, num, _count, hp, weaknesses, immunities, dmg, type, initiative): 10 + self.alliance = 'Infection' if is_infection else 'Immune System' 11 + self.is_infection = is_infection 12 + self.num = num 13 + self.count = _count 14 + self.hp = hp 15 + self.weaknesses = weaknesses 16 + self.immunities = immunities 17 + self.dmg = dmg + (0 if self.is_infection else BOOST) 18 + self.type = type 19 + self.initiative = initiative 20 + 21 + @property 22 + def power(self): 23 + return self.count * self.dmg 24 + 25 + def calc_dmg(self, other): 26 + if self.type in other.immunities: 27 + return 0 28 + elif self.type in other.weaknesses: 29 + return 2 * self.power 30 + else: 31 + return self.power 32 + 33 + def deal_dmg(self, other): 34 + dmg = self.calc_dmg(other) 35 + units_lost = min(dmg // other.hp, other.count) 36 + other.count -= units_lost 37 + return units_lost 38 + 39 + 40 + def setup_simulation(): 41 + immune = {} 42 + infect = {} 43 + groups = [] 44 + 45 + for i, line in enumerate(IMMUNE): 46 + g = Group(False, i, *line) 47 + immune[i] = g 48 + groups.append(g) 49 + 50 + for i, line in enumerate(INFECT): 51 + g = Group(True, i, *line) 52 + infect[i] = g 53 + groups.append(g) 54 + 55 + return immune, infect, groups 56 + 57 + 58 + # Read problem input 59 + IMMUNE = [] 60 + INFECT = [] 61 + BOOST = 0 62 + DEBUG = False 63 + 64 + on_infection = False 65 + 66 + for i, line in enumerate(fileinput.input()): 67 + line = line.strip() 68 + 69 + if line == 'Infection:': 70 + on_infection = True 71 + continue 72 + 73 + try: 74 + _count, hp, dmg, initiative = parse_nums(line, negatives=False) 75 + except Exception: 76 + continue 77 + clauses = next(iter(re.findall(r'(\(.+\))', line)), None) 78 + type = re.findall(r'(\S+) damage', line) 79 + type = next(iter(type), None) 80 + 81 + parts = (clauses or '').replace(',', '').replace('(', '').replace(')', '').split(';') 82 + weaknesses = [] 83 + immunities = [] 84 + 85 + if parts[0] != '': 86 + for part in parts: 87 + things = part.split() 88 + if things[0] == 'weak': 89 + weaknesses = things[2:] 90 + else: 91 + immunities = things[2:] 92 + 93 + group = (_count, hp, weaknesses, immunities, dmg, type, initiative) 94 + 95 + if on_infection: 96 + INFECT.append(group) 97 + else: 98 + IMMUNE.append(group) 99 + 100 + 101 + def simulate(boost=0): 102 + global BOOST 103 + BOOST = boost 104 + immune, infection, groups = setup_simulation() 105 + 106 + last_infcount = None 107 + last_imscount = None 108 + 109 + while True: 110 + # Target selection 111 + targets = {} 112 + 113 + if DEBUG: 114 + for alliance, group in [("Immune System", immune), ("Infection", infection)]: 115 + print "{}:".format(alliance) 116 + for n, g in group.items(): 117 + if g.count > 0: 118 + print "Group {} contains {} units".format(n, g.count) 119 + 120 + print 121 + 122 + for g in sorted(groups, key=lambda g: (g.power, g.initiative), reverse=True): 123 + if g.count == 0: 124 + continue 125 + 126 + other = immune if g.is_infection else infection 127 + 128 + target_num = None 129 + 130 + for h in (z for z in groups if z.alliance != g.alliance): 131 + if h.count == 0: 132 + continue 133 + 134 + leave = False 135 + for a, b in targets.items(): 136 + if a[0] == g.alliance and h.num == b: 137 + leave = True 138 + break 139 + 140 + if leave: 141 + continue 142 + 143 + if DEBUG: 144 + print "{} group {} would deal defending group {} {} damage".format(g.alliance, g.num, h.num, g.calc_dmg(h)) 145 + 146 + if g.calc_dmg(h) > 0: 147 + if target_num is None: 148 + target_num = h.num 149 + else: 150 + poss = other[target_num] 151 + if g.calc_dmg(h) > g.calc_dmg(poss): 152 + target_num = h.num 153 + elif g.calc_dmg(h) == g.calc_dmg(poss): 154 + if h.power > poss.power: 155 + target_num = h.num 156 + elif h.power == poss.power: 157 + if h.initiative > poss.initiative: 158 + target_num = h.num 159 + 160 + targets[g.alliance, g.num] = target_num 161 + 162 + if DEBUG: 163 + print 164 + 165 + # Attack 166 + for g in sorted(groups, key=lambda g: g.initiative, reverse=True): 167 + other = immune if g.is_infection else infection 168 + h = other.get(targets.get((g.alliance, g.num), None), None) 169 + if h is None: 170 + continue 171 + killed = g.deal_dmg(h) 172 + 173 + if DEBUG: 174 + print "{} group {} attacks defending group {}, killing {} units".format(g.alliance, g.num, h.num, killed) 175 + 176 + if DEBUG: 177 + print 178 + print 179 + 180 + infcount = 0 181 + imscount = 0 182 + for g in groups: 183 + if g.is_infection: 184 + infcount += g.count 185 + else: 186 + imscount += g.count 187 + 188 + if infcount == 0: 189 + return True, imscount 190 + elif imscount == 0: 191 + return False, infcount 192 + elif infcount == last_infcount and imscount == last_imscount: 193 + return False, None 194 + 195 + last_infcount = infcount 196 + last_imscount = imscount 197 + 198 + print "Units in the winning army:", simulate()[1] 199 + 200 + lo = 0 201 + hi = 1000 202 + 203 + while lo < hi: 204 + mid = (lo + hi) // 2 205 + res, count = simulate(mid) 206 + if not res: 207 + lo = mid + 1 208 + else: 209 + hi = mid 210 + 211 + print "Immune system units after smallest boost ({}): {}".format(mid + 1, simulate(mid + 1)[1])
+23
2018/inputs/24.txt
··· 1 + Immune System: 2 + 2321 units each with 10326 hit points (immune to slashing) with an attack that does 42 fire damage at initiative 4 3 + 2899 units each with 9859 hit points with an attack that does 32 slashing damage at initiative 11 4 + 4581 units each with 7073 hit points (weak to slashing) with an attack that does 11 radiation damage at initiative 9 5 + 5088 units each with 7917 hit points (weak to slashing; immune to bludgeoning, fire, radiation) with an attack that does 15 fire damage at initiative 17 6 + 786 units each with 1952 hit points (immune to fire, bludgeoning, slashing, cold) with an attack that does 23 slashing damage at initiative 16 7 + 3099 units each with 7097 hit points (weak to bludgeoning) with an attack that does 17 radiation damage at initiative 8 8 + 4604 units each with 4901 hit points with an attack that does 8 fire damage at initiative 13 9 + 7079 units each with 10328 hit points with an attack that does 14 bludgeoning damage at initiative 18 10 + 51 units each with 11243 hit points with an attack that does 1872 cold damage at initiative 15 11 + 4910 units each with 5381 hit points (immune to fire; weak to radiation) with an attack that does 10 slashing damage at initiative 19 12 + 13 + Infection: 14 + 1758 units each with 23776 hit points with an attack that does 24 radiation damage at initiative 2 15 + 4000 units each with 12869 hit points with an attack that does 5 cold damage at initiative 14 16 + 2319 units each with 43460 hit points (weak to bludgeoning, cold) with an attack that does 33 radiation damage at initiative 3 17 + 1898 units each with 44204 hit points (immune to cold; weak to radiation) with an attack that does 39 radiation damage at initiative 1 18 + 2764 units each with 50667 hit points (weak to slashing, radiation) with an attack that does 31 radiation damage at initiative 5 19 + 3046 units each with 27907 hit points (immune to radiation, fire) with an attack that does 16 slashing damage at initiative 7 20 + 1379 units each with 8469 hit points (immune to cold) with an attack that does 8 cold damage at initiative 20 21 + 1824 units each with 25625 hit points (immune to bludgeoning) with an attack that does 23 radiation damage at initiative 6 22 + 115 units each with 41114 hit points (immune to fire; weak to slashing, bludgeoning) with an attack that does 686 slashing damage at initiative 10 23 + 4054 units each with 51210 hit points (immune to radiation, cold, fire) with an attack that does 22 cold damage at initiative 12
+2
2018/outputs/24.txt
··· 1 + 18346 2 + 8698