Advertisement
Guest User

Untitled

a guest
Dec 15th, 2018
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.99 KB | None | 0 0
  1. def adjacents(point):
  2.     x, y = point
  3.     return sorted([(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)], key=lambda x: (x[1], x[0]))
  4.  
  5.  
  6. def targets(attacker, units):
  7.     faction = units[attacker][0]
  8.     targets = dict()
  9.     for unit in units:
  10.         if faction != units[unit][0]:
  11.             targets[unit] = units[unit]
  12.     return targets
  13.  
  14.  
  15. def in_range(targets, area, units):
  16.     in_range = list()
  17.     for target in targets:
  18.         for adjacent in adjacents(target):
  19.             if adjacent not in units and area[adjacent] != "#":
  20.                 in_range.append(adjacent)
  21.     return in_range
  22.  
  23.  
  24. def reachable(attacker, in_range, area, units):
  25.     reachable = list()
  26.     for point in in_range:
  27.         if pathsearch(attacker, point, area, units):
  28.             reachable.append(point)
  29.     return reachable
  30.  
  31.  
  32. def nearest(attacker, reachable, area, units):
  33.     nearest = list()
  34.     distances = []
  35.     for point in reachable:
  36.         distance = len(pathsearch(attacker, point, area, units))
  37.         if distance:
  38.             distances.append(distance)
  39.     for point in reachable:
  40.         if len(pathsearch(attacker, point, area, units)) == min(distances):
  41.             nearest.append(point)
  42.     return nearest
  43.  
  44.  
  45. def chosen(nearest):
  46.     return sorted(nearest, key=lambda x: (x[1], x[0]))[0]
  47.  
  48.  
  49. def pathsearch(origin, destination, area, units):
  50.     unvisited = dict()
  51.     visited = dict()
  52.     current = origin
  53.     current_distance = 0
  54.     unvisited[origin] = current_distance
  55.  
  56.     for point in area:
  57.         if area[point] != "#" and point not in units:
  58.             unvisited[point] = None
  59.  
  60.     while True:
  61.         for adjacent in adjacents(current):
  62.             if adjacent not in unvisited:
  63.                 continue
  64.             distance = current_distance + 1
  65.             if unvisited[adjacent] is None or unvisited[adjacent] > distance:
  66.                 unvisited[adjacent] = distance
  67.         visited[current] = current_distance
  68.         del unvisited[current]
  69.         candidates = [point for point in unvisited.items() if point[1]]
  70.         if destination in visited:
  71.             break
  72.         elif not candidates:
  73.             return False
  74.         else:
  75.             current, current_distance = sorted(candidates, key=lambda x: x[1])[0]
  76.  
  77.     path = [destination]
  78.  
  79.     while True:
  80.         next_step = []
  81.         for adjacent in adjacents(destination):
  82.             if adjacent in visited:
  83.                 next_step.append((adjacent[0], adjacent[1], visited[adjacent]))
  84.  
  85.         next_step = min(next_step, key=lambda x: (x[2], x[1], x[0]))
  86.         destination = (next_step[0], next_step[1])
  87.         if destination == origin:
  88.             break
  89.         path.append((next_step[0], next_step[1]))
  90.  
  91.     path.reverse()
  92.     return path
  93.  
  94.  
  95. def move(unit, area, units):
  96.     t = targets(unit, units)
  97.     # no targets remaining
  98.     if not t:
  99.         return False
  100.     for target in t:
  101.         # target adjacent so return without moving
  102.         if target in adjacents(unit):
  103.             return unit
  104.     ir = in_range(t, area, units)
  105.     r = reachable(unit, ir, area, units)
  106.     # no reachable point so return without moving
  107.     if not r:
  108.         return unit
  109.     n = nearest(unit, r, area, units)
  110.     c = chosen(n)
  111.     m = pathsearch(unit, c, area, units)[0]
  112.     units[m] = units[unit]
  113.     del units[unit]
  114.     area[unit] = "."
  115.     return m
  116.  
  117.  
  118. def main():
  119.     elves_attack_power = 2
  120.  
  121.     while True:
  122.         has_dead_elf = False
  123.         elves_attack_power += 1
  124.         if elves_attack_power > 3:
  125.             print(f"Trying ap {elves_attack_power}")
  126.  
  127.         data = [line for line in open('input', 'rt').readlines()]
  128.         area = dict()
  129.         units = dict()
  130.  
  131.         for y, datum in enumerate(data):
  132.             for x, char in enumerate(datum):
  133.                 if char == "#":
  134.                     area[(x, y)] = char
  135.                 elif char == ".":
  136.                     area[(x, y)] = char
  137.                 elif char == "E":
  138.                     units[(x, y)] = ("E", 200, elves_attack_power)
  139.                 elif char == "G":
  140.                     units[(x, y)] = ("G", 200, 3)
  141.  
  142.         round = 0
  143.         has_target = True
  144.  
  145.         while True:
  146.             for unit in sorted(units, key=lambda x: (x[1], x[0])):
  147.                 if unit not in units:
  148.                     continue
  149.  
  150.                 has_target = move(unit, area, units)
  151.                 if not has_target:
  152.                     break
  153.                 unit = has_target
  154.  
  155.                 targets = list()
  156.                 for adjacent in adjacents(unit):
  157.                     if adjacent in units and unit in units and units[adjacent][0] != units[unit][0]:
  158.                         targets.append((adjacent, units[adjacent][1]))
  159.  
  160.                 if targets:
  161.                     target = sorted(targets, key=lambda x: (x[1], x[0][1], x[0][0]))[0]
  162.                     target = target[0]
  163.                     units[target] = (units[target][0], units[target][1] - units[unit][2], units[target][2])
  164.                     if units[target][1] <= 0:
  165.  
  166.                         if units[target][0] == "E" and elves_attack_power != 3:
  167.                             has_dead_elf = True
  168.                             break
  169.  
  170.                         del units[target]
  171.                         area[target] = "."
  172.  
  173.             if not has_target or has_dead_elf:
  174.                 break
  175.  
  176.             round += 1
  177.  
  178.         if has_dead_elf:
  179.             continue
  180.  
  181.         total_hit_points = 0
  182.         for faction, hit_points, _ in units.values():
  183.             total_hit_points += hit_points
  184.  
  185.         if elves_attack_power == 3:
  186.             print(f"Part 1:")
  187.             print(f"Ended with {round} rounds and {total_hit_points} total hp")
  188.             print(f"Puzzle part 1 answer is {round * total_hit_points}")
  189.             print(f"Part 2:")
  190.         else:
  191.             print(f"Ended with {round} rounds and {total_hit_points} total hp")
  192.             print(f"Puzzle part 2 answer is {round * total_hit_points}")
  193.             return 0
  194.  
  195.  
  196. exit(main())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement