Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2021-12-17 - Trick Shot

(original .ipynb)

Day 17 puzzle input is a couple of x/y coordinate range pairs that describe a target box (mine is here). Part 1 involves calculating the highest y position you'd of a probe you chuck at this co-ordinate (there are some weird deceleration calcs given the initial x/y velocities you chuck the probe at). Part 2 involves finding the number of possible ways you can chuck the probe so that it hits the target range.

Really phoning it in today. Couldn't make my brain work and determine a suitable range of velocities to try so just totally busked it.

puzzle_input_str = "target area: x=119..176, y=-141..-84"
test_input_str = "target area: x=20..30, y=-10..-5"


def parse_range(range_str):
    start, end = range_str.split("..")
    return int(start), int(end)

def parse_input(input_str):
    range_str = input_str.removeprefix("target area: ")
    x_range, y_range = range_str.split(", ")
    return parse_range(x_range.removeprefix("x=")), parse_range(y_range.removeprefix("y="))
    

def step_x_velocity(x_vel):
    if x_vel > 0:
        return x_vel - 1
    elif x_vel < 0:
        return x_vel + 1
    return x_vel


def step_y_velocity(y_vel):
    return y_vel - 1


def approaching_area(x, y, x_vel, y_vel, x_range, y_range):
    x_min, x_max = x_range
    y_min, y_max = y_range
    above_and_sinking = x_vel == 0 and y > y_max
        
    return above_and_sinking or any((
        approaching_point(x, y, x_vel, y_vel, x_min, y_min),
        approaching_point(x, y, x_vel, y_vel, x_min, y_max),
        approaching_point(x, y, x_vel, y_vel, x_max, y_min),
        approaching_point(x, y, x_vel, y_vel, x_max, y_max))
    )
    
def probe_inside_target(x, y, x_range, y_range):
    x_min, x_max = x_range
    y_min, y_max = y_range
    return x >= x_min and x <= x_max and y >= y_min and y <= y_max
    
    
def approaching_point(x, y, x_vel, y_vel, target_x, target_y):
    approaching_x = abs(x - target_x) > abs(x + x_vel - target_x)
    approaching_y = abs(y - target_y) > abs(y + y_vel - target_y)
    
    return approaching_x or approaching_y
    
    
def test_velocity(x_range, y_range, x_vel, y_vel):
    x, y = 0, 0
    max_height = 0
    
    while approaching_area(x, y, x_vel, y_vel, x_range, y_range):
        if probe_inside_target(x, y, x_range, y_range):
            return True, max_height
    
        x += x_vel
        y += y_vel
        x_vel = step_x_velocity(x_vel)
        y_vel = step_y_velocity(y_vel)
        if y > max_height:
            max_height = y
        
    return probe_inside_target(x, y, x_range, y_range), max_height
    
    
def part_one(input_str):
    x_range, y_range = parse_input(input_str)
    max_height = 0
    
    for x_vel in range(200):
        for y_vel in range(500):
            res, height = test_velocity(x_range, y_range, x_vel, y_vel)
            
            if res and height > max_height:
                max_height = height
    
    return max_height

assert 45 == part_one(test_input_str)
print("part one:", part_one(puzzle_input_str))
part one: 9870
def part_two(input_str):
    x_range, y_range = parse_input(input_str)
    count = 0
    for x_vel in range(500):
        for y_vel in range(-200, 800):
            res, _ = test_velocity(x_range, y_range, x_vel, y_vel)
            if res:
                count += 1
                
    return count


assert 112 == part_two(test_input_str)
print("part two:", part_two(puzzle_input_str))
part two: 5523