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/15

+214
+180
2018/day15.py
··· 1 + import copy 2 + import fileinput 3 + from itertools import count 4 + 5 + from utils import Point 6 + 7 + 8 + class Entity: 9 + def __init__(self, x, y, type): 10 + self.pos = Point(x, y) 11 + self.hp = 200 12 + self.type = type 13 + 14 + def __str__(self): 15 + return "{}({} @ {},{})".format(self.type, self.hp, self.pos.x, self.pos.y) 16 + 17 + 18 + GRID = {} 19 + ENTITIES = [] 20 + 21 + for y, line in enumerate(fileinput.input()): 22 + for x, t in enumerate(line.strip()): 23 + if t == 'G' or t == 'E': 24 + ENTITIES.append(Entity(x, y, t)) 25 + GRID[Point(x, y)] = '.' 26 + else: 27 + GRID[Point(x, y)] = t 28 + 29 + 30 + def simulate(elf_dmg=3): 31 + grid = copy.deepcopy(GRID) 32 + entities = copy.deepcopy(ENTITIES) 33 + elves = set(e.pos for e in entities if e.type == 'E') 34 + goblins = set(e.pos for e in entities if e.type == 'G') 35 + 36 + def dmg(u): 37 + if u == 'E': 38 + return elf_dmg 39 + else: 40 + return 3 41 + 42 + def dist(p, q): 43 + horizon = [p] 44 + seen = set() 45 + 46 + depth = 0 47 + while horizon: 48 + next_horizon = [] 49 + for h in horizon: 50 + if h == q: 51 + return depth 52 + 53 + for n in h.neighbours_4(): 54 + if n in seen: 55 + continue 56 + 57 + if free(n): 58 + next_horizon.append(n) 59 + seen.add(n) 60 + 61 + horizon = next_horizon 62 + depth += 1 63 + 64 + return 1e8 65 + 66 + def free(p): 67 + return grid.get(p) == '.' and p not in goblins and p not in elves 68 + 69 + for curr_round in count(): 70 + entities.sort(key=lambda u: (u.pos.y, u.pos.x)) 71 + dead = set() 72 + 73 + for i, unit in enumerate(entities): 74 + if i in dead: 75 + continue 76 + 77 + enemies = goblins if unit.type == 'E' else elves 78 + selves = goblins if unit.type == 'G' else elves 79 + 80 + # First, attempt to attack if there are nearby enemies 81 + attack_can = [n for n in unit.pos.neighbours_4() if n in enemies] 82 + if attack_can: 83 + okays = [] 84 + for j, qnit in enumerate(entities): 85 + if qnit.pos in attack_can and unit.type != qnit.type: 86 + okays.append((qnit, j)) 87 + qnit, j = sorted(okays, key=lambda (qnit, j): (qnit.hp, qnit.pos.y, qnit.pos.x))[0] 88 + 89 + qnit.hp -= dmg(unit.type) 90 + if qnit.hp <= 0: 91 + enemies.remove(qnit.pos) 92 + dead.add(j) 93 + 94 + if not enemies: 95 + import sys 96 + rr = curr_round 97 + if i == len(entities) - len(dead): 98 + rr += 1 99 + score = sum(u.hp for u in entities if u.type == unit.type) 100 + return rr * score, unit.type 101 + 102 + # Process next entity, since we can't attack and then move 103 + continue 104 + 105 + # Otherwise, move according to pathfinding algorithm 106 + in_range = set() 107 + for e in enemies: 108 + for n in e.neighbours_4(): 109 + if free(n): 110 + in_range.add(n) 111 + 112 + horizon = [unit.pos] 113 + seen = set() 114 + 115 + next_spot = None 116 + 117 + while horizon: 118 + found = set() 119 + next_horizon = [] 120 + for h in horizon: 121 + for n in h.neighbours_4(): 122 + if n in seen: 123 + continue 124 + 125 + if free(n): 126 + next_horizon.append(n) 127 + seen.add(n) 128 + if n in in_range: 129 + found.add(n) 130 + 131 + if found: 132 + next_spot = sorted(found, key=lambda zz: (zz.y, zz.x))[0] 133 + 134 + poss = [] 135 + for n in unit.pos.neighbours_4(): 136 + if free(n): 137 + poss.append((dist(n, next_spot), n)) 138 + 139 + poss.sort(key=lambda z: (z[0], z[1].y, z[1].x)) 140 + 141 + next_spot = poss[0][1] 142 + 143 + selves.remove(unit.pos) 144 + selves.add(next_spot) 145 + 146 + unit.pos = next_spot if next_spot is not None else unit.pos 147 + break 148 + 149 + horizon = next_horizon 150 + 151 + # Now try attacking again 152 + attack_can = [n for n in unit.pos.neighbours_4() if n in enemies] 153 + if attack_can: 154 + # Attack 155 + okays = [] 156 + for j, qnit in enumerate(entities): 157 + if qnit.pos in attack_can and unit.type != qnit.type: 158 + okays.append((qnit, j)) 159 + qnit, j = sorted(okays, key=lambda (qnit, j): (qnit.hp, qnit.pos.y, qnit.pos.x))[0] 160 + 161 + qnit.hp -= dmg(unit.type) 162 + if qnit.hp <= 0: 163 + enemies.remove(qnit.pos) 164 + dead.add(j) 165 + 166 + if not enemies: 167 + import sys 168 + rr = curr_round 169 + if i == len(entities) - len(dead): 170 + rr += 1 171 + score = sum(u.hp for u in entities if u.type == unit.type) 172 + return rr * score, unit.type 173 + 174 + # Clean up any entities who have died 175 + for j in reversed(sorted(dead)): 176 + del entities[j] 177 + 178 + 179 + print "Outcome of initial simulation:", simulate()[0] 180 + print "Outcome of elf victory:", simulate(elf_dmg=23)[0]
+32
2018/inputs/15.txt
··· 1 + ################################ 2 + #########....G#######.########## 3 + ##########.G########...######### 4 + ###########.########.#.######### 5 + ###########.#..G######..######.# 6 + ##########..#...###......G..##.# 7 + ##.#######......#..G....E#.....# 8 + ##.##..######...........E..E#### 9 + ##.##...###................##### 10 + #.....G.G...........G.....###### 11 + #...G....G...................### 12 + #G.G.............EG..........### 13 + #..G..........#####.........#### 14 + ##.G.......G.#######.........### 15 + #####....G..#########..G.E....## 16 + ####....#...#########.........## 17 + #######.##..#########...#.....## 18 + #########...#########.....###### 19 + ##########..#########G....###### 20 + ##########...#######..E...###### 21 + ##########....#####...#EE.###### 22 + #########.........G.......###### 23 + #########............########### 24 + ############..........########## 25 + ############.......#.########### 26 + ###########...........########## 27 + ###########........#.########### 28 + ##########E.#......############# 29 + #########...##....E.############ 30 + #########.######....############ 31 + ################....############ 32 + ################################
+2
2018/outputs/15.txt
··· 1 + 207059 2 + 49120