From 21436fdb21c3a9968a024c97e308e1f49e4fa0f7 Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Fri, 17 Dec 2021 11:22:12 -0800 Subject: [PATCH] Add files via upload --- ipynb/Advent-2021.ipynb | 1273 ++++++++++++++++++++++++++++++--------- 1 file changed, 1001 insertions(+), 272 deletions(-) diff --git a/ipynb/Advent-2021.ipynb b/ipynb/Advent-2021.ipynb index be8483e..72223b9 100644 --- a/ipynb/Advent-2021.ipynb +++ b/ipynb/Advent-2021.ipynb @@ -12,11 +12,11 @@ "\n", "To fully understand each puzzle's instructions, click on the link (e.g. [**Day 1**](https://adventofcode.com/2021/day/1)); I give only brief summaries here. \n", "\n", - "Part of the idea of AoC is that you have to make some design choices to solve part 1 *before* you get to see the instructions for part 2. So there is a tension of wanting the solution to part 1 to provide components that can be re-used in part 2, without falling victim to [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it). In this notebook I won't refactor the code for part 1 after I see what is in part 2 (although I may edit the code for clarity, without changing the initial approach).\n", + "Part of the idea of AoC is that you have to make some design choices to solve Part 1 *before* you get to see the instructions for Part 2. So there is a tension of wanting the solution to Part 1 to provide components that can be re-used in Part 2, without falling victim to [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it). In this notebook I won't refactor the code for Part 1 after I see what is requested in Part 2 (although I may edit the code for clarity, without changing the initial approach).\n", "\n", "# Day 0: Preparations\n", "\n", - "First, imports that I have used in past AoC years:" + "Imports that I have used in past AoC years:" ] }, { @@ -43,12 +43,19 @@ "Each day's work will consist of three tasks, denoted by three bulleted section:\n", "- **Input**: Parse the day's input file. I will use the function `parse(day, parser, sep)`, which:\n", " - Reads the input file for `day`.\n", - " - Prints out the first few lines of the file (to remind me, and the notebook reader, what's there).\n", + " - Prints out the first few lines of the file (to remind me, and the notebook reader, what's in the file).\n", " - Breaks the file into a sequence of *entries* separated by `sep` (default newline).\n", " - Applies `parser` to each entry and returns the results as a tuple.\n", " - Useful parser functions include `ints`, `digits`, `atoms`, `words`, and the built-ins `int` and `str`.\n", - "- **Part 1**: Write code to compute the answer to Part 1, and submit the answer to the AoC site. Use the function `answer` to record the correct answer here in the notebook and serve as a regression test when the notebook is re-run. If there are non-trivial components in this code, I might provide unit tests for them using `assert`.\n", - "- **Part 2**: Repeat coding and `answer` for Part 2.\n" + "- **Part 1**: Understand the day's instructions and:\n", + " - Write code to compute the answer to Part 1.\n", + " - Record the answer with the `answer` function, which also serves as a unit test when the notebook is re-run.\n", + "- **Part 2**: Understand the second part of the instructions and:\n", + " - Write code and record `answer` for Part 2.\n", + " \n", + "Occasionally I'll introduce a **Part 3** where I explore beyond the instructions.\n", + "\n", + "Here are the helper functions for `answer` and `parse`:" ] }, { @@ -64,36 +71,51 @@ "\n", "def parse(day, parser=str, sep='\\n', print_lines=7) -> tuple:\n", " \"\"\"Split the day's input file into entries separated by `sep`, and apply `parser` to each.\"\"\"\n", - " text = open(f'AOC2021/input{day}.txt').read()\n", + " fname = f'AOC2021/input{day}.txt'\n", + " text = open(fname).read()\n", " entries = mapt(parser, text.rstrip().split(sep))\n", " if print_lines:\n", - " lines = text.splitlines()[:print_lines]\n", - " head = f'First {len(lines)} lines of Day {day} input (which is parsed into {len(entries)} entries)'\n", - " print(f'{head}:\\n{\"-\" * len(head)}')\n", + " all_lines = text.splitlines()\n", + " lines = all_lines[:print_lines]\n", + " head = f'{fname} ➜ {len(text)} chars, {len(all_lines)} lines; first {len(lines)} lines:'\n", + " dash = \"-\" * 100\n", + " print(f'{dash}\\n{head}\\n{dash}')\n", " for line in lines:\n", - " print(line if len(line) <= 100 else line[:100] + ' ...')\n", + " print(trunc(line))\n", + " print(f'{dash}\\nparse({day}) ➜ {len(entries)} entries:\\n'\n", + " f'{dash}\\n{trunc(str(entries))}\\n{dash}')\n", " return entries\n", "\n", + "def trunc(s: str, left=70, right=25, dots=' ... ') -> str: \n", + " \"\"\"All of string s if it fits; else left and right ends of s with dots in the middle.\"\"\"\n", + " dots = ' ... '\n", + " return s if len(s) <= left + right + len(dots) else s[:left] + dots + s[-right:]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "Char = str # Intended as the type of a one-character string\n", + "Atom = Union[float, int, str]\n", + "\n", "def ints(text: str) -> Tuple[int]:\n", " \"\"\"A tuple of all the integers in text, ignoring non-number characters.\"\"\"\n", - " return mapt(int, re.findall('-?[0-9]+', text))\n", + " return mapt(int, re.findall(r'-?[0-9]+', text))\n", "\n", "def digits(text: str) -> Tuple[int]:\n", " \"\"\"A tuple of all the digits in text (as ints 0–9), ignoring non-digit characters.\"\"\"\n", - " return mapt(int, re.findall('[0-9]', text))\n", + " return mapt(int, re.findall(r'[0-9]', text))\n", "\n", "def words(text: str) -> List[str]:\n", " \"\"\"A list of all the alphabetic words in text, ignoring non-letters.\"\"\"\n", - " return re.findall('[a-zA-Z]+', text)\n", + " return re.findall(r'[a-zA-Z]+', text)\n", "\n", - "Char = str # Intended as a one-character string\n", - "\n", - "Atom = Union[float, int, str]\n", - "\n", - "def atoms(text: str, sep=None) -> Tuple[Atom]:\n", - " \"\"\"A tuple of all the atoms (numbers or strs) in text.\n", - " By default, atoms are space-separated but you can change that with `sep`.\"\"\"\n", - " return mapt(atom, text.split(sep))\n", + "def atoms(text: str) -> Tuple[Atom]:\n", + " \"\"\"A tuple of all the atoms (numbers or symbol names) in text.\"\"\"\n", + " return mapt(atom, re.findall(r'[a-zA-Z_0-9.+-]+', text))\n", "\n", "def atom(text: str) -> Atom:\n", " \"\"\"Parse text into a single float or int or str.\"\"\"\n", @@ -117,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -137,10 +159,10 @@ " self[val].append(key)\n", "\n", "def prod(numbers) -> float: # Will be math.prod in Python 3.8\n", - " \"\"\"The product formed by multiplying numbers together.\"\"\"\n", + " \"\"\"The product formed by multiplying `numbers` together.\"\"\"\n", " result = 1\n", - " for n in numbers:\n", - " result *= n\n", + " for x in numbers:\n", + " result *= x\n", " return result\n", "\n", "def total(counter: Counter) -> int: \n", @@ -148,11 +170,11 @@ " return sum(counter.values())\n", "\n", "def sign(x) -> int: return (0 if x == 0 else +1 if x > 0 else -1)\n", - " \n", - "def dotproduct(A, B) -> float: return sum(a * b for a, b in zip(A, B))\n", "\n", "def transpose(matrix) -> list: return list(zip(*matrix))\n", "\n", + "def nothing(*args) -> None: return None\n", + "\n", "cat = ''.join\n", "flatten = chain.from_iterable\n", "cache = lru_cache(None)" @@ -162,12 +184,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "A lot of puzzles seem to involve (x, y) points on a rectangular grid, so I'll define `Point` and `Grid`:" + "Some past puzzles involve (x, y) points on a rectangular grid, so I'll define `Point` and `Grid`:" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -220,22 +242,28 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 1 input (which is parsed into 2000 entries):\n", - "----------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input1.txt ➜ 9756 chars, 2000 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "148\n", "167\n", "168\n", "169\n", "182\n", "188\n", - "193\n" + "193\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(1) ➜ 2000 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "(148, 167, 168, 169, 182, 188, 193, 209, 195, 206, 214, 219, 225, 219, ... , 5604, 5623, 5626, 5625)\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -252,7 +280,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -261,7 +289,7 @@ "True" ] }, - "execution_count": 6, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -284,7 +312,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -293,7 +321,7 @@ "True" ] }, - "execution_count": 7, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -311,12 +339,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "- **Part 3**: Visualization\n", + "\n", "Let's take a look at where the depths are taking us:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -351,27 +381,33 @@ "\n", "- **Input**: Each entry in the input is a command name (\"forward\", \"down\", or \"up\") followed by an integer.\n", "\n", - "I'll parse a command into a tuple like `('forward', 1)`." + "I'll parse a command into a tuple like `('forward', 2)`." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 2 input (which is parsed into 1000 entries):\n", - "----------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input2.txt ➜ 7723 chars, 1000 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "forward 2\n", "down 7\n", "down 8\n", "forward 9\n", "down 8\n", "forward 9\n", - "forward 8\n" + "forward 8\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(2) ➜ 1000 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "(('forward', 2), ('down', 7), ('down', 8), ('forward', 9), ('down', 8) ... ard', 3), ('forward', 6))\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -388,7 +424,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -397,7 +433,7 @@ "True" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -426,7 +462,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -435,7 +471,7 @@ "True" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -467,22 +503,28 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 3 input (which is parsed into 1000 entries):\n", - "----------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input3.txt ➜ 13000 chars, 1000 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "101000111100\n", "000011111101\n", "011100000100\n", "100100010000\n", "011110010100\n", "101001100000\n", - "110001010000\n" + "110001010000\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(3) ➜ 1000 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "('101000111100', '000011111101', '011100000100', '100100010000', '0111 ... 1100111', '110111100100')\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -499,7 +541,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -508,7 +550,7 @@ "True" ] }, - "execution_count": 13, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -549,7 +591,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -558,7 +600,7 @@ "True" ] }, - "execution_count": 14, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -595,22 +637,28 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 4 input (which is parsed into 101 entries):\n", - "---------------------------------------------------------------\n", - "73,42,95,35,13,40,99,92,33,30,83,1,36,93,59,90,55,25,77,44,37,62,41,47,80,23,51,61,21,20,76,8,71,34, ...\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input4.txt ➜ 7890 chars, 601 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", + "73,42,95,35,13,40,99,92,33,30,83,1,36,93,59,90,55,25,77,44,37,62,41,47 ... 7,84,86,45,75,60,15,14,11\n", "\n", "91 5 64 81 34\n", "15 99 31 63 65\n", "45 39 54 93 83\n", "51 14 23 86 32\n", - "19 22 16 13 3\n" + "19 22 16 13 3\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(4) ➜ 101 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "((73, 42, 95, 35, 13, 40, 99, 92, 33, 30, 83, 1, 36, 93, 59, 90, 55, 2 ... 69, 17, 49, 91, 30, 33))\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -634,7 +682,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -643,7 +691,7 @@ "True" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -693,7 +741,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -702,7 +750,7 @@ "True" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -742,22 +790,28 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 5 input (which is parsed into 500 entries):\n", - "---------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input5.txt ➜ 9249 chars, 500 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "409,872 -> 409,963\n", "149,412 -> 281,280\n", "435,281 -> 435,362\n", "52,208 -> 969,208\n", "427,265 -> 884,265\n", "779,741 -> 779,738\n", - "949,41 -> 13,977\n" + "949,41 -> 13,977\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(5) ➜ 500 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "((409, 872, 409, 963), (149, 412, 281, 280), (435, 281, 435, 362), (52 ... 13), (919, 123, 88, 954))\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -774,7 +828,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -783,7 +837,7 @@ "True" ] }, - "execution_count": 19, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -827,7 +881,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -836,7 +890,7 @@ "True" ] }, - "execution_count": 20, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -877,16 +931,22 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 1 lines of Day 6 input (which is parsed into 300 entries):\n", - "---------------------------------------------------------------\n", - "5,4,3,5,1,1,2,1,2,1,3,2,3,4,5,1,2,4,3,2,5,1,4,2,1,1,2,5,4,4,4,1,5,4,5,2,1,2,5,5,4,1,3,1,4,2,4,2,5,1, ...\n" + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input6.txt ➜ 600 chars, 1 lines; first 1 lines:\n", + "----------------------------------------------------------------------------------------------------\n", + "5,4,3,5,1,1,2,1,2,1,3,2,3,4,5,1,2,4,3,2,5,1,4,2,1,1,2,5,4,4,4,1,5,4,5, ... 5,5,1,3,1,4,2,3,3,1,4,1,1\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(6) ➜ 300 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "(5, 4, 3, 5, 1, 1, 2, 1, 2, 1, 3, 2, 3, 4, 5, 1, 2, 4, 3, 2, 5, 1, 4, ... , 4, 2, 3, 3, 1, 4, 1, 1)\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -907,7 +967,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -916,7 +976,7 @@ "True" ] }, - "execution_count": 22, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -951,7 +1011,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -960,7 +1020,7 @@ "True" ] }, - "execution_count": 23, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -989,16 +1049,22 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 1 lines of Day 7 input (which is parsed into 1000 entries):\n", - "----------------------------------------------------------------\n", - "1101,1,29,67,1102,0,1,65,1008,65,35,66,1005,66,28,1,67,65,20,4,0,1001,65,1,65,1106,0,8,99,35,67,101, ...\n" + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input7.txt ➜ 3887 chars, 1 lines; first 1 lines:\n", + "----------------------------------------------------------------------------------------------------\n", + "1101,1,29,67,1102,0,1,65,1008,65,35,66,1005,66,28,1,67,65,20,4,0,1001, ... 684,51,1186,1801,627,1379\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(7) ➜ 1000 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "(1101, 1, 29, 67, 1102, 0, 1, 65, 1008, 65, 35, 66, 1005, 66, 28, 1, 6 ... 1, 1186, 1801, 627, 1379)\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -1017,7 +1083,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -1026,7 +1092,7 @@ "True" ] }, - "execution_count": 25, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -1050,7 +1116,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -1059,7 +1125,7 @@ "True" ] }, - "execution_count": 26, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -1084,12 +1150,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Now that I got the right answer and have some time to think about it, if the travel cost were exactly quadratic, we would be minimizing the sum of square distances, and Legendre and Gauss knew that the **mean**, not the **median**, is the alignment point that does that. What's the mean of the positions?" + "- **Part 3**: Analysis and Visualization\n", + "\n", + "Now that I got the right answer and have some time to think about it, if the travel cost were exactly quadratic, we would be minimizing the sum of square distances, and Legendre and Gauss knew that the **mean**, not the **median**, is the alignment point that does that. What's the mean of the positions?" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -1098,7 +1166,7 @@ "490.543" ] }, - "execution_count": 27, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -1117,7 +1185,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -1126,7 +1194,7 @@ "{490: 95519693, 491: 95519725, 490.543: 95519083.0}" ] }, - "execution_count": 28, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -1147,7 +1215,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -1156,7 +1224,7 @@ "[376.0, 490.543]" ] }, - "execution_count": 29, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" }, @@ -1201,22 +1269,28 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 8 input (which is parsed into 200 entries):\n", - "---------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input8.txt ➜ 16614 chars, 200 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "daegb gadbcf cgefda edcfagb dfg acefbd fdgab fg bdcfa fcgb | cdfgba fgbc dbfac gfadbc\n", "bdfc dcbegf bf egfbcda gebad cfgaed bfe edfgc aegfcb gebdf | fb fb bcdfaeg fcgdeb\n", "cebdgaf bfcd gceab bf bfcea gceafd ecdfa fegdab bfcade fba | dfcb dagfbe fbaged bfa\n", "efabcg aegcdb fgaed fac dgafbc becf eadcgbf aegfc fc cagbe | ecgfa agdef eagfc gdceab\n", "fcdae cdeabf fga gf gabfde cgadb gadebfc cgfe aegcdf afgcd | fbgadce gadefb fag bafegd\n", "gecadbf bgc dacgf gaecbf cbeda dbfg bgdca bg bafcgd gdacef | cdgfa fceabg dgfb dgabc\n", - "fbecdga gcdbea cegab fc cafe cfg ebgdf cbgfe afbgec bagcdf | feac acegb bfagce gcafbe\n" + "fbecdga gcdbea cegab fc cafe cfg ebgdf cbgfe afbgec bagcdf | feac acegb bfagce gcafbe\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(8) ➜ 200 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "((('daegb', 'gadbcf', 'cgefda', 'edcfagb', 'dfg', 'acefbd', 'fdgab', ' ... cdg', 'agecb', 'acbeg')))\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -1226,7 +1300,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -1247,7 +1321,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -1256,7 +1330,7 @@ "True" ] }, - "execution_count": 32, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -1290,7 +1364,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -1299,7 +1373,7 @@ "True" ] }, - "execution_count": 33, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -1338,22 +1412,28 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 9 input (which is parsed into 100 entries):\n", - "---------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input9.txt ➜ 10100 chars, 100 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "9897656789865467895698765469899988672134598894345689864101378965457932349943210987654789653198789434\n", "8789542499996878954329984398789976561012987789245678953212567892345791998899329899765678969997668912\n", "7678943978987989965998993297649875432129876567956789864487678991056899877778939769886789998766457899\n", "4578999868998996899867894976532986543299876476897899987569899989167898766567898654998898998655345678\n", "2456987657679535679756799988643498657987654345789978899789998878998919954349997543219967987543237889\n", "1234896545568986798645678999754989767898765456998769759899987765789329863238898659301256798793156891\n", - "2346789432379997987434689489899879898919876567899954346998796434678997642127789798512345989989247892\n" + "2346789432379997987434689489899879898919876567899954346998796434678997642127789798512345989989247892\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(9) ➜ 100 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "((9, 8, 9, 7, 6, 5, 6, 7, 8, 9, 8, 6, 5, 4, 6, 7, 8, 9, 5, 6, 9, 8, 7, ... 6, 7, 9, 7, 6, 8, 7, 9))\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -1372,7 +1452,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -1381,7 +1461,7 @@ "True" ] }, - "execution_count": 35, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -1412,7 +1492,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -1421,7 +1501,7 @@ "True" ] }, - "execution_count": 36, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1453,12 +1533,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "That's the correct answer; I'm done. But I do want to check that the set of low points is the same as the set of basins I identified:" + "- **Part 3**: Verification and Visualization\n", + "\n", + "I want to check that the set of low points is the same as the set of basins I identified:" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -1467,7 +1549,7 @@ "249" ] }, - "execution_count": 37, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1482,12 +1564,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "I would like to visualize the basins. I'll use a scatter plot that displays the height 9 locations in yellow and the height 0 locations in deep purple, with a gradient in between:" + "I would also like to visualize the basins. I'll use a scatter plot that displays the height 9 locations in yellow and the height 0 locations in deep purple, with a gradient in between:" ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -1523,7 +1605,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -1563,22 +1645,28 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 10 input (which is parsed into 102 entries):\n", - "----------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input10.txt ➜ 10196 chars, 102 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "[(([{<{(<{{[({{}{}}{[]()})<{{}()}>]}}(([{{{}[]}[[]()]}[<{}[]]{()()}]](({{}{}}{{}()}))){[{({}())[[\n", "<(({[<([{({[{{<>()}}[{<>()}({}{})]]<{<()<>>{[]()}}(((){}>[[][]])>}([{<[]{}>(<>[])}]))<[[[[[][]\n", - "(<<(<{{{{<<<[(()<>){()<>}][[()()]]>{<{[]{}}<<>()>>}>{(<{<>}([]{})><(<>())<(){}>>)<(([]{})(()()))<<() ...\n", + "(<<(<{{{{<<<[(()<>){()<>}][[()()]]>{<{[]{}}<<>()>>}>{(<{<>}([]{})><(<> ... []{})(()()))<<()[]>{{}[]}\n", "[[[[<[{[(<{{{({}<>)((){})}((()())[()()])}}><[([((){})]<[()[]]{{}<>}>)[[{[]<>}][([]{})[{}()]]]]>)<{(<\n", "[<(<[[((<{((<<<>[]>><<<>{}>>){<[{}<>][<>[]]><<<>()>[(){}]>})[<{[{}<>][(){}]}<[[]<>][{}[]])>{([<>[]][\n", "(([[[[<([[{([{<>()}{()<>}][((){})]){[{[]<>}({}<>)][(<><>)[()[]]]}}<{{({}{}){[]{}}}<{<><>}({}{})>}>\n", - "{{{[<(<([<{({{[]()}[{}()]}{<()<>>(()<>)})}><<[{<()()>(()[])}<<<>[]]>][<{()}{<><>}>({{}[]})]>>](\n" + "{{{[<(<([<{({{[]()}[{}()]}{<()<>>(()<>)})}><<[{<()()>(()[])}<<<>[]]>][<{()}{<><>}>({{}[]})]>>](\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(10) ➜ 102 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "('[(([{<{(<{{[({{}{}}{[]()})<{{}()}>]}}(([{{{}[]}[[]()]}[<{}[]]{()()}] ... []}>][<({}<>)>]]>)[[[((')\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -1600,7 +1688,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -1609,7 +1697,7 @@ "True" ] }, - "execution_count": 41, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1644,7 +1732,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 43, "metadata": {}, "outputs": [ { @@ -1653,7 +1741,7 @@ "True" ] }, - "execution_count": 42, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1685,22 +1773,28 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 11 input (which is parsed into 10 entries):\n", - "---------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input11.txt ➜ 110 chars, 10 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "1224346384\n", "5621128587\n", "6388426546\n", "1556247756\n", "1451811573\n", "1832388122\n", - "2748545647\n" + "2748545647\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(11) ➜ 10 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "((1, 2, 2, 4, 3, 4, 6, 3, 8, 4), (5, 6, 2, 1, 1, 2, 8, 5, 8, 7), (6, 3 ... 2, 4, 8, 7, 6, 6, 2, 7))\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -1719,7 +1813,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 45, "metadata": {}, "outputs": [ { @@ -1728,7 +1822,7 @@ "True" ] }, - "execution_count": 44, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1771,7 +1865,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 46, "metadata": {}, "outputs": [ { @@ -1780,7 +1874,7 @@ "True" ] }, - "execution_count": 45, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -1821,22 +1915,28 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 12 input (which is parsed into 22 entries):\n", - "---------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input12.txt ➜ 144 chars, 22 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "xx-xh\n", "vx-qc\n", "cu-wf\n", "ny-LO\n", "cu-DR\n", "start-xx\n", - "LO-vx\n" + "LO-vx\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(12) ➜ 22 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "(['xx', 'xh'], ['vx', 'qc'], ['cu', 'wf'], ['ny', 'LO'], ['cu', 'DR'], ... xh', 'DR'], ['cu', 'xh'])\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -1858,7 +1958,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 48, "metadata": {}, "outputs": [ { @@ -1867,7 +1967,7 @@ "True" ] }, - "execution_count": 47, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1901,7 +2001,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 49, "metadata": {}, "outputs": [ { @@ -1910,7 +2010,7 @@ "True" ] }, - "execution_count": 48, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1937,38 +2037,75 @@ "source": [ "# [Day 13](https://adventofcode.com/2021/day/13): Transparent Origami\n", "\n", - "- **Input**: The input is in two sections: (1) a set of dots, e.g. \"`6,10`\". (2) a list of fold instructions, e.g. \"`fold along y=7`\".\n", + "- **Input**: The input is a set of dots, e.g. \"`6,10`\", followed by an ordered list of fold instructions, e.g. \"`fold along y=7`\".\n", "\n", - "My `parse` command is not set up to parse different sections differently, so I'll only ask `parse` to do a minimal amount of work, just parsing the input file into two sections, each a list of lines. Then I'll further process the two sections to get two variables:\n", - "- `dots`: a set of `(x, y)` points, such as `(103, 224)`. \n", - "- `folds`: a list of fold instructions, each implemented as a tuple, such as `('y', 7)` or `('x', 15)`." + "My `parse` command is not set up to parse two different sections, so I'll ask `parse` only to parse each line into a tuple of atoms. Then I'll further process the entries to get two variables:\n", + "- `dots`: a set of `(x, y)` points, such as `(6, 10)`. \n", + "- `folds`: a list of fold instructions such as `('fold', 'along', 'y', 7)`." ] }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 50, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 13 input (which is parsed into 2 entries):\n", - "--------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input13.txt ➜ 6424 chars, 789 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "103,224\n", "624,491\n", "808,688\n", "1076,130\n", "700,26\n", "55,794\n", - "119,724\n" + "119,724\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(13) ➜ 789 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "((103, 224), (624, 491), (808, 688), (1076, 130), (700, 26), (55, 794) ... 'fold', 'along', 'y', 6))\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], "source": [ - "in13 = parse(13, str.splitlines, sep='\\n\\n')\n", - "dots = {ints(line) for line in in13[0]}\n", - "folds = [(words(line)[2], ints(line)[0]) for line in in13[1]]" + "in13 = parse(13, atoms, sep='\\n')" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('fold', 'along', 'x', 655),\n", + " ('fold', 'along', 'y', 447),\n", + " ('fold', 'along', 'x', 327),\n", + " ('fold', 'along', 'y', 223),\n", + " ('fold', 'along', 'x', 163),\n", + " ('fold', 'along', 'y', 111),\n", + " ('fold', 'along', 'x', 81),\n", + " ('fold', 'along', 'y', 55),\n", + " ('fold', 'along', 'x', 40),\n", + " ('fold', 'along', 'y', 27),\n", + " ('fold', 'along', 'y', 13),\n", + " ('fold', 'along', 'y', 6)]" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dots = {entry for entry in in13 if len(entry) == 2} \n", + "folds = [entry for entry in in13 if len(entry) > 2]\n", + "folds" ] }, { @@ -1982,22 +2119,22 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 52, "metadata": {}, "outputs": [], "source": [ "def fold(dots, instruction) -> Set[Point]: \n", " \"\"\"The set of dots that result from following the fold instruction.\"\"\"\n", - " x_or_y, line = instruction\n", + " fold, along, x_or_y, line = instruction\n", " if x_or_y == 'x':\n", - " return {(line - abs(line - x), y) for (x, y) in dots}\n", + " return {(line - abs(line - x), y) for (x, y) in dots}\n", " else:\n", - " return {(x, line - abs(line - y)) for (x, y) in dots}" + " return {(x, line - abs(line - y)) for (x, y) in dots}" ] }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 53, "metadata": {}, "outputs": [ { @@ -2006,7 +2143,7 @@ "True" ] }, - "execution_count": 51, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -2024,7 +2161,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -2038,7 +2175,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 55, "metadata": {}, "outputs": [ { @@ -2047,7 +2184,7 @@ "True" ] }, - "execution_count": 53, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" }, @@ -2097,22 +2234,28 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 14 input (which is parsed into 102 entries):\n", - "----------------------------------------------------------------\n", + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input14.txt ➜ 822 chars, 102 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "ONSVVHNCFVBHKVPCHCPV\n", "\n", "VO -> C\n", "VV -> S\n", "HK -> H\n", "FC -> C\n", - "VB -> V\n" + "VB -> V\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(14) ➜ 102 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "(['ONSVVHNCFVBHKVPCHCPV'], [], ['VO', 'C'], ['VV', 'S'], ['HK', 'H'], ... ['FO', 'C'], ['VS', 'B'])\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -2133,7 +2276,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 57, "metadata": {}, "outputs": [ { @@ -2142,7 +2285,7 @@ "True" ] }, - "execution_count": 55, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -2181,7 +2324,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 58, "metadata": {}, "outputs": [ { @@ -2190,7 +2333,7 @@ "Counter({'NN': 1, 'NC': 1, 'CB': 1})" ] }, - "execution_count": 56, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -2210,7 +2353,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ @@ -2228,7 +2371,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -2243,16 +2386,16 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Counter({'B': 1, 'N': 2, 'C': 1})" + "Counter({'B': 1, 'C': 1, 'N': 2})" ] }, - "execution_count": 59, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -2270,7 +2413,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 62, "metadata": {}, "outputs": [], "source": [ @@ -2286,7 +2429,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 63, "metadata": {}, "outputs": [ { @@ -2295,7 +2438,7 @@ "True" ] }, - "execution_count": 61, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -2332,29 +2475,62 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# [Day 15](https://adventofcode.com/2021/day/15): Chiton\n", + "- **Part 3**: Polymer length?\n", "\n", - "- **Input**: The input is a square grid of *risk levels*, each one digit, 1–9." + "The instructions didn't ask, but I want to know the length of the polymer that was created after 40 steps. The calculation below says over 20 trillion." ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 64, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 7 lines of Day 15 input (which is parsed into 100 entries):\n", - "----------------------------------------------------------------\n", + "20,890,720,927,744\n" + ] + } + ], + "source": [ + "length = total(pair_insertion2(polymer, rules, 40))\n", + "print(f'{length:,d}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# [Day 15](https://adventofcode.com/2021/day/15): Chiton\n", + "\n", + "- **Input**: The input is a square grid of *risk levels* (each one digit, 1–9) for locations in the cave." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input15.txt ➜ 10100 chars, 100 lines; first 7 lines:\n", + "----------------------------------------------------------------------------------------------------\n", "4249856395422795894919869133487611581179923326874763428673979547991221931142777981153991369468629849\n", "5812974178739823463799939791688998895568796557798392761499941349143539572865883254186633218867928826\n", "3699989976298596286299499129934993241824395574879938998946914116375199242199151918863674914554714898\n", "5682435936794718871685718386458294198391116125679589438794914499278679393779734596558953699438589518\n", "7681197997388219696918569664119968498599547892968929425479817979816979144947916716989874825679487436\n", "9981166198272997899142698141878643123757515999788822988261499197559193945291512682763935126815448215\n", - "8849481991861599951293183728419792414164347979985169641698899853377259811688489269959429131918919179\n" + "8849481991861599951293183728419792414164347979985169641698899853377259811688489269959429131918919179\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(15) ➜ 100 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "((4, 2, 4, 9, 8, 5, 6, 3, 9, 5, 4, 2, 2, 7, 9, 5, 8, 9, 4, 9, 1, 9, 8, ... 7, 8, 9, 3, 9, 2, 3, 9))\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -2368,12 +2544,16 @@ "source": [ "- **Part 1**: You start in the top left position, your destination is the bottom right position, and you cannot move diagonally. What is the lowest total risk of any path from the top left to the bottom right? (Don't count the risk level of your starting position.)\n", "\n", - "I'll use a search that updates a grid of the `cost` of the best known path from start to each point. The cost for each point is initially infinite (because we don't know any paths), and is updated each time we find a better path to the point." + "Gary Grady's drawing represents the risk involved in finding a path that avoids bumping into the ceiling above or the chitons below.\n", + "\n", + "\n", + "\n", + "I'll use a search that updates a grid of the `cost` of the best known path from start to each point. The cost for each point is initially infinite (because we don't know any paths), and is updated each time we find a better path to the point. Whenever we find a better path to a point, we see if that will lead to a better path for the neighbors." ] }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 66, "metadata": {}, "outputs": [ { @@ -2382,7 +2562,7 @@ "True" ] }, - "execution_count": 63, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -2390,16 +2570,16 @@ "source": [ "def search_grid(grid, start=(0, 0), goal=None) -> int:\n", " \"\"\"The total cost of the best path from start to goal (which defaults to bottom right).\"\"\"\n", - " goal = goal or max(grid)\n", - " cost = Grid({p: inf for p in grid}) # cost[p] is cost of best known path from start to p\n", - " frontier = {start}\n", + " goal = goal or max(grid) # default bottom right\n", + " path_cost = Grid({p: inf for p in grid}) # cost of best known path from start to p\n", + " frontier = {start} # Set of grid points to consider for possible improvement to path_cost\n", " while frontier:\n", " p = frontier.pop()\n", - " new_cost = 0 if p is start else (grid[p] + min(cost[b] for b in grid.neighbors(p)))\n", - " if new_cost < cost[p]:\n", - " cost[p] = new_cost\n", + " new_cost = 0 if p is start else (grid[p] + min(path_cost[b] for b in grid.neighbors(p)))\n", + " if new_cost < path_cost[p]:\n", + " path_cost[p] = new_cost\n", " frontier.update(grid.neighbors(p))\n", - " return cost[goal]\n", + " return path_cost[goal]\n", "\n", "answer(15.1, search_grid(in15), 687)" ] @@ -2415,7 +2595,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 67, "metadata": {}, "outputs": [], "source": [ @@ -2435,24 +2615,45 @@ " return (i % m) or m" ] }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "250000" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "full_map = repeat_grid(in15, 5)\n", + "len(full_map)" + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ - "With 250,000 points in the full map, I estimated it would take several minutes to run the `search_grid` algorithm. I tested it, and it was 5 minutes. I wasn't satisfied with that, so I copied over the [A* search](https://en.wikipedia.org/wiki/A*_search_algorithm) from my [AoC 2017](https://github.com/norvig/pytudes/blob/main/ipynb/Advent%202017.ipynb) notebook, and supplied ity with the proper functions to make a move, compute the cost of a move, and estimate the distance to the goal (the `h_func` or \"heuristic function\"). A* is guaranteed to find an optimal path if the heuristic function never overestimates the cost from a state to the goal, so I use as an estimate the cost if every grid point along the way had a risk level of 1." + "With 250,000 points in the full map, `search_grid` takes about 5 minutes (I tried it). That's too slow, so I grabbed the [A* search](https://en.wikipedia.org/wiki/A*_search_algorithm) from my [AoC 2017](https://github.com/norvig/pytudes/blob/main/ipynb/Advent%202017.ipynb) notebook, and supplied it with the proper functions to make a move, compute the cost of a move, and estimate the distance to the goal (the `h_func` or \"heuristic function\"). A* is guaranteed to find an optimal path if the heuristic function never overestimates the cost from a state to the goal, so I will use the [Manhattan distance](https://en.wikipedia.org/wiki/Taxicab_geometry) as my heuristic–this is the same as assuming that every risk level in the remainder of the path will be 1, the lowest possible." ] }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 69, "metadata": {}, "outputs": [], "source": [ "from heapq import heappop, heappush\n", "\n", - "def Astar(start, moves_func, h_func, cost_func) -> Tuple[int, list]:\n", - " \"\"\"Find a (cost, path) tuple for the lowest-cost path from start to goal.\n", - " A goal is any state where h_func(s) == 0.\"\"\"\n", + "def Astar(start, neighbors, h_func, step_cost) -> Tuple[int, list]:\n", + " \"\"\"Find a (cost, path) tuple for the lowest-cost path from start to a goal.\n", + " A goal is any state `s` such that `h_func(s) == 0`.\"\"\"\n", " frontier = [(h_func(start), start)] # A priority queue, ordered by path_cost(s) + h(s)\n", " previous = {start: None} # start state has no previous state; other states will\n", " path_cost = {start: 0} # The cost of the best path to a state.\n", @@ -2461,20 +2662,20 @@ " (f, s) = heappop(frontier)\n", " if h_func(s) == 0:\n", " return path_cost[s], Path(s)\n", - " for s2 in moves_func(s):\n", - " g = path_cost[s] + cost_func(s, s2)\n", + " for s2 in neighbors(s):\n", + " g = path_cost[s] + step_cost(s, s2)\n", " if s2 not in path_cost or g < path_cost[s2]:\n", " heappush(frontier, (g + h_func(s2), s2))\n", " path_cost[s2] = g\n", " previous[s2] = s\n", " \n", - "def Astar_search_grid(grid):\n", - " \"\"\"The total cost of the best path from (0, 0) to bottom-right on grid.\"\"\"\n", - " start, goal = (0, 0), max(grid)\n", - " def moves_func(s): return grid.neighbors(s)\n", - " def h_func(s): return sum(goal) - sum(s)\n", - " def cost_func(s, s2): return grid[s2]\n", - " return Astar(start, moves_func, h_func, cost_func)[0]" + "def Astar_search_grid(grid, start=(0, 0)) -> Tuple[int, list]:\n", + " \"\"\"The (risk, path) tuple of the best path from start to bottom-right on grid.\"\"\"\n", + " goal = max(grid)\n", + " def neighbors(s): return grid.neighbors(s) # possible moves\n", + " def h_func(s): return sum(goal) - sum(s) # estimated path cost from s to goal\n", + " def step_cost(_, s2): return grid[s2] # cost of moving to s2\n", + " return Astar(start, neighbors, h_func, step_cost)" ] }, { @@ -2486,7 +2687,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 70, "metadata": {}, "outputs": [ { @@ -2495,22 +2696,70 @@ "True" ] }, - "execution_count": 66, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "answer(15.2, Astar_search_grid(repeat_grid(in15, 5)), 2957)" + "answer(15.2, Astar_search_grid(full_map)[0], 2957)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Gary Grady's drawing represents the risk involved in finding a path that avoids bumping into the ceiling above or the chitons below.\n", - "\n", - "" + "- **Part 3**: Visualization" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "def plot_search_grid(grid, fmt='g-'):\n", + " \"\"\"PLot the path from start to goal.\"\"\"\n", + " risk, path = Astar_search_grid(grid)\n", + " plt.plot(*transpose(path), fmt); plt.gca().invert_yaxis()\n", + " plt.title(f'Path with risk level {risk}')\n", + " \n", + "plot_search_grid(in15)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plot_search_grid(full_map)" ] }, { @@ -2519,21 +2768,31 @@ "source": [ "# [Day 16](https://adventofcode.com/2021/day/16): Packet Decoder\n", "\n", - "- **Input**: The input is a single line containing a sequence of hexadecimal digits, a message using the Buoyancy Interchange Transmission System (BITS). For now I will leave the input as a string of hex digits." + "- **Input**: The input is a single line containing a sequence of hexadecimal digits, a message using the Buoyancy Interchange Transmission System (BITS). \n", + "\n", + "\n", + "\n", + "For now I will leave the input as a string of hex digits:" ] }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 73, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "First 1 lines of Day 16 input (which is parsed into 1 entries):\n", - "--------------------------------------------------------------\n", - "220D790065B2745FF004672D99A34E5B33439D96CEC80373C0068663101A98C406A5E7395DC1804678BF25A4093BFBDB886C ...\n" + "----------------------------------------------------------------------------------------------------\n", + "AOC2021/input16.txt ➜ 1307 chars, 1 lines; first 1 lines:\n", + "----------------------------------------------------------------------------------------------------\n", + "220D790065B2745FF004672D99A34E5B33439D96CEC80373C0068663101A98C406A5E7 ... 97652008065992443E7872714\n", + "----------------------------------------------------------------------------------------------------\n", + "parse(16) ➜ 1 entries:\n", + "----------------------------------------------------------------------------------------------------\n", + "('220D790065B2745FF004672D99A34E5B33439D96CEC80373C0068663101A98C406A5 ... 52008065992443E7872714',)\n", + "----------------------------------------------------------------------------------------------------\n" ] } ], @@ -2547,12 +2806,12 @@ "source": [ "- **Part 1:** The puzzle is to parse this hexadecimal transmission into data packets, according to the rules contained in [the instructions](https://adventofcode.com/2021/day/16), and add up all of the version numbers of the packets.\n", "\n", - "The gist of [the instructions](https://adventofcode.com/2021/day/16) is to consider the hexadecimal sequence as a bit string, divide the bit string into bit fields, and construct nested packets based on the values of the fields. Here are basic types for `Bits` (a bit string) and `Packet` (which contains a version umber `V`, a type ID `T`, and a `contents` field which can be either a number or a list of packets), along with functions to convert from a hexadecimal string to a bit string, and from there to an int: " + "The gist of [the instructions](https://adventofcode.com/2021/day/16) is to consider the hexadecimal sequence as a bit string, divide the bit string into bit fields, and construct nested packets based on the values of the fields. Here are basic types for `Bits` (a bit string) and `Packet` (which contains a version number `V`, a type ID `T`, and a `contents` field which can be either a number or a list of packets), along with functions to convert from a hexadecimal string to a bit string, and from there to an int: " ] }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 74, "metadata": {}, "outputs": [], "source": [ @@ -2569,36 +2828,16 @@ " return int(bits, 2)" ] }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'0b11111111'" - ] - }, - "execution_count": 69, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bin(int('ff', 16))" - ] - }, { "cell_type": "markdown", "metadata": {}, "source": [ - "To parse the bit string into packets, I will have various functions that start with the word `parse_` and return a tuple of two values: the object parsed (either an int or a packet) and the remaining bits that were not parsed." + "To parse the bit string into packets, I will have four functions that start with the word `parse_` and return a tuple of two values: the object parsed (either an int or a packet) and the remaining bits that were not parsed." ] }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 75, "metadata": {}, "outputs": [], "source": [ @@ -2616,24 +2855,23 @@ " \"\"\"Build a packet with a literal value; return it and the remaining bits.\"\"\"\n", " literal = ''\n", " while True:\n", - " bit, nibble, bits = bits[0], bits[1:5], bits[5:]\n", - " literal += nibble\n", - " if bit == '0':\n", + " prefix, group, bits = bits[0], bits[1:5], bits[5:]\n", + " literal += group\n", + " if prefix == '0':\n", " return Packet(V, T, int2(literal)), bits\n", - " \n", + "\n", "def parse_operator_packet(V, T, bits) -> Tuple[Packet, Bits]:\n", " \"\"\"Build a packet with subpackets; return it and the remaining bits.\"\"\"\n", " I, bits = parse_int(1, bits)\n", + " L, bits = parse_int((15, 11)[I], bits)\n", " subpackets = [] \n", - " if I == 0:\n", - " L, bits = parse_int(15, bits)\n", - " target = len(bits) - L\n", - " while len(bits) > target:\n", - " packet, bits = parse_packet(bits)\n", + " if I == 0: # Parse L bits of subpackets\n", + " subpacket_bits, bits = bits[:L], bits[L:]\n", + " while subpacket_bits:\n", + " packet, subpacket_bits = parse_packet(subpacket_bits)\n", " subpackets.append(packet)\n", - " else:\n", - " L, bits = parse_int(11, bits)\n", - " for _ in range(L):\n", + " else: # Parse L subpackets\n", + " for p in range(L):\n", " packet, bits = parse_packet(bits)\n", " subpackets.append(packet) \n", " return Packet(V, T, subpackets), bits" @@ -2643,12 +2881,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now we're ready to solve the puzzle by summing up the version numbers of the parsed packet and its subpackets:" + "Now we're ready to solve the puzzle by summing up the version numbers, `V`, of all the packets:" ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 76, "metadata": {}, "outputs": [ { @@ -2657,22 +2895,21 @@ "True" ] }, - "execution_count": 71, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def subpackets(packet) -> List[Packet]: \n", - " \"\"\"The subpackets of a packet.\"\"\"\n", - " return packet.contents if isinstance(packet.contents, list) else []\n", + "def nested_packets(packet) -> Iterator[Packet]: \n", + " \"\"\"The packet and all its subpackets.\"\"\"\n", + " yield packet\n", + " if packet.T != 4: \n", + " for p in packet.contents:\n", + " yield from nested_packets(p)\n", "\n", - "def sum_versions(packet) -> int:\n", - " \"\"\"The sum of the version numbers of this packet and all its subpackets (recursively).\"\"\"\n", - " return packet.V + sum(sum_versions(p) for p in subpackets(packet))\n", - " \n", "packet16, _ = parse_packet(bits_from_hex(in16))\n", - "answer(16.1, sum_versions(packet16), 989)" + "answer(16.1, sum(p.V for p in nested_packets(packet16)), 989)" ] }, { @@ -2684,17 +2921,24 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 77, "metadata": {}, "outputs": [], "source": [ "assert (bits_from_hex('D2FE28') \n", " == '110100101111111000101000')\n", "\n", + "assert (int2(bits_from_hex('D2FE28'))\n", + " == 13827624)\n", + "\n", "assert (bits_from_hex('38006F45291200') \n", " == '00111000000000000110111101000101001010010001001000000000')\n", "\n", + "assert (parse_int(4, '011100111') \n", + " == (7, '00111'))\n", + "\n", "assert (parse_packet('110100101111111000101000') \n", + " == parse_literal_packet(6, 4, '101111111000101000')\n", " == (Packet(V=6, T=4, contents=2021), '000'))\n", "\n", "assert (parse_packet('00111000000000000110111101000101001010010001001000000000')\n", @@ -2720,7 +2964,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 78, "metadata": {}, "outputs": [ { @@ -2729,7 +2973,7 @@ "True" ] }, - "execution_count": 73, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -2740,7 +2984,7 @@ " if packet.T == 4:\n", " return packet.contents\n", " else:\n", - " vals = [eval_packet(p) for p in subpackets(packet)]\n", + " vals = [eval_packet(p) for p in packet.contents]\n", " return packet_ops[packet.T](vals)\n", " \n", "packet_ops = {0: sum, 1: prod, 2: min, 3: max, \n", @@ -2750,6 +2994,491 @@ "\n", "answer(16.2, eval_packet(packet16), 7936430475134)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# [Day 17](https://adventofcode.com/2021/day/17): Trick Shot\n", + "\n", + "- **Input**: The input is a short string describing the x and y coordinates of a target area.\n", + "\n", + "Because the input is so short, I will copy it literally here instead of reading it from a file. I use `ints` to extract the four integers." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [], + "source": [ + "in17 = ints(\"target area: x=257..286, y=-101..-57\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The puzzle involves firing a probe and checking if it hits the target area. The probe starts from an initial position with an initial velocity, and traverses a path according to the physics described in the instructions.\n", + "\n", + "- **Part 1**: Find the initial velocity that causes the probe to reach the highest `y` position and still eventually be within the target area after some time step. What is the highest `y` position it reaches on this trajectory?\n", + "\n", + "First I'll define two classes:\n", + "- `Target` keeps track of the `Xs` and `Ys` that define the target area.\n", + "- `Probe` keeps track of:\n", + " - The `x` and `y` position coordinates\n", + " - The `vx` and `vy` velocity values\n", + " - A boolean `hit` which is True if the probe hit the target at some point in its path\n", + " - The `highest` height it ever reached." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [], + "source": [ + "class Target:\n", + " \"\"\"The target has a range of Xs and Ys coordinates.\"\"\"\n", + " def __init__(self, a, b, c, d): self.Xs, self.Ys = range(a, b + 1),range(c, d + 1) \n", + " \n", + "Probe = namedtuple('Probe', 'x, y, vx, vy, hit, highest', \n", + " defaults=(0, 0, 0, 0, False, 0))\n", + "\n", + "target17 = Target(*in17)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The function `probe_step` simulates the physics of the world for one time step: incrementing the probe's position by its velocity vector, changing the `xv` velocity due to drag and the `yv` velocity due to gravity, and tracking the `hit` and `highest` values.\n", + "\n", + "The function `probe_steps` simulates for multiple time steps; until the probe has passed the target. " + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [], + "source": [ + "def probe_step(probe, target) -> Probe:\n", + " \"\"\"Simulate the physics of the probe for one time step.\"\"\"\n", + " x, y, vx, vy, hit, highest = probe\n", + " return Probe(x=x + vx, y=y + vy, \n", + " vx=sign(vx) * (abs(vx) - 1), vy=vy - 1,\n", + " hit=hit or (x in target.Xs and y in target.Ys),\n", + " highest=max(highest, y + vy))\n", + "\n", + "def probe_steps(probe, target=target17, do=nothing) -> Probe:\n", + " \"\"\"Simulate the probe until it passes the target.\n", + " You can optionally `do` something to the probe on each time step.\"\"\"\n", + " maxx, miny = max(target.Xs), min(target.Ys)\n", + " do(probe)\n", + " while probe.x <= maxx and probe.y >= miny:\n", + " probe = probe_step(probe, target)\n", + " do(probe)\n", + " return probe" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Probe(x=290, y=-90, vx=4, vy=-15, hit=True, highest=15)" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "probe_steps(Probe(vx=24, vy=5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By experimentation, I found that:\n", + "- Any `vx<23` will never reach the target (regardless of `vy`).\n", + "- A `vx=23` value means that the probe will have an `x` velocity of zero when it is inside the width of the target. \n", + "- Any `vx>23` will eventually pass beyond the target width (and might or might not hit the target along the way). \n", + "- This is because 23 is the only value that leads to a sequence of decreasing `vx` values adding up to an `x` position that is within the x=257..286 target area: 23 + 22 + 21 + ... + 2 + 1 = 276. (As with Day 7, we're dealing with triangular numbers.) Here's the demonstration:" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{22: [253, False], 23: [276, True], 24: [300, False]}" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{vx: [sum(range(vx, 0, -1)), sum(range(vx, 0, -1)) in target17.Xs]\n", + " for vx in [22, 23, 24]}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Specifying `do=print` is useful for experimentation:" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Probe(x=0, y=0, vx=23, vy=7, hit=False, highest=0)\n", + "Probe(x=23, y=7, vx=22, vy=6, hit=False, highest=7)\n", + "Probe(x=45, y=13, vx=21, vy=5, hit=False, highest=13)\n", + "Probe(x=66, y=18, vx=20, vy=4, hit=False, highest=18)\n", + "Probe(x=86, y=22, vx=19, vy=3, hit=False, highest=22)\n", + "Probe(x=105, y=25, vx=18, vy=2, hit=False, highest=25)\n", + "Probe(x=123, y=27, vx=17, vy=1, hit=False, highest=27)\n", + "Probe(x=140, y=28, vx=16, vy=0, hit=False, highest=28)\n", + "Probe(x=156, y=28, vx=15, vy=-1, hit=False, highest=28)\n", + "Probe(x=171, y=27, vx=14, vy=-2, hit=False, highest=28)\n", + "Probe(x=185, y=25, vx=13, vy=-3, hit=False, highest=28)\n", + "Probe(x=198, y=22, vx=12, vy=-4, hit=False, highest=28)\n", + "Probe(x=210, y=18, vx=11, vy=-5, hit=False, highest=28)\n", + "Probe(x=221, y=13, vx=10, vy=-6, hit=False, highest=28)\n", + "Probe(x=231, y=7, vx=9, vy=-7, hit=False, highest=28)\n", + "Probe(x=240, y=0, vx=8, vy=-8, hit=False, highest=28)\n", + "Probe(x=248, y=-8, vx=7, vy=-9, hit=False, highest=28)\n", + "Probe(x=255, y=-17, vx=6, vy=-10, hit=False, highest=28)\n", + "Probe(x=261, y=-27, vx=5, vy=-11, hit=False, highest=28)\n", + "Probe(x=266, y=-38, vx=4, vy=-12, hit=False, highest=28)\n", + "Probe(x=270, y=-50, vx=3, vy=-13, hit=False, highest=28)\n", + "Probe(x=273, y=-63, vx=2, vy=-14, hit=False, highest=28)\n", + "Probe(x=275, y=-77, vx=1, vy=-15, hit=True, highest=28)\n", + "Probe(x=276, y=-92, vx=0, vy=-16, hit=True, highest=28)\n", + "Probe(x=276, y=-108, vx=0, vy=-17, hit=True, highest=28)\n" + ] + }, + { + "data": { + "text/plain": [ + "Probe(x=276, y=-108, vx=0, vy=-17, hit=True, highest=28)" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "probe_steps(Probe(vx=23, vy=7), do=print)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once I found the critical `vx=23` value, I figured I could simply vary the `vy` values to find the highest height:" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def highest_height(vxs=[23], vys=[]) -> int:\n", + " \"\"\"The highest height reached by a probe that hits the target, among all vx and vy values.\"\"\"\n", + " probes = [probe_steps(Probe(vx=vx, vy=vy)) for vx in vxs for vy in vys]\n", + " return max(probe.highest for probe in probes if probe.hit)\n", + " \n", + "answer(17.1, highest_height(vys=range(150)), 5050)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- **Part 2**: How many distinct initial velocity values cause the probe to be within the target area after some time step?\n", + " \n", + "I can try a bunch of `vx` and `vy` values. For `vx`, start at the critical 23 value and go up to the maximum of the target area (meaning that the probe hits the right edge of the target area on the first time step). For `vy`, start with a negative value that would hit the bottom of the target area on the first time step, and go up to 100. (Anything more than that passes through the target without touching it: the probe's `y` value is higher than the target on one step and lower on the next.)" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def probe_hits(vxs=range(23, max(target17.Xs) + 1), vys=range(min(target17.Ys), 101)) -> int:\n", + " \"\"\"How many of these velocities cause the probe to hit the target?\"\"\"\n", + " return quantify(probe_steps(Probe(vx=vx, vy=vy)).hit \n", + " for vx in vxs for vy in vys)\n", + "\n", + "answer(17.2, probe_hits(), 2223)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- **Part 3**: Visualization\n", + "\n", + "I'd like to understand things a bit better with some visualization. The function `plot_probes` plots the target as a black box and plots the paths of various probes with different initial velocities in different colors:" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_probes(velocities: List[Point], target=target17) -> None:\n", + " \"\"\"Plot the target as a black box and the paths of probes with colored lines.\"\"\"\n", + " x1, x2 = min(target.Xs), max(target.Xs)\n", + " y1, y2 = min(target.Ys), max(target.Ys)\n", + " plt.plot([x1, x2, x2, x1, x1], [y1, y1, y2, y2, y1], 'k-', linewidth=4)\n", + " for (vx, vy) in velocities:\n", + " path = []\n", + " probe_steps(Probe(vx=vx, vy=vy), do=path.append)\n", + " plt.plot([p.x for p in path], [p.y for p in path], '.:', label=f'({vx}, {vy})')\n", + " plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below are four paths: two that hit the target, and two that miss:" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + " plot_probes([(24, 5), (32, 0), (34, -5), (36, -10)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below we see that `vx=22` is doomed to stall before it reaches the target area; `vx=23` is the critical value that stalls and falls into the target area; and `vx=24` shoots beyond the target area before stalling." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + " plot_probes([(22, 12), (23, 10), (24, 8)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below is `vx=23` paired with three different `vy` velocities:" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + " plot_probes([(23, 16), (23, 11), (23, 7)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When `vx < vy` the probe's path begins by bending up (e.g. for `(23, 32)`); when `vx = vy` the path is a straight line (with ever-slowing speed); and when `vx > vy` the path bends down (e.g. for `(23, 8)`). But all paths eventually stall and fall due to drag." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + " plot_probes([(23, 32), (23, 23), (23, 8)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below, initial velocity `(23, 100)`, yields the high point of 5050 before stalling and falling into the target:" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + " plot_probes([(23, 100)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note the y-axis on this plot is ten times more than the previous plot. That's why the target box looks so squished and the path slope looks shallow. If we equalize the two axes, we can see how steep the slope is (and why this day is titled \"Trick Shot\"):" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + " plot_probes([(23, 100)]); plt.axis('square');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I can now see a more efficient way to deal with this problem:\n", + " - The movement in `x` and `y` are independent, so we can treat them separately:\n", + " - For each `vx`, determine in which time step(s) a probe could intersect `target.Xs`.\n", + " - For each `vy`, determine in which time step(s) a probe could intersect `target.Ys`.\n", + " - Now if a time step has 10 `vx` and 12 `vy` intersects, then that's 120 hits. \n", + " - The number of simulations we have to do is the sum of the lengths of `vxs` and `vys`, not their product.\n", + " - This technique could have cut the number of simulations from about 50,000 to about 500. \n", + " - But 50,000 is a small number and the code runs in under a second, so we don't need a re-implementation" + ] } ], "metadata": {