diff --git a/day-15/01.py b/day-15/01.py new file mode 100644 index 0000000..6cd530f --- /dev/null +++ b/day-15/01.py @@ -0,0 +1,202 @@ +from pprint import pprint +from time import sleep + +m = list() +hitpoints = dict() + + +with open('input.txt', 'r') as f: + for raw_line in f.readlines(): + line = list() + for field in raw_line.strip(): + line.append(field) + m.append(line) + +for x in range(len(m)): + for y in range(len(m[x])): + if m[x][y] in ['G', 'E']: + hitpoints[(x, y)] = 200 + +def get_patch(pos): + x, y = pos + if x < 0 or x >= len(m): + return None + if y < 0 or y >= len(m[x]): + return None + return m[x][y] + +def neighbors(pos): + return [ + (pos[0], pos[1] + 1), + (pos[0], pos[1] - 1), + (pos[0] + 1, pos[1]), + (pos[0] - 1, pos[1]) + ] + +def valid_neighbors(pos): + return [x for x in neighbors(pos) if get_patch(x) == '.'] + + +def flood(pos): + patches = set([pos]) + flood_map = { + pos: 0 + } + new = set(valid_neighbors(pos)) + i = 1 + while new: + patches.update(new) + new_new = set() + for p in new: + flood_map[p] = i + new_new.update(valid_neighbors(p)) + new = new_new - patches + i += 1 + return flood_map + +def get_units(): + """unit is a tuple (pos, symbol)""" + units = list() + for x in range(len(m)): + for y in range(len(m[x])): + if m[x][y] in ['G', 'E']: + units.append(((x, y), m[x][y])) + return units + +def get_closest_target(flood_map, targets): + reachable_targets = {pos: dist for pos, dist in flood_map.items() if pos in targets} + if len(reachable_targets) == 0: + return None + # print('Reachable targets:', reachable_targets) + min_range = min(reachable_targets.values()) + closest = [pos for pos, dist in reachable_targets.items() if dist == min_range] + # print('Closest targets:', closest) + closest.sort() + return closest[0] + +def get_enemies(unit): + return [e for e in get_units() if e[1] != unit[1]] + +def get_targets(unit): + targets = set() + for e in get_enemies(unit): + targets.update(valid_neighbors(e[0])) + return targets + +def get_enemies_in_range(unit): + x, y = unit[0] + enemies = get_enemies(unit) + neighbors = [ + (x + 1, y), + (x - 1, y), + (x, y + 1), + (x, y - 1) + ] + return [e for e in enemies if e[0] in neighbors] + +def move(unit, target): + move_to = dict() + neighbors = valid_neighbors(unit[0]) + # print('neighbors', neighbors) + for n in neighbors: + flood_map = flood(n) + # print(n, target, flood_map.get('target')) + if target in flood_map: + move_to[n] = flood_map[target] + # print(move_to) + min_range = min(move_to.values()) + closest = [pos for pos, dist in move_to.items() if dist == min_range] + closest.sort() + + x1, y1 = unit[0] + x2, y2 = closest[0] + m[x1][y1] = '.' + m[x2][y2] = unit[1] + + htp = hitpoints[unit[0]] + del hitpoints[unit[0]] + hitpoints[closest[0]] = htp + + return (closest[0]) + +def count_dwarfes(): + c = 0 + for line in m: + for sym in line: + if sym == 'E': + c += 1 + return c + +def count_goblins(): + c = 0 + for line in m: + for sym in line: + if sym == 'G': + c += 1 + return c + +def print_map(): + print(' ', ''.join([ str(y // 10) for y, _ in enumerate(m[0]) ])) + print(' ', ''.join([ str(y % 10) for y, _ in enumerate(m[0]) ])) + for i, line in enumerate(m): + htp_list = [(pos, htp) for pos, htp in hitpoints.items() if pos[0] == i] + htp_list.sort() + print(str(i).ljust(2), ''.join(line), [h[1] for h in htp_list]) + print(' ', ''.join([ str(y % 10) for y, _ in enumerate(m[0]) ])) + print(' ', ''.join([ str(y // 10) for y, _ in enumerate(m[0]) ])) + + +def main(): + i = 0 + while 1: + print() + print(f'After {i} rounds:') + print_map() + units = get_units() + units.sort() + for u in units: + if get_patch(u[0]) != u[1]: + continue + if count_dwarfes() == 0 or count_goblins() == 0: + return i + # print(f'UNIT: {u[1]} at {u[0]}') + e = get_enemies_in_range(u) + if len(e) == 0: + # move + targets = get_targets(u) + closest = get_closest_target(flood(u[0]), targets) + if closest is None: + # print('do nothing') + continue + d = move(u, closest) + u = (d, u[1]) + # print(f'Moved towards {closest} via {d}') + e = get_enemies_in_range(u) + if len(e) != 0: + # attack + atk_pos = [en[0] for en in e] + htp_map = {pos: val for pos, val in hitpoints.items() if pos in atk_pos} + min_htp = min(htp_map.values()) + atk_targets = [pos for pos, htp in htp_map.items() if htp == min_htp] + + target = atk_targets[0] + # print(f'Unit {u} attacking {target}') + hitpoints[target] -= 3 + if hitpoints[target] <= 0: + del hitpoints[target] + x, y = target + m[x][y] = '.' + + i += 1 + # pprint(hitpoints) + sleep(.1) + + +i = main() +print() +print(f'After {i + 1} rounds:') +print_map() +print() +print('Number of complete rounds:', i) +print('hitpoints:', sum(hitpoints.values())) +print('Result', i * sum(hitpoints.values())) \ No newline at end of file diff --git a/day-15/custom.txt b/day-15/custom.txt new file mode 100644 index 0000000..5d438a9 --- /dev/null +++ b/day-15/custom.txt @@ -0,0 +1,32 @@ +################################ +##########################..#### +#########################...#### +#########################..##### +########################G..##### +#####################.#.....##.# +#####################..........# +##############.#####...........# +########G...G#.####............# +#######......G....#.....#......# +#######...G....GG.#............# +#######G.G.............####....# +#######.#.....#####....E.....### +#######......#######.G.......### +#..####..G..#########.###..##### +#........G..#########.########## +#..#..#G....#########.########## +#.###...E...#########.########## +#####...G.G.#########.########## +########G....#######..########## +####..........#####...########## +####......E........G..########## +#.G..................########### +#G...................########### +###.....##E.......E..########### +###....#............############ +###.................############ +##G.....#.............########## +###########...#E..##..########## +###########.E...###.E.EE.####### +###########......#.......####### +################################ \ No newline at end of file diff --git a/day-15/e1.txt b/day-15/e1.txt new file mode 100644 index 0000000..4175f26 --- /dev/null +++ b/day-15/e1.txt @@ -0,0 +1,7 @@ +####### +#G..#E# +#E#E.E# +#G.##.# +#...#E# +#...E.# +####### \ No newline at end of file diff --git a/day-15/e2.txt b/day-15/e2.txt new file mode 100644 index 0000000..11b555e --- /dev/null +++ b/day-15/e2.txt @@ -0,0 +1,7 @@ +####### +#E..EG# +#.#G.E# +#E.##E# +#G..#.# +#..E#.# +####### \ No newline at end of file diff --git a/day-15/e3.txt b/day-15/e3.txt new file mode 100644 index 0000000..9929491 --- /dev/null +++ b/day-15/e3.txt @@ -0,0 +1,7 @@ +####### +#E.G#.# +#.#G..# +#G.#.G# +#G..#.# +#...E.# +####### \ No newline at end of file diff --git a/day-15/e4.txt b/day-15/e4.txt new file mode 100644 index 0000000..5541989 --- /dev/null +++ b/day-15/e4.txt @@ -0,0 +1,7 @@ +####### +#.E...# +#.#..G# +#.###.# +#E#G#G# +#...#G# +####### \ No newline at end of file diff --git a/day-15/e5.txt b/day-15/e5.txt new file mode 100644 index 0000000..95882b2 --- /dev/null +++ b/day-15/e5.txt @@ -0,0 +1,9 @@ +######### +#G......# +#.E.#...# +#..##..G# +#...##..# +#...#...# +#.G...G.# +#.....G.# +######### diff --git a/day-15/example.txt b/day-15/example.txt new file mode 100644 index 0000000..e2e60a9 --- /dev/null +++ b/day-15/example.txt @@ -0,0 +1,7 @@ +####### +#.G...# +#...EG# +#.#.#G# +#..G#E# +#.....# +####### \ No newline at end of file diff --git a/day-15/input.txt b/day-15/input.txt new file mode 100644 index 0000000..4f541ea --- /dev/null +++ b/day-15/input.txt @@ -0,0 +1,32 @@ +################################ +##########..#####...############ +##########.G..####..############ +########.....##.##.############# +####G..#G#.......#.############# +#G.....#.GG...G.##.############# +#.#...G.......#.##..############ +###..#.......#####......######## +######.......#####..G....####### +######..GG..######.......####### +#####.GG....##.####G......G.#### +###.............G...........#### +###.#.........#####G..G....##### +###..#.##....#######E.....###### +########....#########.#######..# +#########...#########.#######..# +########....#########..##......# +#########...#########...#...#.E# +#########...#########.......##.# +########....E#######.......##### +######........#####....E.####### +######.......E..E..G.E....###### +#######.............###....##### +#######............####.E...#### +#######...G....E##....##....#### +#######...............########## +############.E.......########### +###########.....#....########### +###########.....#....########### +###########.....###.############ +###########.#.################## +################################ \ No newline at end of file diff --git a/day-15/movement.txt b/day-15/movement.txt new file mode 100644 index 0000000..5d6aaaa --- /dev/null +++ b/day-15/movement.txt @@ -0,0 +1,9 @@ +######### +#G..G..G# +#.......# +#.......# +#G..E..G# +#.......# +#.......# +#G..G..G# +######### \ No newline at end of file