Solve 2018 day 15

This commit is contained in:
felixm 2024-07-13 15:23:09 -04:00
parent 9346e3f4d0
commit db82589857
2 changed files with 118 additions and 1 deletions

117
2018/d15.py Normal file
View File

@ -0,0 +1,117 @@
from lib import Grid2D, add2
from collections import deque
DIRS = [Grid2D.N, Grid2D.W, Grid2D.E, Grid2D.S]
def find_move(unit, grid):
assert grid[unit] in "GE"
target_fields = set()
enemies = grid.find("G") if grid[unit] == "E" else grid.find("E")
for e in enemies:
for dir in DIRS:
nb = add2(e, dir)
if grid[nb] == ".":
target_fields.add(nb)
# we are already next to an enemy
if unit in target_fields:
return None
seen = set()
states = deque([(unit, [])])
while states:
pos, hist = states.popleft()
if pos in seen:
continue
else:
seen.add(pos)
for dir in DIRS:
nb = add2(pos, dir)
if nb in target_fields:
if hist:
return hist[0]
else:
return nb
elif grid[nb] == ".":
nhist = hist + [nb]
states.append((nb, nhist))
# we did not find a path to an enemy
return None
def part_2(data):
for elf_attack in range(3, 100):
g = Grid2D(data)
pos_to_health = {pos: 200 for pos in g.find("GE")}
len_elfs_orig = len(g.find("E"))
rounds = 0
done = False
while not done:
for unit in sorted(list(pos_to_health.keys())):
if not unit in pos_to_health:
continue # unit died in the meantime
enemy_type = "G" if g[unit] == "E" else "E"
# move stage
for dir in DIRS:
nb = add2(unit, dir)
if g[nb] == enemy_type:
break # we are already next to an enemy
else:
npos = find_move(unit, g)
if npos is not None:
assert npos not in pos_to_health and g[npos] == "."
pos_to_health[npos] = pos_to_health[unit]
del pos_to_health[unit]
g[npos] = g[unit]
g[unit] = "."
unit = npos
# attack stage
enemy_type = "G" if g[unit] == "E" else "E"
enemy_hit = 999
enemy = None
for dir in DIRS:
nb = add2(unit, dir)
if g[nb] == enemy_type and pos_to_health[nb] < enemy_hit:
enemy_hit = pos_to_health[nb]
enemy = nb
if enemy is not None:
if g[enemy] == "G":
pos_to_health[enemy] -= elf_attack
else:
pos_to_health[enemy] -= 3
if pos_to_health[enemy] <= 0:
del pos_to_health[enemy]
g[enemy] = "."
if len(g.find("E")) == 0 or len(g.find("G")) == 0:
done = True
break
else:
rounds += 1
if elf_attack == 3:
print(sum(pos_to_health.values()) * rounds)
if len(g.find("E")) == len_elfs_orig:
print(sum(pos_to_health.values()) * (rounds))
break
def main():
input_file = __file__.replace(".py", ".txt")
with open(input_file) as f:
data = f.read()
part_2(data)
if __name__ == "__main__":
main()

View File

@ -103,7 +103,7 @@ written in Python.
- Day 12: 180:00
- Day 13: 73:09
- Day 14: 20:48
- Day 15:
- Day 15: 185:11
# 2022