diff --git a/src/AoC_2024/Dazbo's_Advent_of_Code_2024.ipynb b/src/AoC_2024/Dazbo's_Advent_of_Code_2024.ipynb index c4aa85b..f119125 100644 --- a/src/AoC_2024/Dazbo's_Advent_of_Code_2024.ipynb +++ b/src/AoC_2024/Dazbo's_Advent_of_Code_2024.ipynb @@ -86,7 +86,7 @@ "from dataclasses import asdict, dataclass, field\n", "from enum import Enum, auto\n", "from functools import cache, reduce\n", - "from itertools import combinations, count, cycle, permutations\n", + "from itertools import combinations, count, cycle, permutations, product\n", "from getpass import getpass\n", "from pathlib import Path\n", "\n", @@ -2241,7 +2241,7 @@ "metadata": {}, "source": [ "---\n", - "## Day 7: title" + "## Day 7: Bridge Repair" ] }, { @@ -2282,9 +2282,31 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Day n Part 1\n", + "### Day 7 Part 1\n", "\n", - "Overview..." + "Our input data represents equations with operators missing! E.g.\n", + "\n", + "```text\n", + "190: 10 19\n", + "3267: 81 40 27\n", + "83: 17 5\n", + "156: 15 6\n", + "7290: 6 8 6 15\n", + "161011: 16 10 13\n", + "192: 17 8 14\n", + "21037: 9 7 18 13\n", + "292: 11 6 16 20\n", + "```\n", + "\n", + "- Operators are always evaluated left-to-right.\n", + "- We have two operators to use: `+` and `*`\n", + "\n", + "By inserting operator combinations between numbers, determine which equations could possibly be true. **What is their total calibration result?**\n", + "\n", + "#### Solution Approach\n", + "\n", + "- For each set of numbers on the right, there will be `n-1` operators to try.\n", + "- Let's use `itertools.product` to determine the unique arrangements of `n-1` operators, given two operators. We use `product` because it allows us to repeat an operator.\n" ] }, { @@ -2293,8 +2315,61 @@ "metadata": {}, "outputs": [], "source": [ - "def solve_part1(data):\n", - " pass" + "def process_input(data) -> list[tuple]:\n", + " \"\"\" Return equations in the form: [(answer, numbers), ...] \"\"\"\n", + " equations = []\n", + " for line in data:\n", + " ans, nums = line.split(\":\")\n", + " ans = int(ans)\n", + " nums = list(map(int, nums.split()))\n", + " equations.append((ans, nums))\n", + " \n", + " return equations\n", + "\n", + "@cache\n", + "def get_op_perms(ops: str, num_parameters: int):\n", + " \"\"\" How many ways of ordering our operators, for a given number of parameters \"\"\"\n", + " return list(product(ops, repeat=num_parameters-1))\n", + " \n", + "def apply_op(num_pair: tuple[int], op: str) -> int:\n", + " if op == \"+\":\n", + " return operator.add(*num_pair)\n", + " \n", + " if op == \"*\":\n", + " return operator.mul(*num_pair)\n", + " \n", + " if op == \"|\":\n", + " return int(\"\".join(str(num) for num in num_pair))\n", + " \n", + " assert False, \"Don't know what to do with op\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def solve(data, ops):\n", + " equations = process_input(data) # [0] is the ans; [1] are the numbers\n", + " total = 0\n", + " \n", + " for equation in tqdm(equations):\n", + " # get a list of operator combinations, with each element being of length n-1\n", + " # E.g. with 3 numbers, we'll get: [('+', '+'), ('+', '*'), ('*', '+'), ('*', '*')]\n", + " op_perms = get_op_perms(ops, len(equation[1]))\n", + " \n", + " for op_perm in op_perms: # a tuple of operators of length n-1\n", + " res = equation[1][0] # Initialize with the first number\n", + " for i, op in enumerate(op_perm):\n", + " res = apply_op((res, equation[1][i + 1]), op) # Correct indexing\n", + "\n", + " if res == equation[0]:\n", + " total += equation[0] # Add the correct value to the total\n", + " break # we only need one successful result per equestion\n", + " \n", + " return total\n", + " " ] }, { @@ -2305,16 +2380,27 @@ "source": [ "%%time\n", "sample_inputs = []\n", - "sample_inputs.append(\"\"\"abcdef\"\"\")\n", - "sample_answers = [\"uvwxyz\"]\n", + "sample_inputs.append(\"\"\"\\\n", + "190: 10 19\n", + "3267: 81 40 27\n", + "83: 17 5\n", + "156: 15 6\n", + "7290: 6 8 6 15\n", + "161011: 16 10 13\n", + "192: 17 8 14\n", + "21037: 9 7 18 13\n", + "292: 11 6 16 20\"\"\")\n", + "sample_answers = [3749]\n", + "\n", + "OPERATORS = \"+*\"\n", "\n", "for curr_input, curr_ans in zip(sample_inputs, sample_answers):\n", - " validate(solve_part1(curr_input), curr_ans) # test with sample data\n", + " validate(solve(curr_input.splitlines(), OPERATORS), curr_ans) # test with sample data\n", " logger.info(\"Test passed\")\n", "\n", "logger.info(\"All tests passed!\")\n", "\n", - "soln = solve_part1(input_data)\n", + "soln = solve(input_data, OPERATORS)\n", "logger.info(f\"Part 1 soln={soln}\")" ] }, @@ -2322,19 +2408,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Day n Part 2\n", + "### Day 7 Part 2\n", "\n", - "Overview..." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def solve_part2(data):\n", - " pass" + "Oh, surprise. There's a third operator. The op `||` combines digits from left and right into a single number. E.g. `12 || 345` results in `12345`" ] }, { @@ -2345,17 +2421,28 @@ "source": [ "%%time\n", "sample_inputs = []\n", - "sample_inputs.append(\"\"\"abcdef\"\"\")\n", - "sample_answers = [\"uvwxyz\"]\n", + "sample_inputs.append(\"\"\"\\\n", + "190: 10 19\n", + "3267: 81 40 27\n", + "83: 17 5\n", + "156: 15 6\n", + "7290: 6 8 6 15\n", + "161011: 16 10 13\n", + "192: 17 8 14\n", + "21037: 9 7 18 13\n", + "292: 11 6 16 20\"\"\")\n", + "sample_answers = [11387]\n", + "\n", + "OPERATORS = \"+*|\"\n", "\n", "for curr_input, curr_ans in zip(sample_inputs, sample_answers):\n", - " validate(solve_part2(curr_input), curr_ans) # test with sample data\n", - " logger.info(\"Test passed\") \n", + " validate(solve(curr_input.splitlines(), OPERATORS), curr_ans) # test with sample data\n", + " logger.info(\"Test passed\")\n", "\n", - "logger.info(\"Tests passed!\")\n", + "logger.info(\"All tests passed!\")\n", "\n", - "soln = solve_part2(input_data)\n", - "logger.info(f\"Part 2 soln={soln}\")" + "soln = solve(input_data, OPERATORS)\n", + "logger.info(f\"Part 1 soln={soln}\")" ] }, { @@ -2489,7 +2576,7 @@ "toc_visible": true }, "kernelspec": { - "display_name": ".aoc-conda-env", + "display_name": ".AoC-env", "language": "python", "name": "python3" }, @@ -2503,7 +2590,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.0" + "version": "3.11.9" }, "toc-autonumbering": false, "toc-showcode": false, diff --git a/src/AoC_2024/d07/input/input.txt b/src/AoC_2024/d07/input/input.txt new file mode 100644 index 0000000..ab9855e Binary files /dev/null and b/src/AoC_2024/d07/input/input.txt differ