From e32a98204d5d34c299b9b189fc9bb725b7df5b86 Mon Sep 17 00:00:00 2001 From: stanislas Date: Wed, 4 Dec 2024 11:16:44 +0100 Subject: [PATCH] day 3 - part 2 --- src/aoc_2024/day3/part2.py | 53 ++++++++++++++ .../day3/{test-data => test-data-part1} | 0 tests/aoc_2024/day3/test-data-part2 | 1 + tests/aoc_2024/day3/test_part1.py | 2 +- tests/aoc_2024/day3/test_part2.py | 73 +++++++++++++++++++ 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/aoc_2024/day3/part2.py rename tests/aoc_2024/day3/{test-data => test-data-part1} (100%) create mode 100644 tests/aoc_2024/day3/test-data-part2 create mode 100644 tests/aoc_2024/day3/test_part2.py diff --git a/src/aoc_2024/day3/part2.py b/src/aoc_2024/day3/part2.py new file mode 100644 index 0000000..950c80a --- /dev/null +++ b/src/aoc_2024/day3/part2.py @@ -0,0 +1,53 @@ +import re +from pathlib import Path +from typing import Match, List, Any + + +def detect_mul(data: str) -> list[Match]: + return re.findall(r"mul\((\d{1,3}),(\d{1,3})\)", data) + + +def compute_multiplication(data: str) -> int: + result = 0 + for match in detect_mul(data): + left = int(match[0]) + right = int(match[1]) + result += left * right + return result + + +def find_activated_parts(data: str) -> str: + res = [] + + start_match = re.search(r"^.*?don't\(\)", data) + if start_match: + start_data = start_match[0] + res.append(start_data) + data = data.removeprefix(start_data) + + middle_match = re.search(r"^.*don't\(\)", data) + if middle_match: + middle_data = middle_match[0] + matches = re.findall(r"do\(\).*?don't\(\)", middle_data) + res.extend(matches) + data = data.removeprefix(middle_data) + + end_match = re.search(r"do\(\).*$", data) + if end_match: + end_data = end_match[0] + res.append(end_data) + + return "".join(res) + + +def main(input_file: Path) -> int: + input_data = "" + for line in input_file.open(): + input_data += line.strip() + activated_parts = find_activated_parts(input_data) + return compute_multiplication(activated_parts) + + +if __name__ == "__main__": + file = Path(__file__).parent / "input-data" + print(main(file)) diff --git a/tests/aoc_2024/day3/test-data b/tests/aoc_2024/day3/test-data-part1 similarity index 100% rename from tests/aoc_2024/day3/test-data rename to tests/aoc_2024/day3/test-data-part1 diff --git a/tests/aoc_2024/day3/test-data-part2 b/tests/aoc_2024/day3/test-data-part2 new file mode 100644 index 0000000..b774ec9 --- /dev/null +++ b/tests/aoc_2024/day3/test-data-part2 @@ -0,0 +1 @@ +xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5)) \ No newline at end of file diff --git a/tests/aoc_2024/day3/test_part1.py b/tests/aoc_2024/day3/test_part1.py index 94a2cbb..ae53f40 100644 --- a/tests/aoc_2024/day3/test_part1.py +++ b/tests/aoc_2024/day3/test_part1.py @@ -61,5 +61,5 @@ def test_compute_multiplication2_from_multiple_muls(): def test_main(): - file = Path(__file__).parent / "test-data" + file = Path(__file__).parent / "test-data-part1" assert part1.main(file) == 161 diff --git a/tests/aoc_2024/day3/test_part2.py b/tests/aoc_2024/day3/test_part2.py new file mode 100644 index 0000000..1872645 --- /dev/null +++ b/tests/aoc_2024/day3/test_part2.py @@ -0,0 +1,73 @@ +from pathlib import Path + +from aoc_2024.day3 import part2 + + +def test_find_activated_parts(): + input_data = "xmul(2,4)&mul[3,7]!^don't()_mdo()ul(5,5)+mudon't()l(32,64](mul(11,8)undo()?mul(8,5))" + assert ( + part2.find_activated_parts(input_data) + == "xmul(2,4)&mul[3,7]!^don't()do()ul(5,5)+mudon't()do()?mul(8,5))" + ) + + +def test_detect_mul_is_successful(): + res = part2.detect_mul("mul(1,5)")[0] + assert res[0] == "1" + assert res[1] == "5" + + +def test_detect_mul_with_3_digits_is_successful(): + assert part2.detect_mul("mul(123,567)") + + +def test_detect_mul_with_4_digits_fails(): + assert not part2.detect_mul("mul(1234,5678)") + + +def test_detect_mul_with_space_fails(): + assert not part2.detect_mul("mul(123 ,678)") + + +def test_detect_mul_without_mul_fails(): + assert not part2.detect_mul("div(1,5)") + + +def test_detect_multiple_muls_is_successful(): + res = part2.detect_mul( + "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))" + ) + match1 = res[0] + assert match1[0] == "2" + assert match1[1] == "4" + match2 = res[1] + assert match2[0] == "5" + assert match2[1] == "5" + match3 = res[2] + assert match3[0] == "11" + assert match3[1] == "8" + match4 = res[3] + assert match4[0] == "8" + assert match4[1] == "5" + + +def test_compute_multiplication_from_mul(): + assert part2.compute_multiplication("mul(1,5)") == 5 + + +def test_compute_multiplication2_from_mul(): + assert part2.compute_multiplication("mul(8,5)") == 40 + + +def test_compute_multiplication2_from_multiple_muls(): + assert ( + part2.compute_multiplication( + "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))" + ) + == 161 + ) + + +def test_main(): + file = Path(__file__).parent / "test-data-part2" + assert part2.main(file) == 48