diff --git a/src/adventofcode/year_2023/day_06_2023.py b/src/adventofcode/year_2023/day_06_2023.py new file mode 100644 index 0000000..54f93bd --- /dev/null +++ b/src/adventofcode/year_2023/day_06_2023.py @@ -0,0 +1,73 @@ +import re +from itertools import starmap + +from math import prod + +from adventofcode.util.exceptions import SolutionNotFoundError +from adventofcode.registry.decorators import register_solution +from adventofcode.util.input_helpers import get_input_for_day + + +def parse_input(data: list[str]) -> list[tuple[int, int]]: + """ + Zip the time and distance values into a list of tuples of int + """ + pattern = re.compile("\\d+") + times = map(int, pattern.findall(data[0])) + distances = map(int, pattern.findall(data[1])) + return list(zip(times, distances)) + + +def calculate_distance(hold: int, max_time: int) -> int: + """ + Calculate the distance you can travel within the max limit + for the given hold duration + """ + if hold == 0 or hold == max_time: + return 0 + + return (max_time - hold) * hold + + +def calculate_ways_to_win(duration: int, farthest_distance: int) -> int: + """ + Calculate ways to beat the current farthest distance + """ + possibilities = zip(range(duration + 1), [duration] * duration) + distances = starmap(calculate_distance, possibilities) + winning_distances = filter(lambda x: x > farthest_distance, distances) + return len(list(winning_distances)) + + +def calculate_margin_of_error(data: list[str]) -> int: + """ + Calculate the margin of error + """ + parsed_input = parse_input(data) + return prod(starmap(calculate_ways_to_win, parsed_input)) + + +@register_solution(2023, 6, 1) +def part_one(input_data: list[str]): + answer = calculate_margin_of_error(input_data) + + if not answer: + raise SolutionNotFoundError(2023, 6, 1) + + return answer + + +@register_solution(2023, 6, 2) +def part_two(input_data: list[str]): + answer = ... + + if not answer: + raise SolutionNotFoundError(2023, 6, 2) + + return answer + + +if __name__ == '__main__': + data = get_input_for_day(2023, 6) + part_one(data) + part_two(data) diff --git a/tests/adventofcode/year_2023/test_day_06_2023.py b/tests/adventofcode/year_2023/test_day_06_2023.py new file mode 100644 index 0000000..b0396e4 --- /dev/null +++ b/tests/adventofcode/year_2023/test_day_06_2023.py @@ -0,0 +1,44 @@ +import pytest + +from adventofcode.year_2023.day_06_2023 import (part_two, part_one, calculate_distance, parse_input, + calculate_ways_to_win) + +test_input = [ + "Time: 7 15 30", + "Distance: 9 40 200", +] + + +def test_parse_input(): + assert parse_input(test_input) == [(7, 9), (15, 40), (30, 200)] + + +@pytest.mark.parametrize(["hold", "max_time", "expected"], [ + (0, 7, 0), + (1, 7, 6), + (2, 7, 10), + (3, 7, 12), + (4, 7, 12), + (5, 7, 10), + (6, 7, 6), + (7, 7, 0), +]) +def test_calculate_distance(hold, max_time, expected): + assert calculate_distance(hold, max_time) == expected + + +@pytest.mark.parametrize(["duration", "farthest_distance", "expected"], [ + (7, 9, 4), + (15, 40, 8), + (30, 200, 9), +]) +def test_calculate_ways_to_win(duration, farthest_distance, expected): + assert calculate_ways_to_win(duration, farthest_distance) == expected + + +def test_part_one(): + assert part_one(test_input) == 288 + + +def test_part_two(): + assert part_two(test_input) == 'x'