Skip to content

Commit

Permalink
feat: add 2023 day 6 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelblijleven committed Dec 8, 2023
1 parent d0cff8a commit 6f002fc
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 3 deletions.
85 changes: 84 additions & 1 deletion src/adventofcode/year_2023/day_06_2023.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import math
import re
from itertools import starmap
from math import prod
Expand All @@ -17,6 +18,16 @@ def parse_input(data: list[str]) -> list[tuple[int, int]]:
return list(zip(times, distances, strict=True))


def parse_input_part_two(data: list[str]) -> tuple[int, int]:
"""
Parse input to a single pair of duration and distance
"""
pattern = re.compile("\\d+")
duration = int("".join(pattern.findall(data[0])))
distance = int("".join(pattern.findall(data[1])))
return duration, distance


def calculate_distance(hold: int, max_time: int) -> int:
"""
Calculate the distance you can travel within the max limit
Expand All @@ -38,6 +49,19 @@ def calculate_ways_to_win(duration: int, farthest_distance: int) -> int:
return len(list(winning_distances))


def calculate_ways_to_win_part_two(duration: int, farthest_distance: int) -> int:
"""
Calculate ways to beat the current farthest distance
"""
possibilities = zip(range(duration), [duration] * duration, strict=True)
winning_distances: int = 0
for possibility in possibilities:
if calculate_distance(*possibility) > farthest_distance:
winning_distances += 1

return winning_distances


def calculate_margin_of_error(data: list[str]) -> int:
"""
Calculate the margin of error
Expand All @@ -46,6 +70,44 @@ def calculate_margin_of_error(data: list[str]) -> int:
return prod(starmap(calculate_ways_to_win, parsed_input))


def calculate_margin_of_error_part_two(data: list[str]) -> int:
"""
Calculate the margin of error
"""
parsed_input = parse_input_part_two(data)
return calculate_ways_to_win(*parsed_input)


def calculate_margin_of_error_quadratic(data: list[str]) -> int:
"""
I suspected that the outcome of calculate_distance for x was a parabola,
when running calculate distance for x in range(12) with max time of 12 the values were:
0, 11, 20, 27, 32, 35, 32, 27, 20, 11, 0.
We can use a quadratic equation to solve the problem.
By first calculating the discriminant and then the upper and lower bound.
After calculating the bounds, we can add/subtract from that value until the
distance is no longer a winning distance, each time it is, add 1 to total
"""
duration, distance = parse_input_part_two(data)
discriminant = duration * duration - 4 * distance
lower_bound = math.ceil((-duration + math.sqrt(discriminant)) / - 2)
upper_bound = math.floor((-duration - math.sqrt(discriminant)) / - 2)

total = 0

while calculate_distance(lower_bound - 1, duration) > distance:
lower_bound = lower_bound - 1
total += 1

while calculate_distance(upper_bound + 1, duration) > distance:
upper_bound = upper_bound + 1
total += 1

return upper_bound - lower_bound + 1


@register_solution(2023, 6, 1)
def part_one(input_data: list[str]):
answer = calculate_margin_of_error(input_data)
Expand All @@ -58,7 +120,27 @@ def part_one(input_data: list[str]):

@register_solution(2023, 6, 2)
def part_two(input_data: list[str]):
answer = ...
answer = calculate_margin_of_error_part_two(input_data)

if not answer:
raise SolutionNotFoundError(2023, 6, 2)

return answer


@register_solution(2023, 6, 2, version="quadratic")
def part_two_quadratic(input_data: list[str]):
answer = calculate_margin_of_error_quadratic(input_data)

if not answer:
raise SolutionNotFoundError(2023, 6, 2)

return answer


@register_solution(2023, 6, 2, version="quadratic")
def part_two_quadratic(input_data: list[str]):
answer = calculate_margin_of_error_quadratic(input_data)

if not answer:
raise SolutionNotFoundError(2023, 6, 2)
Expand All @@ -70,3 +152,4 @@ def part_two(input_data: list[str]):
data = get_input_for_day(2023, 6)
part_one(data)
part_two(data)
part_two_quadratic(data)
12 changes: 10 additions & 2 deletions tests/adventofcode/year_2023/test_day_06_2023.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
calculate_ways_to_win,
parse_input,
part_one,
part_two,
part_two, parse_input_part_two, calculate_ways_to_win_part_two,
)

test_input = [
Expand All @@ -18,6 +18,10 @@ def test_parse_input():
assert parse_input(test_input) == [(7, 9), (15, 40), (30, 200)]


def test_parse_input_part_two():
assert parse_input_part_two(test_input) == (71530, 940200)


@pytest.mark.parametrize(
["hold", "max_time", "expected"],
[
Expand Down Expand Up @@ -47,9 +51,13 @@ def test_calculate_ways_to_win(duration, farthest_distance, expected):
assert calculate_ways_to_win(duration, farthest_distance) == expected


def test_calculate_ways_to_win_part_two():
assert calculate_ways_to_win_part_two(71530, 940200) == 71503


def test_part_one():
assert part_one(test_input) == 288


def test_part_two():
assert part_two(test_input) == "x"
assert part_two(test_input) == 71503

0 comments on commit 6f002fc

Please sign in to comment.