Advertisement
usesbiggerwords

AoC 2021 Day 17

Dec 17th, 2021
548
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.29 KB | None | 0 0
  1. # Just like bull's-eyeing wamp rats in my T-16 back home
  2.  
  3.  
  4. from copy import copy
  5. from aoccore import Vector2D
  6. from typing import List
  7. from collections import defaultdict
  8.  
  9.  
  10. class Rectangle:
  11.     def __init__(self, pt0: Vector2D, pt1: Vector2D):
  12.         self.x0 = pt0.x
  13.         self.x1 = pt1.x
  14.         self.y0 = pt0.y
  15.         self.y1 = pt1.y
  16.  
  17.     def __repr__(self):
  18.         return repr((self.x0, self.y0, self.x1, self.y1))
  19.  
  20.     def inside(self, pt: Vector2D):
  21.         return (self.x0 <= pt.x <= self.x1) and (self.y0 <= pt.y <= self.y1)
  22.  
  23.     def below(self, pt: Vector2D):
  24.         return (pt.x > max(self.x0, self.x1)) or (pt.y < min(self.y0, self.y1))
  25.  
  26.  
  27. EXAMPLE = Rectangle(Vector2D(20, -10), Vector2D(30, -5))
  28. INPUT = Rectangle(Vector2D(96, -144), Vector2D(125, -98))
  29.  
  30.  
  31. def do_physics(point: Vector2D, velocity: Vector2D) -> tuple:
  32.     """
  33.    Simulates a basic physics engine, with fixed gravity and drag forces, represented by the 'phob' object
  34.    :param point:
  35.    :param velocity:
  36.    :return:
  37.    """
  38.     phob = Vector2D(-1, -1)
  39.     new_pt = point.add(velocity)
  40.     vel = velocity.add(phob)
  41.     vel.x = 0 if vel.x < 0 else vel.x  # clamp forward velocity at zero since drag is a function of forward velocity
  42.     return new_pt, vel
  43.  
  44.  
  45. def display(path: List[Vector2D], target: Rectangle):
  46.     x_min = 0
  47.     x_max = max([target.x0, target.x1] + [p.x for p in path]) + 10
  48.     y_min = min([target.y0, target.y1] + [p.y for p in path]) - 10
  49.     y_max = max([target.y0, target.y1] + [p.y for p in path]) + 10
  50.     buffer = defaultdict(lambda: ' ')
  51.     for x in range(target.x0, target.x1):
  52.         for y in range(target.y0, target.y1):
  53.             buffer[Vector2D(x, y)] = '#'
  54.     for loc in path:
  55.         buffer[loc] = '*'
  56.         if target.inside(loc):
  57.             buffer[loc] = '@'
  58.     output = []
  59.     for y in range(y_max, y_min - 1, -1):
  60.         line = ''
  61.         for x in range(x_min, x_max):
  62.             line += buffer[Vector2D(x, y)]
  63.         output.append(line)
  64.     for line in output:
  65.         print(line)
  66.  
  67.  
  68. def trajectory(start: Vector2D, target: Rectangle, min_vel: Vector2D, max_vel: Vector2D, good_vis: List[Vector2D]):
  69.     trajectories, final_path = [], []
  70.     test_velocities = [Vector2D(x, y) for x in range(min_vel.x, max_vel.x + 1) for y in range(min_vel.y, max_vel.y + 1)]
  71.     for stv in test_velocities:
  72.         pt = copy(start)
  73.         tv = copy(stv)
  74.         pt_path = []
  75.         while True:
  76.             pt, tv = do_physics(pt, tv)
  77.             pt_path.append(pt)
  78.             if target.inside(pt):
  79.                 trajectories.append(pt_path)
  80.                 good_vis.append(stv)
  81.                 break
  82.             if target.below(pt):
  83.                 break
  84.         # display(pt_path, target)
  85.     if not trajectories:
  86.         min_vel = max_vel
  87.         max_vel = Vector2D(max_vel.x + min_vel.x, max_vel.y + min_vel.y)
  88.         max_height, good_vis = trajectory(start, target, min_vel, max_vel, good_vis)
  89.         return max_height, good_vis
  90.     max_height = max(p0.y for p in trajectories for p0 in p)
  91.     for t in trajectories:
  92.         if max_height in [pt.y for pt in t]:
  93.             final_path = t[:]
  94.             break
  95.     # display(final_path, target)
  96.     return max_height, good_vis
  97.  
  98.  
  99. def targeting_solutions(start: Vector2D, target: Rectangle, min_vel: Vector2D, max_vel: Vector2D) -> List[Vector2D]:
  100.     good_vis = []
  101.     test_velocities = [Vector2D(x, y) for x in range(min_vel.x, max_vel.x + 1) for y in range(min_vel.y, max_vel.y + 1)]
  102.     for stv in test_velocities:
  103.         pt = copy(start)
  104.         tv = copy(stv)
  105.         while True:
  106.             pt, tv = do_physics(pt, tv)
  107.             if target.inside(pt):
  108.                 good_vis.append(stv)
  109.                 break
  110.             if target.below(pt):
  111.                 break
  112.     return good_vis
  113.  
  114.  
  115. def part1(target: Rectangle):
  116.     start = Vector2D(0, 0)
  117.     for vy in range(0, 101, 10):
  118.         max_height, vis = trajectory(start, target, Vector2D(0, vy), Vector2D(10, vy + 10), [])
  119.         print('Max height:', max_height)
  120.  
  121.  
  122. def part2(target: Rectangle):
  123.     start = Vector2D(0, 0)
  124.     max_vx = target.x1
  125.     min_vy = target.y0
  126.     good_vis = targeting_solutions(start, target, Vector2D(0, min_vy), Vector2D(max_vx + 1, 2 * abs(min_vy)))
  127.     print('Good initial velocities', len(good_vis))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement