\n",
"\n",
"# Advent of Code 2021\n",
"\n",
"I'm doing [Advent of Code](https://adventofcode.com/2021) (AoC) this year. I'm not competing for points, just participating for fun.\n",
"\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 requested in Part 2 (although I may edit the code for clarity, without changing the initial approach).\n",
"\n",
"# Day 0: Preparations\n",
"\n",
"Imports that I have used in past AoC years:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from __future__ import annotations\n",
"from collections import Counter, defaultdict, namedtuple, deque\n",
"from itertools import permutations, combinations, chain, count as count_from, product as cross_product\n",
"from typing import *\n",
"from statistics import mean, median\n",
"from math import ceil, inf\n",
"from functools import lru_cache\n",
"import matplotlib.pyplot as plt\n",
"import re"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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 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**: 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`:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def answer(puzzle_number, got, expected) -> bool:\n",
" \"\"\"Verify the answer we got was the expected answer.\"\"\"\n",
" assert got == expected, f'For {puzzle_number}, expected {expected} but got {got}.'\n",
" return True\n",
"\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",
" fname = f'AOC2021/input{day}.txt'\n",
" text = open(fname).read()\n",
" entries = mapt(parser, text.rstrip().split(sep))\n",
" if print_lines:\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(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(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(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(r'[a-zA-Z]+', text)\n",
"\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",
" try:\n",
" x = float(text)\n",
" return round(x) if round(x) == x else x\n",
" except ValueError:\n",
" return text\n",
" \n",
"def mapt(fn, *args) -> tuple:\n",
" \"\"\"map(fn, *args) and return the result as a tuple.\"\"\"\n",
" return tuple(map(fn, *args))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A few additional utility functions that I have used in the past:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def quantify(iterable, pred=bool) -> int:\n",
" \"\"\"Count the number of items in iterable for which pred is true.\"\"\"\n",
" return sum(1 for item in iterable if pred(item))\n",
"\n",
"class multimap(defaultdict):\n",
" \"\"\"A mapping of {key: [val1, val2, ...]}.\"\"\"\n",
" def __init__(self, pairs: Iterable[tuple], symmetric=False):\n",
" \"\"\"Given (key, val) pairs, return {key: [val, ...], ...}.\n",
" If `symmetric` is True, treat (key, val) as (key, val) plus (val, key).\"\"\"\n",
" self.default_factory = list\n",
" for (key, val) in pairs:\n",
" self[key].append(val)\n",
" if symmetric:\n",
" 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",
" result = 1\n",
" for x in numbers:\n",
" result *= x\n",
" return result\n",
"\n",
"def total(counter: Counter) -> int: \n",
" \"\"\"The sum of all the counts in a Counter.\"\"\"\n",
" 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 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)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some past puzzles involve (x, y) points on a rectangular grid, so I'll define `Point` and `Grid`:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"Point = Tuple[int, int] # (x, y) points on a grid\n",
"\n",
"neighbors4 = ((0, 1), (1, 0), (0, -1), (-1, 0)) \n",
"neighbors8 = ((1, 1), (1, -1), (-1, 1), (-1, -1)) + neighbors4\n",
"\n",
"class Grid(dict):\n",
" \"\"\"A 2D grid, implemented as a mapping of {(x, y): cell_contents}.\"\"\"\n",
" def __init__(self, mapping=(), rows=(), neighbors=neighbors4):\n",
" \"\"\"Initialize with, e.g., either `mapping={(0, 0): 1, (1, 0): 2, ...}`,\n",
" or `rows=[(1, 2, 3), (4, 5, 6)].\n",
" `neighbors` is a collection of (dx, dy) deltas to neighboring points.`\"\"\"\n",
" self.update(mapping if mapping else\n",
" {(x, y): val \n",
" for y, row in enumerate(rows) \n",
" for x, val in enumerate(row)})\n",
" self.width = max(x for x, y in self) + 1\n",
" self.height = max(y for x, y in self) + 1\n",
" self.deltas = neighbors\n",
" \n",
" def copy(self) -> Grid: return Grid(self, neighbors=self.deltas)\n",
" \n",
" def neighbors(self, point) -> List[Point]:\n",
" \"\"\"Points on the grid that neighbor `point`.\"\"\"\n",
" x, y = point\n",
" return [(x+dx, y+dy) for (dx, dy) in self.deltas \n",
" if (x+dx, y+dy) in self]\n",
" \n",
" def to_rows(self) -> List[List[object]]:\n",
" \"\"\"The contents of the grid in a rectangular list of lists.\"\"\"\n",
" return [[self[x, y] for x in range(self.width)]\n",
" for y in range(self.height)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This year's AoC theme involves Santa's Elves on a submarine. [Gary J Grady](https://twitter.com/GaryJGrady/) has some nice drawings to set the scene:\n",
"\n",
"\n",
"\n",
"# [Day 1](https://adventofcode.com/2021/day/1): Sonar Sweep\n",
"\n",
"\n",
"- **Input**: Each entry in the input is an integer depth measurement.\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"in1 = parse(1, int)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: How many measurements are larger than the previous measurement?"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def increases(nums: List[int]) -> int:\n",
" \"\"\"How many measurements are larger than the previous measurement?\"\"\"\n",
" return quantify(nums[i] > nums[i - 1] \n",
" for i in range(1, len(nums)))\n",
"\n",
"answer(1.1, increases(in1), 1400)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Consider sums of a three-measurement sliding window. How many sums are larger than the previous sum?"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def window_increases(nums: List[int], w=3) -> int:\n",
" \"\"\"How many sliding windows of `w` numbers have a sum larger than the previous window?\"\"\"\n",
" return quantify(sum(nums[i:i+w]) > sum(nums[i-1:i-1+w])\n",
" for i in range(1, len(nums) + 1 - w))\n",
"\n",
"answer(1.2, window_increases(in1), 1429)"
]
},
{
"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": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.plot(in1, 'b.'); plt.ylabel('Depth'); plt.gca().invert_yaxis();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It looks like Gary Grady was right; the submarine is descending at a steep angle."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 2](https://adventofcode.com/2021/day/2): Dive! \n",
"\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', 2)`."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\n",
"parse(2) ➜ 1000 entries:\n",
"----------------------------------------------------------------------------------------------------\n",
"(('forward', 2), ('down', 7), ('down', 8), ('forward', 9), ('down', 8) ... ard', 3), ('forward', 6))\n",
"----------------------------------------------------------------------------------------------------\n"
]
}
],
"source": [
"in2 = parse(2, atoms)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: Calculate the horizontal position and depth you would have after following the planned course. What do you get if you multiply your final horizontal position by your final depth?"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def drive(commands) -> int:\n",
" \"\"\"What is the product of position and depth after following commands?\"\"\"\n",
" pos = depth = 0\n",
" for (op, n) in commands:\n",
" if op == 'forward': pos += n\n",
" if op == 'down': depth += n\n",
" if op == 'up': depth -= n\n",
" return pos * depth\n",
"\n",
"answer(2.1, drive(in2), 1_670_340)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Using the new interpretation of the commands, calculate the horizontal position and depth you would have after following the planned course. What do you get if you multiply your final horizontal position by your final depth? \n",
"\n",
"The *new interpretation* is that the \"down\" and \"up\" commands no longer change depth, rather they change *aim*, and going forward *n* units both increments position by *n* and depth by *aim* × *n*."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def drive2(commands) -> int:\n",
" \"\"\"What is the product of position and depth after following commands?\n",
" This time we have to keep track of `aim` as well.\"\"\"\n",
" pos = depth = aim = 0\n",
" for (op, n) in commands:\n",
" if op == 'forward': pos += n; depth += aim * n\n",
" if op == 'down': aim += n\n",
" if op == 'up': aim -= n\n",
" return pos * depth\n",
"\n",
"answer(2.2, drive2(in2), 1_954_293_920)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 3](https://adventofcode.com/2021/day/3): Binary Diagnostic\n",
"\n",
"- **Input**: Each entry in the input is a bit string of `0`s and `1`s.\n",
"\n",
"I'll parse them as strings; I won't convert them into ints."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\n",
"parse(3) ➜ 1000 entries:\n",
"----------------------------------------------------------------------------------------------------\n",
"('101000111100', '000011111101', '011100000100', '100100010000', '0111 ... 1100111', '110111100100')\n",
"----------------------------------------------------------------------------------------------------\n"
]
}
],
"source": [
"in3 = parse(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: Use the binary numbers in your diagnostic report to calculate the gamma rate and epsilon rate, then multiply them together. What is the power consumption of the submarine?"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def common(strs, i) -> Char: # '1' or '0'\n",
" \"\"\"The bit that is most common in position i among strs.\"\"\"\n",
" bits = [s[i] for s in strs]\n",
" return '1' if bits.count('1') >= bits.count('0') else '0'\n",
"\n",
"def uncommon(strs, i) -> Char: # '1' or '0'\n",
" \"\"\"The bit that is least common in position i among strs.\"\"\"\n",
" return '1' if common(strs, i) == '0' else '0'\n",
"\n",
"def epsilon(strs) -> str:\n",
" \"\"\"The bit string formed from most common bit at each position.\"\"\"\n",
" return cat(common(strs, i) for i in range(len(strs[0])))\n",
"\n",
"def gamma(strs) -> str:\n",
" \"\"\"The bit string formed from most uncommon bit at each position.\"\"\"\n",
" return cat(uncommon(strs, i) for i in range(len(strs[0])))\n",
"\n",
"def power(strs) -> int: \n",
" \"\"\"Product of epsilon and gamma rates.\"\"\"\n",
" return int(epsilon(strs), 2) * int(gamma(strs), 2)\n",
" \n",
"answer(3.1, power(in3), 2261546)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Use the binary numbers in your diagnostic report to calculate the oxygen generator rating and CO2 scrubber rating, then multiply them together. What is the life support rating of the submarine?\n",
"\n",
"This time I'll have a single function, `select_str` which selects the str that survives the process of picking strs with the most common or uncommon bit at each position. Then I call `select_str` with `common` to get the oxygen rating and `uncommon` to get the CO2 rating."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def select_str(strs, common_fn, i=0) -> str:\n",
" \"\"\"Select a str from strs according to common_fn:\n",
" Going left-to-right, repeatedly select just the strs that have the right i-th bit.\n",
" When only one string is remains, return it.\"\"\"\n",
" if len(strs) == 1:\n",
" return strs[0]\n",
" else:\n",
" bit = common_fn(strs, i)\n",
" selected = [s for s in strs if s[i] == bit]\n",
" return select_str(selected, common_fn, i + 1)\n",
"\n",
"def life_support(strs) -> int: \n",
" \"\"\"The product of oxygen (most common select) and CO2 (least common select) rates.\"\"\"\n",
" return int(select_str(strs, common), 2) * int(select_str(strs, uncommon), 2)\n",
" \n",
"answer(3.2, life_support(in3), 6775520)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 4](https://adventofcode.com/2021/day/4): Giant Squid\n",
"\n",
"- **Input**: The first entry of the input is a permutation of the integers 0-99. Subsequent entries are bingo boards: 5 lines of 5 ints each. Entries are separated by *two* newlines. \n",
"\n",
"I'll represent a board as a tuple of 25 ints; that makes `parse` easy: the permutation of integers and the bingo boards can both be parsed by `ints`. (Bingo games will be played against a giant squid; we get to choose which board we want to play.)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"order, *boards = in4 = parse(4, ints, sep='\\n\\n')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: What will your final score be if you choose the first bingo board to win?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I'm worried about an ambiguity: what if two boards win at the same time? I'll have to assume Eric arranged it so that can't happen. I'll define `bingo_winners` to return a list of boards that win when a number has just been called, and I'll arbitrarily choose the first of them."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"B = 5 # Bingo board is size B by B.\n",
"Board = Tuple[int] # B * B ints\n",
"Line = List[int] # B ints\n",
"\n",
"def lines(square) -> Tuple[Line, Line]:\n",
" \"\"\"The two lines (horizontal and vertical) through square number `square`.\"\"\"\n",
" def sq(x, y) -> int: return x + B * y\n",
" return ([sq(x, square // B) for x in range(B)], \n",
" [sq(square % B, y) for y in range(B)])\n",
"\n",
"def bingo_winners(boards, drawn, just_called) -> List[Board]:\n",
" \"\"\"Board(s) that win due to the number just called.\"\"\"\n",
" def filled(board, line) -> bool: return all(board[n] in drawn for n in line)\n",
" return [board for board in boards\n",
" if just_called in board\n",
" and any(filled(board, line) \n",
" for line in lines(board.index(just_called)))]\n",
"\n",
"def bingo_score(board, drawn, just_called) -> int:\n",
" \"\"\"Sum of unmarked numbers multiplied by the number just called.\"\"\"\n",
" unmarked = sum(n for n in board if n not in drawn)\n",
" return unmarked * just_called\n",
"\n",
"def bingo(boards, order) -> int: \n",
" \"\"\"What is the final score of the first winning board?\"\"\"\n",
" drawn = set()\n",
" for num in order:\n",
" drawn.add(num)\n",
" winners = bingo_winners(boards, drawn, num)\n",
" if winners:\n",
" return bingo_score(winners[0], drawn, num)\n",
"\n",
"answer(4.1, bingo(boards, order), 39902)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Figure out which board will win last. Once it wins, what would its final score be?"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def bingo_last(boards, order) -> int: \n",
" \"\"\"What is the final score of the last winning board?\"\"\"\n",
" remaining_boards = set(boards)\n",
" drawn = set()\n",
" for num in order:\n",
" drawn.add(num)\n",
" winners = bingo_winners(remaining_boards, drawn, num)\n",
" remaining_boards -= set(winners)\n",
" if not remaining_boards:\n",
" return bingo_score(winners[-1], drawn, num)\n",
" \n",
"answer(4.2, bingo_last(boards, order), 26936)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 5](https://adventofcode.com/2021/day/5): Hydrothermal Venture\n",
"\n",
"- **Input**: Each entry in the input is a \"line\" denoted by start and end x,y points, e.g. \"`0,9 -> 5,9`\". \n",
"\n",
"I'll represent a line as a 4-tuple of integers, e.g. `(0, 9, 5, 9)`."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"in5 = parse(5, ints)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: Consider only horizontal and vertical lines. At how many points do at least two lines overlap?"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def points(line) -> List[Point]:\n",
" \"\"\"All the (integer) points on a line.\"\"\"\n",
" x1, y1, x2, y2 = line\n",
" if x1 == x2:\n",
" return [(x1, y) for y in cover(y1, y2)]\n",
" elif y1 == y2:\n",
" return [(x, y1) for x in cover(x1, x2)]\n",
" else: # non-orthogonal lines not allowed\n",
" return []\n",
" \n",
"def overlaps(lines) -> int:\n",
" \"\"\"How many points overlap 2 or more lines?\"\"\"\n",
" counts = Counter(flatten(map(points, lines)))\n",
" return quantify(counts[p] >= 2 for p in counts)\n",
"\n",
"def cover(*xs) -> range:\n",
" \"\"\"All the ints from the min of the arguments to the max, inclusive.\"\"\"\n",
" return range(min(xs), max(xs) + 1)\n",
"\n",
"answer(5.1, overlaps(in5), 7436)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Consider all of the lines (including diagonals, which are all at ±45°). At how many points do at least two lines overlap?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For Part 2 I'll redefine `points` and `overlaps` in a way that doesn't break Part 1:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def points(line, diagonal=False) -> bool:\n",
" \"\"\"All the (integer) points on a line; optionally allow diagonal lines.\"\"\"\n",
" x1, y1, x2, y2 = line\n",
" if diagonal or x1 == x2 or y1 == y2:\n",
" dx, dy = sign(x2 - x1), sign(y2 - y1)\n",
" length = max(abs(x2 - x1), abs(y2 - y1))\n",
" return [(x1 + k * dx, y1 + k * dy) for k in range(length + 1)]\n",
" else: # non-orthogonal lines not allowed when diagonal is False\n",
" return []\n",
" \n",
"def overlaps(lines, diagonal=False) -> int:\n",
" \"\"\"How many points overlap 2 or more lines?\"\"\"\n",
" counts = Counter(flatten(points(line, diagonal) for line in lines))\n",
" return quantify(counts[p] >= 2 for p in counts)\n",
"\n",
"assert points((1, 1, 1, 3), False) == [(1, 1), (1, 2), (1, 3)]\n",
"assert points((1, 1, 3, 3), False) == []\n",
"assert points((1, 1, 3, 3), True) == [(1, 1), (2, 2), (3, 3)]\n",
"assert points((9, 7, 7, 9), True) == [(9, 7), (8, 8), (7, 9)]\n",
"\n",
"answer(5.1, overlaps(in5, False), 7436)\n",
"answer(5.2, overlaps(in5, True), 21104)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 6](https://adventofcode.com/2021/day/6): Lanternfish\n",
"\n",
"- **Input**: The input is comma-separated integers, each the age of a lanternfish. Over time, the lanternfish age and reproduce in a specified way."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"in6 = parse(6, int, sep=',')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: Find a way to simulate lanternfish. How many lanternfish would there be after 80 days?\n",
"\n",
"Although the puzzle instructions treats each fish individually, I won't take the bait (pun intended). \n",
"\n",
"Instead, I'll use a `Counter` of fish, and process all the fish of each age group together, all at once. That way each update will be *O*(1), not *O*(*n*). I have a hunch that Part 2 will involve a ton-o'-fish."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Fish = Counter # Represent a school of fish as a Counter of their timer-ages\n",
"\n",
"def simulate(fish, days=1) -> Fish:\n",
" \"\"\"Simulate the aging and birth of fish over `days`.\"\"\"\n",
" for day in range(days):\n",
" fish = Fish({t - 1: fish[t] for t in fish})\n",
" if -1 in fish: # births\n",
" fish[6] += fish[-1]\n",
" fish[8] = fish[-1]\n",
" del fish[-1]\n",
" return fish\n",
" \n",
"assert simulate(Fish((3, 4, 3, 1, 2))) == Fish((2, 3, 2, 0, 1))\n",
"assert simulate(Fish((2, 3, 2, 0, 1))) == Fish((1, 2, 1, 6, 0, 8))\n",
"\n",
"answer(6.1, total(simulate(Fish(in6), 80)), 350917)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: How many lanternfish would there be after 256 days?\n",
"\n",
"My hunch was right, so part 2 is simple:"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"answer(6.2, total(simulate(Fish(in6), 256)), 1_592_918_715_629)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That's over a trillion lanternfish. Latest [estimates](https://www.google.com/search?q=how+many+fish+are+in+the+sea) say that there are in fact trillions of fish in the sea. But not trillions of lanternfish, and not increasing from 300 to over a trillion in just 256 days.\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 7](https://adventofcode.com/2021/day/7): The Treachery of Whales\n",
"\n",
"- **Input**: The input is a single line of comma-separated integers, each the horizontal position of a crab (in its own submarine)."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"in7 = parse(7, int, sep=',')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The idea is that if the crabs can all align in one horizontal position, they can save you from a giant whale.\n",
"\n",
"- **Part 1**: Determine the horizontal position that the crabs can align to using the least fuel possible. How much fuel must they spend to align to that position? (Each unit of horizontal travel costs one unit of fuel.)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def fuel_cost(positions) -> int:\n",
" \"\"\"How much fuel does it cost to get everyone to the best alignment point?\"\"\"\n",
" # I happen to know that the best alignment point is the median\n",
" align = median(positions)\n",
" return sum(abs(p - align) for p in positions)\n",
"\n",
"answer(7.1, fuel_cost(in7), 352707)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Determine the horizontal position that the crabs can align to using the least fuel possible so they can make you an escape route! How much fuel must they spend to align to that position? (Now for each crab the first unit of travel costs 1, the second 2, the third 3, and so on.) "
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def fuel_cost2(positions) -> int:\n",
" \"\"\"How much fuel does it cost to get everyone to the best alignment point, \n",
" with nonlinear fuel costs?\"\"\"\n",
" # I don't know the best alignment point, so I'll try all of them\n",
" return min(sum(burn_rate2(p, align) for p in positions)\n",
" for align in range(min(positions), max(positions) + 1))\n",
"\n",
"def burn_rate2(p, align) -> int:\n",
" \"\"\"The first step costs 1, the second 2, etc. (i.e. triangular numbers).\"\"\"\n",
" steps = abs(p - align)\n",
" return steps * (steps + 1) // 2\n",
"\n",
"answer(7.2, fuel_cost2(in7), 95519693)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **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": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"490.543"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"positions = in7\n",
"mean(positions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That's not an integer, but I'll try it, along with the integers above and below it:"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{490: 95519693, 491: 95519725, 490.543: 95519083.0}"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{align: sum(burn_rate2(p, align) for p in positions)\n",
" for align in [490, 491, mean(positions)]}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We see that rounding down gives the right answer, rounding up does a bit worse, and using the exact mean gives a total fuel cost that is *better* than the correct answer (but is apparently not a legal alignment point). A reddit user with the name CrashAndSideburns looked more carefully into the use of the mean, and wrote [a paper](https://www.reddit.com/r/adventofcode/comments/rawxad/2021_day_7_part_2_i_wrote_a_paper_on_todays/) showing that the best alignment point must be within ±0.5 from the mean.\n",
"\n",
"Below I show a histogram of the number of crabs at each range of horizontal positions, along with red stars for the two alignment points (median and mean)."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[376.0, 490.543]"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"stars = [median(in7), mean(in7)]\n",
"plt.hist(in7, bins=33, rwidth=0.8); \n",
"plt.plot(stars, [50, 50], 'r*')\n",
"plt.ylabel('Number of Crabs'); plt.xlabel('Position')\n",
"stars"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 8](https://adventofcode.com/2021/day/8): Seven Segment Search\n",
"\n",
"- **Input**: Each entry in the input consists of 10 patterns followed by a \"`|`\", followed by 4 output values.\n",
" \n",
"Each pattern and output value represents a digit on a [7-segment display](https://en.wikipedia.org/wiki/Seven-segment_display), with each letter a–g representing one of the 7 segments. The mapping of letters to segments is unknown, but is consistent within each entry."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\n",
"parse(8) ➜ 200 entries:\n",
"----------------------------------------------------------------------------------------------------\n",
"((('daegb', 'gadbcf', 'cgefda', 'edcfagb', 'dfg', 'acefbd', 'fdgab', ' ... cdg', 'agecb', 'acbeg')))\n",
"----------------------------------------------------------------------------------------------------\n"
]
}
],
"source": [
"in8 = parse(8, lambda line: mapt(atoms, line.split('|')))"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [],
"source": [
"assert in8[0] == (('daegb', 'gadbcf', 'cgefda', 'edcfagb', 'dfg', 'acefbd', 'fdgab', 'fg', 'bdcfa', 'fcgb'), \n",
" ('cdfgba', 'fgbc', 'dbfac', 'gfadbc'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"- **Part 1**: In the output values, how many times do digits 1, 4, 7, or 8 appear?\n",
"\n",
"That's the same as asking *how many output values have 2, 4, 3, or 7 segments?*"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def count1478(entries) -> int:\n",
" \"\"\"How many of the rhs digits in the entries are a 1, 4, 7, or 8?\"\"\"\n",
" return quantify(len(value) in (2, 4, 3, 7) \n",
" for (lhs, rhs) in entries for value in rhs)\n",
"\n",
"answer(8.1, count1478(in8), 493)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: For each entry, determine all of the wire/segment connections and decode the four-digit output values. What do you get if you add up all of the output values?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Part 2 is *tricky*. The first output value `'cdfgba'` could be either a 0, 6, or 9. To figure out which one it is I could do some fancy constraint satisfaction. That sounds hard. Or I could exhaustively try all permutations of the 7 letters. That sounds easy! Here's my plan:\n",
"- Make a list of the 7! = 5,040 possible string translators that permute `'abcdefg'`.\n",
"- Decode an entry by trying all translators and keeping the one that maps all of the ten lhs patterns to a valid digit. `decode` then applies the translator to the four rhs values, concatenates them, and converts the result into an `int`.\n",
" - Note that `get_digit` must *sort* the translated letters to get a key that can be looked up in `segment_map`.\n",
"- Finally, sum up the decoding of each entry."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"segments7 = 'abcdefg'\n",
"segment_map = {'abcefg': '0', 'cf': '1', 'acdeg': '2', 'acdfg': '3', 'bcdf': '4',\n",
" 'abdfg': '5', 'abdefg': '6', 'acf': '7', 'abcdefg': '8', 'abcdfg': '9'}\n",
"\n",
"translators = [str.maketrans(segments7, cat(p)) for p in permutations(segments7)]\n",
"\n",
"def get_digit(pattern, translator) -> Optional[Char]:\n",
" \"\"\"Translate the pattern, and return a digit '0' to '9' if valid.\"\"\"\n",
" return segment_map.get(cat(sorted(pattern.translate(translator))))\n",
"\n",
"def decode(entry) -> int:\n",
" \"\"\"Decode an entry's rhs into a 4-digit integer.\"\"\"\n",
" lhs, rhs = entry\n",
" for t in translators:\n",
" if all(get_digit(pattern, t) for pattern in lhs):\n",
" return int(cat(get_digit(pattern, t) for pattern in rhs))\n",
"\n",
"answer(8.2, sum(map(decode, in8)), 1010460)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 9](https://adventofcode.com/2021/day/9): Smoke Basin\n",
"\n",
"- **Input:** The input is a *heightmap*: a 2D array of characters '0'–'9' representing the heights on the ocean floor. \n",
"\n",
"I'll use `parse` to get a tuple of rows (where each row is a tuple of digits), and turn that into a `Grid`."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"in9 = Grid(rows=parse(9, digits))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: Find all of the *low points* on your heightmap. What is the sum of the risk levels of all low points on your heightmap?\n",
"\n",
"A low point is a point where all the neighbors are higher. The risk level is 1 more than the height of the low point."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def low_points(grid) -> List[Point]:\n",
" \"\"\"All low points on grid.\"\"\"\n",
" return [p for p in grid \n",
" if all(grid[p] < grid[nbr] for nbr in grid.neighbors(p))]\n",
"\n",
"def total_risk(grid) -> int:\n",
" \"\"\"Sum of height + 1 for all low points on grid.\"\"\"\n",
" return sum(grid[p] + 1 for p in low_points(grid))\n",
"\n",
"answer(9.1, total_risk(in9), 607)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: What do you get if you multiply together the sizes of the three largest basins?\n",
" \n",
"I thought there was an ambiguity in the definition of *basin*: what happens if there is a high point that is not of height 9, but has low points on either side of it? Wouldn't that high point then be part of two basins? The puzzle instructions says *Locations of height 9 do not count as being in any basin, and all other locations will always be part of exactly one basin.* I decided this must mean that the heightmap is carefully arranged so that every basin has only one low point and is surrounded by either edges or height 9 locations.\n",
"\n",
"Given that definition of *basin,* I can associate each location with its low point using a [flood fill](https://en.wikipedia.org/wiki/Flood_fill) that starts from each low point. I can then get the sizes of the three largest (most common) basins and multiply them together."
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def find_basins(grid) -> Dict[Point, Point]:\n",
" \"\"\"Compute `basins` as a map of {point: low_point} for each point in grid.\"\"\"\n",
" basins = {} # A dict mapping each non-9 location to its low point.\n",
" def flood_fill(p, low):\n",
" \"\"\"Spread from p in all directions until hitting a 9;\n",
" mark each point p as being part of the basin with `low` point.\"\"\"\n",
" if grid[p] < 9 and p not in basins:\n",
" basins[p] = low\n",
" for p2 in grid.neighbors(p):\n",
" flood_fill(p2, low)\n",
" for p in low_points(grid):\n",
" flood_fill(p, low=p)\n",
" return basins\n",
"\n",
"def flood_size_products(grid, b=3) -> int:\n",
" \"\"\"The product of the sizes of the `b` largest basins.\"\"\"\n",
" basins = find_basins(grid)\n",
" return prod(c for _, c in Counter(basins.values()).most_common(b))\n",
"\n",
"answer(9.2, flood_size_products(in9), 900864)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **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": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"249"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"assert set(low_points(in9)) == set(find_basins(in9).values())\n",
"\n",
"len(low_points(in9))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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": 39,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"def show(in9, low=None):\n",
" plt.figure(figsize=(10, 10))\n",
" C = [in9[p] for p in in9]\n",
" plt.scatter(*transpose(in9), marker='s', s=10, c=C, cmap=plt.get_cmap('plasma'))\n",
" if low: plt.plot(*transpose(low_points(in9)), low, markersize=4)\n",
" plt.axis('square'); plt.axis('off')\n",
" \n",
"show(in9)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can optionally display the low points. Here I'll display them as black diamonds:"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"show(in9, 'kD')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Apropos to *Smoke* and *Water,* and to the color scheme of my plot, Gary Grady's drawing for the day references [Deep Purple](https://www.youtube.com/watch?v=_zO6lWfvM0g):\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 10](https://adventofcode.com/2021/day/10): Syntax Scoring\n",
"\n",
"- **Input**: Each entry in the input is a string of opening and closing brackets: `[({<` and `>})]`.\n"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\n",
"AOC2021/input10.txt ➜ 10196 chars, 102 lines; first 7 lines:\n",
"----------------------------------------------------------------------------------------------------\n",
"[(([{<{(<{{[({{}{}}{[]()})<{{}()}>]}}(([{{{}[]}[[]()]}[<{}[]]{()()}]](({{}{}}{{}()}))){[{({}())[[\n",
"<(({[<([{({[{{<>()}}[{<>()}({}{})]]<{<()<>>{[]()}}(((){}>[[][]])>}([{<[]{}>(<>[])}]))<[[[[[][]\n",
"(<<(<{{{{<<<[(()<>){()<>}][[()()]]>{<{[]{}}<<>()>>}>{(<{<>}([]{})><(<> ... []{})(()()))<<()[]>{{}[]}\n",
"[[[[<[{[(<{{{({}<>)((){})}((()())[()()])}}><[([((){})]<[()[]]{{}<>}>)[[{[]<>}][([]{})[{}()]]]]>)<{(<\n",
"[<(<[[((<{((<<<>[]>><<<>{}>>){<[{}<>][<>[]]><<<>()>[(){}]>})[<{[{}<>][(){}]}<[[]<>][{}[]])>{([<>[]][\n",
"(([[[[<([[{([{<>()}{()<>}][((){})]){[{[]<>}({}<>)][(<><>)[()[]]]}}<{{({}{}){[]{}}}<{<><>}({}{})>}>\n",
"{{{[<(<([<{({{[]()}[{}()]}{<()<>>(()<>)})}><<[{<()()>(()[])}<<<>[]]>][<{()}{<><>}>({{}[]})]>>](\n",
"----------------------------------------------------------------------------------------------------\n",
"parse(10) ➜ 102 entries:\n",
"----------------------------------------------------------------------------------------------------\n",
"('[(([{<{(<{{[({{}{}}{[]()})<{{}()}>]}}(([{{{}[]}[[]()]}[<{}[]]{()()}] ... []}>][<({}<>)>]]>)[[[((')\n",
"----------------------------------------------------------------------------------------------------\n"
]
}
],
"source": [
"in10 = parse(10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ideally, the brackets are balanced, but entries might be *corrupted* (an extra closing bracket of the wrong kind appears in the wrong place) or *incomplete* (one or more closing brackets are missing from the end).\n",
" \n",
"- **Part 1**: Find the first illegal character in each corrupted line of the navigation subsystem. What is the total syntax error score for those errors?\n",
"\n",
"\n",
"The instructions for Part 1 say *Some of the lines aren't corrupted, just incomplete; you can ignore these lines for now.* That suggests we will not ignore the incomplete lines in Part 2. So I'll define `analyze_syntax` to return a tuple of two values: an error score for use in Part 1, and the missing characters for an incomplete line, which I suspect will be used in Part 2."
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"error_scores = {')': 3, ']': 57, '}': 1197, '>': 25137}\n",
"open_close = {'(': ')', '[': ']', '{': '}', '<': '>'}\n",
"\n",
"def analyze_syntax(line) -> Tuple[int, str]:\n",
" \"\"\"A tuple of (error_score, missing_chars) for this line.\"\"\"\n",
" stack = [''] # A stack of closing characters we are looking for.\n",
" for c in line:\n",
" if c == stack[-1]: # A correctly matched closing bracket\n",
" stack.pop()\n",
" elif c in open_close: # A new opening bracket\n",
" stack.append(open_close[c])\n",
" else: # An erroneous closing bracket\n",
" return error_scores[c], cat(reversed(stack))\n",
" return 0, cat(reversed(stack))\n",
" \n",
"answer(10.1, sum(analyze_syntax(line)[0] for line in in10), 367059)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Find the completion string for each incomplete line, score the completion strings, and sort the scores. What is the middle score?\n",
"\n",
"I was right; Part 2 uses the missing characters (now called a *completion string*). To score the completion string, we treat it as a base-5 number, as shown in `completion_score`."
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def completion_score(completion:str) -> int:\n",
" \"\"\"The completion score for the completion string (the missing characters).\"\"\"\n",
" score = completion.translate(str.maketrans(')]}>', '1234'))\n",
" return int(score, base=5)\n",
"\n",
"def median_completion_score(lines) -> int:\n",
" \"\"\"The median completion score out of all the uncorrupted lines.\"\"\"\n",
" scores = (completion_score(completion) \n",
" for e, completion in map(analyze_syntax, lines) \n",
" if e == 0)\n",
" return median(scores)\n",
"\n",
"answer(10.2, median_completion_score(in10), 1_952_146_692)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 11](https://adventofcode.com/2021/day/11): Dumbo Octopus\n",
"\n",
"- **Input**: The input is a 2D array of characters `0`–`9` representing the energy levels of bioluminescent [dumbo octopuses](https://www.youtube.com/watch?v=eih-VSaS2g0)."
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"in11 = Grid(rows=parse(11, digits), neighbors=neighbors8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: Given the starting energy levels of the dumbo octopuses in your cavern, simulate 100 steps. How many total flashes are there after 100 steps?\n",
"\n",
"On each step, each octopus increases by one energy unit; then the ones with an energy level over 9 emit a flash, which makes their neighbors get one more energy unit (potentially causing others to flash); then the flashers reset to zero energy."
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def simulate_flashes(grid, steps=100) -> int:\n",
" \"\"\"Simulate octopus flashes for `steps` steps and return total number of flashes.\"\"\"\n",
" grid = grid.copy() # Don't mutate the original grid\n",
" flashes = 0\n",
" for step in range(steps):\n",
" flashers = set()\n",
" for p in grid:\n",
" grid[p] += 1\n",
" for p in grid:\n",
" check_flash(grid, p, flashers)\n",
" for p in flashers:\n",
" grid[p] = 0\n",
" flashes += len(flashers)\n",
" return flashes\n",
"\n",
"def check_flash(grid, p, flashers):\n",
" \"\"\"Check if grid[p] flashes, and if so, recursively spread.\"\"\"\n",
" if grid[p] > 9 and p not in flashers:\n",
" flashers.add(p)\n",
" for p2 in grid.neighbors(p):\n",
" grid[p2] += 1\n",
" check_flash(grid, p2, flashers)\n",
" \n",
"answer(11.1, simulate_flashes(in11, 100), 1591)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: If you can calculate the exact moments when the octopuses will all flash simultaneously, you should be able to navigate through the cavern. What is the first step during which all octopuses flash?\n",
"\n",
"I feel a bit bad that I have to copy/paste/edit the whole simulation function, changing just the number of steps and the return. But at least I don't have to copy the `check_flash` function."
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def simulate_flashes2(grid) -> int:\n",
" \"\"\"Simulate octopus flashes and return the first step during which all octopuses flash.\"\"\"\n",
" grid = grid.copy() # Don't mutate the original grid\n",
" for step in count_from(1):\n",
" flashers = set()\n",
" for p in grid:\n",
" grid[p] += 1\n",
" for p in grid:\n",
" check_flash(grid, p, flashers)\n",
" for p in flashers:\n",
" grid[p] = 0\n",
" if len(flashers) == len(grid):\n",
" return step\n",
" \n",
"answer(11.2, simulate_flashes2(in11), 314)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 12](https://adventofcode.com/2021/day/11): Passage Pathing\n",
"\n",
"- **Input**: Each entry in the input is a connection between two caves. Big caves are written in uppercase, small caves in lowercase. `start` and `end` are two special caves with the obvious meaning."
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\n",
"parse(12) ➜ 22 entries:\n",
"----------------------------------------------------------------------------------------------------\n",
"(['xx', 'xh'], ['vx', 'qc'], ['cu', 'wf'], ['ny', 'LO'], ['cu', 'DR'], ... xh', 'DR'], ['cu', 'xh'])\n",
"----------------------------------------------------------------------------------------------------\n"
]
}
],
"source": [
"in12 = parse(12, words)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: How many paths through this cave system are there that visit small caves at most once?\n",
"\n",
"My approach is as follows:\n",
"- I'll define a path as a list of cave names: `['start', ..., 'end']`.\n",
"- I'll construct `neighbors` as a mapping from a cave to the list of caves it connects to.\n",
"- I'll do depth-first search, starting from the trivial path `['start']` and returning all possible paths. "
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 48,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Path = List[str]\n",
" \n",
"def search_paths(path, neighbors) -> Iterable[Path]:\n",
" \"\"\"All paths that start with `path` and lead to 'end' using `neighbors`.\n",
" Small caves can only be visited once.\"\"\"\n",
" if path[-1] == 'end':\n",
" yield [path]\n",
" else:\n",
" for cave in neighbors[path[-1]]:\n",
" if cave.isupper() or cave not in path:\n",
" yield from search_paths(path + [cave], neighbors)\n",
"\n",
"neighbors = multimap(in12, symmetric=True)\n",
" \n",
"answer(12.1, quantify(search_paths(['start'], neighbors)), 4167)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: After reviewing the available paths, you realize you might have time to visit a single small cave twice. However, the caves named `start` and `end` can only be visited exactly once each. Given these new rules, how many paths through this cave system are there?\n",
"\n",
"At first I felt bad that I would again have to copy/paste/edit the code for Part 1. I felt better when I realized that the revised function `search_paths2` would have need to call the original `search_paths`: once the path returns to a small cave for the second time, the remainder of the search should be under the `search_paths` rules, not the `search_paths2` rules."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def search_paths2(path, neighbors):\n",
" \"\"\"Find all paths that start with `path` and lead to 'end' using `neighbors`.\n",
" Small caves can only be visited once, except one of them may be visited twice.\"\"\"\n",
" if path[-1] == 'end':\n",
" yield [path]\n",
" else:\n",
" for cave in neighbors[path[-1]]:\n",
" if cave.isupper() or cave not in path:\n",
" yield from search_paths2(path + [cave], neighbors)\n",
" elif cave.islower() and cave != 'start':\n",
" yield from search_paths(path + [cave], neighbors)\n",
" \n",
"answer(12.2, quantify(search_paths2(['start'], neighbors)), 98441)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 13](https://adventofcode.com/2021/day/13): Transparent Origami\n",
"\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 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": 50,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\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, 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"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The idea of this puzzle is that the dots are on transparent paper, and when following the `fold along y=7` instruction, all the dots below the line `y=7` are reflected above the line: they retain the same distance form the `y=7` line, but their `y` value becomes less than `7`. Similarly, for an `x` fold, all the points to the right of the line are reflected to the left. When we finish the folds, a code message will appear.\n",
"\n",
"- **Part 1**: How many dots are visible after completing just the first fold instruction on your transparent paper?"
]
},
{
"cell_type": "code",
"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",
" 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",
" else:\n",
" return {(x, line - abs(line - y)) for (x, y) in dots}"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 53,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"answer(13.1, len(fold(dots, folds[0])), 638)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Finish folding the transparent paper according to the instructions. What is the code?"
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [],
"source": [
"def origami(dots, instructions) -> None:\n",
" \"\"\"Follow all the instructions and plot the resulting dots.\"\"\"\n",
" for instruction in instructions:\n",
" dots = fold(dots, instruction)\n",
" plt.scatter(*transpose(dots), marker='s')\n",
" plt.axis('equal'); plt.gca().invert_yaxis()"
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 55,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"answer(13.2, origami(dots, folds), None) # actual answer: \"CJCKBAPB\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I kind of cheated here. I didn't want to write an OCR program, so I relied on my own eyes to look at the dots and see the code.\n",
"\n",
"**Note**: My transparent paper was folded 12 times. Is that physically feasible? [Britney Gallivan](https://www.youtube.com/watch?v=AfPDvhKvaa0&) says yes (barely)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 14](https://adventofcode.com/2021/day/14): Extended Polymerization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Input**: The input is a a polymer template (a string of one-letter element names, such as \"`NNCB`\") followed by a list of pair insertion rules (such as \"`CH -> B`\", meaning that a `B` should be inserted into the middle of each `CH` pair).\n",
"\n",
"I'll parse each line of the input into a list of `words` (thus ignoring the \"`->`\" characters); then pick out:\n",
"- `polymer`: the sole word on the first line.\n",
"- `rules`: the third through last lines, converted into a dict, like `{'CH': 'B', ...}`."
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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",
"----------------------------------------------------------------------------------------------------\n",
"parse(14) ➜ 102 entries:\n",
"----------------------------------------------------------------------------------------------------\n",
"(['ONSVVHNCFVBHKVPCHCPV'], [], ['VO', 'C'], ['VV', 'S'], ['HK', 'H'], ... ['FO', 'C'], ['VS', 'B'])\n",
"----------------------------------------------------------------------------------------------------\n"
]
}
],
"source": [
"in14 = parse(14, words)\n",
"polymer = in14[0][0]\n",
"rules = dict(in14[2:])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 1**: Apply 10 steps of pair insertion to the polymer template and find the most and least common elements in the result. What do you get if you take the quantity of the most common element and subtract the quantity of the least common element?\n",
"\n",
"Pair insertion means inserting the element on the right hand side of a rule between each two-element pair. All two-element substrings are considered as pairs (that is, the pairs overlap). All insertions happen simultaneously during a step."
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def pair_insertion(polymer, rules, steps) -> str:\n",
" \"\"\"Insert elements into polymer according to rules; repeat `steps` times.\"\"\"\n",
" for _ in range(steps):\n",
" polymer = cat(pair[0] + rules[pair]\n",
" for pair in pairs(polymer)) + polymer[-1]\n",
" return polymer\n",
"\n",
"def pairs(seq) -> list: return [seq[i:i+2] for i in range(len(seq) - 1)]\n",
"\n",
"def quantity_diff(polymer) -> int:\n",
" \"\"\"The count of most common element minus the count of least common element.\"\"\"\n",
" counts = list(Counter(polymer).values())\n",
" return max(counts) - min(counts)\n",
"\n",
"assert pairs('NNCB') == ['NN', 'NC', 'CB']\n",
"assert pair_insertion('NNCB', rules={'NN': 'C', 'NC': 'B', 'CB': 'H'}, steps=1) == 'NCNBCHB'\n",
"\n",
"answer(14.1, quantity_diff(pair_insertion(polymer, rules, 10)), 3259)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: Apply 40 steps of pair insertion to the polymer template and find the most and least common elements in the result. What do you get if you take the quantity of the most common element and subtract the quantity of the least common element?\n",
"\n",
"The instructions warn us that the resulting polymer after 40 steps will be *trillions* of elements long. So it isn't feasible to just call `pair_insertion` with steps=40. Instead, I'll employ the same trick as in Day 6: use a `Counter` of element pairs so that, for example, all the `'NC'` pairs in the polymer are handled simultaneously in one operation, rather than handling each one individually. No matter how many steps we do, there are only 100 distinct element pairs, so iterating over them 40 times should be very fast. \n",
"\n",
"Here's an example Counter of element pairs:"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'NN': 1, 'NC': 1, 'CB': 1})"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Counter(pairs('NNCB'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"What letters does this represent? The complication is that the pairs overlap, so, if we added up the counts for all the times that, say, the letter `'C'` appears in keys of the Counter, we'd get 2; but it should be 1. We can divide each letter count by 2 to avoid double counting. However the first and last letters in the polymer are *not* double-counted, so we need to add back 1/2 for each of those letters. Fortunately the first and last letters are invariant under pair insertion, so we can do this adjustment at the end; we don't have to do it for each step.\n",
"\n",
"So all in all there are three representations of a polymer:"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [],
"source": [
"Polymer = str # e.g. 'NNCB'\n",
"PairCounter = Counter[str] # e.g. Counter({'NN': 1, 'NC': 1, 'CB': 1})\n",
"LetterCounter = Counter[Char] # e.g. Counter({'N': 2, 'C': 1, 'B': 1})"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here's how we convert a PairCounter into a LetterCounter:"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [],
"source": [
"def letter_counts(pair_ctr: PairCounter, polymer: Polymer) -> LetterCounter:\n",
" \"\"\"Return a Counter of the letters in the polymer described by the `pair_ctr`.\"\"\"\n",
" letters = set(flatten(pair_ctr))\n",
" def letter_count(L) -> int:\n",
" return int(sum(pair_ctr[L+M] + pair_ctr[M+L] for M in letters) / 2\n",
" + (L == polymer[0]) / 2 + (L == polymer[-1]) / 2)\n",
" return Counter({L: letter_count(L) for L in letters})"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'B': 1, 'C': 1, 'N': 2})"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"letter_counts(Counter(pairs('NNCB')), 'NNCB')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's make sure it works when the first and last letters are the same:"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [],
"source": [
"assert letter_counts(Counter(pairs('NNCB')), 'NNCB') == letter_counts(Counter(pairs('NCBN')), 'NCBN')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now the new function `pair_insertion_diff` can call on `pair_insertion2` to solve Part 2 (as well as Part 1):"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 63,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def pair_insertion2(polymer, rules, steps) -> PairCounter:\n",
" \"\"\"Insert elements into polymer according to rules; repeat `steps` times.\n",
" Return a Counter of element pairs.\"\"\"\n",
" pair_ctr = Counter(pairs(polymer))\n",
" for _ in range(steps):\n",
" pair_ctr2 = Counter()\n",
" for LM in pair_ctr:\n",
" pair_ctr2[LM[0] + rules[LM]] += pair_ctr[LM]\n",
" pair_ctr2[rules[LM] + LM[1]] += pair_ctr[LM]\n",
" pair_ctr = pair_ctr2\n",
" return pair_ctr\n",
"\n",
"def pair_insertion_diff(polymer, rules, steps):\n",
" \"\"\"Most common minus least common after `steps` of pair insertion.\"\"\"\n",
" return quantity_diff(letter_counts(pair_insertion2(polymer, rules, steps), polymer))\n",
"\n",
"assert Counter(pairs('NNCB')) == Counter({'NN': 1, 'NC': 1, 'CB': 1})\n",
"assert pair_insertion2('NNCB', rules={'NN': 'C', 'NC': 'B', 'CB': 'H'}, steps=1) == (\n",
" Counter({'NC': 1, 'CN': 1, 'NB': 1, 'BC': 1, 'CH': 1, 'HB': 1}))\n",
"assert letter_counts(Counter({'NC': 1, 'CN': 1, 'NB': 1, 'BC': 1, 'CH': 1, 'HB': 1}), 'NNCB') == (\n",
" Counter({'N': 2, 'C': 2, 'B': 2, 'H': 1}))\n",
"assert pair_insertion_diff('NNCB', rules={'NN': 'C', 'NC': 'B', 'CB': 'H'}, steps=1) == 1\n",
"\n",
"answer(14.1, pair_insertion_diff(polymer, rules, 10), 3_259)\n",
"answer(14.2, pair_insertion_diff(polymer, rules, 40), 3_459_174_981_021)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 3**: Polymer length?\n",
"\n",
"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": 64,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"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",
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"in15 = Grid(rows=parse(15, digits))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"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",
"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": 66,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 66,
"metadata": {},
"output_type": "execute_result"
}
],
"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) # 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(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 path_cost[goal]\n",
"\n",
"answer(15.1, search_grid(in15), 687)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: The entire cave is actually five times larger in both dimensions. Your original map tile repeats to the right and downward; each time the tile repeats, all of its risk levels are 1 higher than the tile immediately up or left of it. However, risk levels above 9 wrap back around to 1. Using the full map, what is the lowest total risk of any path from the top left to the bottom right?\n",
"\n",
"Here's how to define the full map of the cave:"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [],
"source": [
"def repeat_grid(grid, repeat=5):\n",
" \"\"\"Extend the grid to be `repeat` times larger in both directions.\n",
" Values within each repeated block are increased by 1 for each repetition to the right or down,\n",
" but values over 9 wrap around to 1.\"\"\"\n",
" w, h = grid.width, grid.height \n",
" return Grid([(x + xr * w, y + yr * h), clock_mod(grid[x, y] + xr + yr, 9)]\n",
" for xr in range(repeat) \n",
" for yr in range(repeat)\n",
" for x, y in grid)\n",
"\n",
"def clock_mod(i, m) -> int:\n",
" \"\"\"i % m, but replace a result of 0 with m\"\"\"\n",
" # This is like a clock, where 24 mod 12 is 12, not 0.\n",
" 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, `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": 69,
"metadata": {},
"outputs": [],
"source": [
"from heapq import heappop, heappush\n",
"\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",
" Path = lambda s: ([] if (s is None) else Path(previous[s]) + [s])\n",
" while frontier:\n",
" (f, s) = heappop(frontier)\n",
" if h_func(s) == 0:\n",
" return path_cost[s], Path(s)\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, 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)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With A* search the run time is greatly improved, from 5 minutes down to about 1 second."
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 70,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"answer(15.2, Astar_search_grid(full_map)[0], 2957)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **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)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"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). \n",
"\n",
"\n",
"\n",
"For now I will leave the input as a string of hex digits:"
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----------------------------------------------------------------------------------------------------\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"
]
}
],
"source": [
"in16 = parse(16)[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"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 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": 74,
"metadata": {},
"outputs": [],
"source": [
"Bits = str # a string of '0's and '1's\n",
"Packet = namedtuple('Packet', 'V, T, contents') # V is version; T is type ID\n",
"\n",
"def bits_from_hex(hex) -> Bits: \n",
" \"\"\"Convert a hexadecimal string into a bit string, making sure each hex digit is 4 bits.\"\"\"\n",
" # I could have used just `bin(int(hex, 16))`, except that wouldn't left-zero-pad when needed.\n",
" return cat(f'{int(x, 16):04b}' for x in hex)\n",
"\n",
"def int2(bits: Bits) -> int: \n",
" \"\"\"Convert a bit string into an int.\"\"\"\n",
" return int(bits, 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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": 75,
"metadata": {},
"outputs": [],
"source": [
"def parse_int(L, bits) -> Tuple[int, Bits]:\n",
" \"\"\"Parse an integer from the first L bits; return the int and the remaining bits.\"\"\"\n",
" return int2(bits[:L]), bits[L:]\n",
"\n",
"def parse_packet(bits) -> Tuple[Packet, Bits]:\n",
" \"\"\"Parse a packet; return it and the remaining bits.\"\"\"\n",
" V, T, bits = int2(bits[0:3]), int2(bits[3:6]), bits[6:]\n",
" parser = parse_literal_packet if T == 4 else parse_operator_packet\n",
" return parser(V, T, bits)\n",
" \n",
"def parse_literal_packet(V, T, bits) -> Tuple[Packet, Bits]:\n",
" \"\"\"Build a packet with a literal value; return it and the remaining bits.\"\"\"\n",
" literal = ''\n",
" while True:\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",
"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: # 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: # 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"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we're ready to solve the puzzle by summing up the version numbers, `V`, of all the packets:"
]
},
{
"cell_type": "code",
"execution_count": 76,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 76,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"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",
"packet16, _ = parse_packet(bits_from_hex(in16))\n",
"answer(16.1, sum(p.V for p in nested_packets(packet16)), 989)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This was way more code than previous days! Here are some assertions I used to make sure I was on the right track:"
]
},
{
"cell_type": "code",
"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",
" == (Packet(V=1, T=6, contents=[Packet(V=6, T=4, contents=10), \n",
" Packet(V=2, T=4, contents=20)]),\n",
" '0000000'))\n",
"\n",
"assert (parse_packet('11101110000000001101010000001100100000100011000001100000')\n",
" == (Packet(V=7, T=3, contents=[Packet(V=2, T=4, contents=1), \n",
" Packet(V=4, T=4, contents=2), \n",
" Packet(V=1, T=4, contents=3)]),\n",
" '00000'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- **Part 2**: What do you get if you evaluate the expression represented by your hexadecimal-encoded BITS transmission?\n",
"\n",
"The evaluation rules are that a literal packet evaluates to the number that is its contents, and an operator packet applies an operator determined by the type id (in the `packet.T` field) to the list of values formed by evaluating the subpackets. I put the operators into the `packet_ops` dict."
]
},
{
"cell_type": "code",
"execution_count": 78,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 78,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def eval_packet(packet) -> int:\n",
" \"\"\"Evaluate a packet according to the operator rules.\"\"\"\n",
" if packet.T == 4:\n",
" return packet.contents\n",
" else:\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",
" 5: lambda v: int(v[0] > v[1]), \n",
" 6: lambda v: int(v[0] < v[1]), \n",
" 7: lambda v: int(v[0] == v[1])}\n",
"\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": "iVBORw0KGgoAAAANSUhEUgAAAYQAAAD4CAYAAADsKpHdAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nOydeVyVVf7H3+durCqIoCjKIu4bBZpaamqZtmipWU2mY6Xl2DYtU03zm2pmmppmypqaqVyyTSubsrTVTC03VEjcN0AQFBEQRGS52/n98VwuoCAo92E9b173xT3Pcs652/N9zvme7+crpJQoFAqFQmFo7A4oFAqFommgDIJCoVAoAGUQFAqFQuFCGQSFQqFQAMogKBQKhcKFqbE7UFc6dOggIyIiGrsbCoVC0axITEzMlVIG1+XYZmMQIiIiSEhIaOxuKBQKRbNCCJFe12PVlJFCoVAoAGUQFAqFQuFCGQSFQqFQAM3Ih1AdNpuNzMxMSktLG7srTRpvb2/CwsIwm82N3RWFQtGEadYGITMzkzZt2hAREYEQorG70ySRUpKXl0dmZiaRkZGN3R2FQtGEadZTRqWlpQQFBSljcAGEEAQFBalRlEKhqJVmbRAAZQzqgHqPFJdKYno+/1mXTGJ6vkeOUzRtmvWUkUKhuDgS0/OJT81jaFQQseGBVfYVW+3kF9voEuADwLsbU/n7twdwSonFZOChMT0wGw3MHhkFwLe7s8g6XUpM1wDuXBRPmc2JySj4ZM4wYsMDsTmcmI2GOrevaHx0NQhCiF7Ap5U2RQF/BgKA2UCOa/sfpZTf6tkXhaK1s2rnMR5dvhOHU7vAzxoewY6MAj6ZMwyAf3x3gC92HGP3c9cBsHJnFnanli/FZnfy7e4s8ottboOweu8JdmaeptTmwGp3IgGbQxKfmkdseCD3fZhIYYmN/80dDsDzq/by4ZZ0t4FZeu9QZRSaGLpOGUkpD0opY6SUMUAsUAyscO2eX76vORuDkpISRo0ahcPhICkpiWHDhtGvXz8GDhzIp59W2MI777yTXr160b9/f+6++25sNlutdY8fP56AgABuvPHGKttrquvrr7/m2Wef9ewLVDRpKk/VSCk5WViK3eEEYN3Bk9yxIJ5iqx2A/yUew+aQOKV2gT+SV4zZaHAfPzGmC8/d1I/ypFlPXNcLb7MBowCzycDzk/qz6akx7rZfu/0y1j1+NUOjgrCYtOO8TAaGRgUBcMOAUG65vIv7+HUHTmJ3VrT/f1/u5t2NR2p8PYqGpyF9CGOBFCllncOo9cDTX7h3332XyZMnYzQa8fX15YMPPmDv3r18//33PPLIIxQUFADaRfzAgQPs3r2bkpISFi1aVGvdTzzxBB9++OF522uq64YbbmDlypUUFxd75LUpmjar957gtne28Mrqg9y5KJ5/r01myN9/IjX3LABOp6TE5iC/WLthmDk8HC9TxQV+9ogoPrznCkyuaZ3Y8ECmxIa5fU5XRndg6b1DeXRcrwvezceGB7qPWza74rgpsWHceUW4+7hXpsVUMTDeZiMlNgcADqdk7CvruX1BxetRRqHhaUgfwu3Ax5XKDwghZgAJwGNSyvM+fSHEHGAOQLdu3Wpt4LZ3tjA1Noxb47picziZvmgrtw/pyi2XhVFidTDlrU0cPlmkDZmNBqKC/XhobA/G9w/l1Fkrcz9KZPaIKK7p25GTZ0oJaeNda5tLly5l2bJlAPTs2dO9vXPnzoSEhJCTk0NAQADXX3+9e9+QIUPIzMyste6xY8eyfv3687bXVJcQgquvvpqvv/6aadOm1Vq/onlx8kwpb69P5ZbLujAgrB0/H8qpMqVTXGbnuZv6EuhrAWBsn46M7dPRff6Y3h1ZNnvoRc3hx4YHeuy4csNRXftnSm2AwO6Q2tST3ckrqw/y7zsuo4O/V63tKzxDg4wQhBAWYCLwmWvTW0B3IAbIAl6p7jwp5QIpZZyUMi44uE5ifReksNSOvXzI7HBSWGqvV31Wq5XU1FSqU2Hdtm0bVquV7t27V9lus9n48MMPGT9+fL3arqmuuLg4NmzYUO+6FQ1LdSPXs2V25i37lVU7jwNgNhhYti2dQ9lnAJgU07nKHf+4fp347ZWRBLep+QIaGx7IvNHRjTZ3X1P7Ab4WXp46EC/XCMJgEGxOyeNkYRkApTYHiWmn1HSSzjTUCGEC8KuUMhug/D+AEGIh8LUnGvn0vmHu52ajoUrZx2Lk9dsv485F8djsTswmA6/ffpn7i9nez1Ll+LqMDnJzcwkICDhve1ZWFnfddRfvv/8+BkNVm/u73/2OkSNHMmLEiIt+fedSXV0hISEcP3683nUrGo7EtFPcuXgrVrs2l3/LZV14ZVoMvhYjR/OKKSjRpnwC/Szsfu4698qdIZFBF33H35Q5dwTROcCb0HbaiqfHP9vJN7uyEALlkNaRhjIId1BpukgIESqlzHIVbwH2NEQnLjRkvRR8fHzOC/gqLCzkhhtu4G9/+xtDhw6tsu/5558nJyeHd955p17tXqiu0tJSfHx86l2/omF4dHkSe46dxmp34pQgwG0AhBCsevCqKsefu4yzrlM6zYWaXk95KE25Q3rtgewW9bqbCrpPGQkhfIFrgS8qbX5ZCLFbCLELGA38Xu9+lOPJIXNgYCAOh8NtFKxWK7fccgszZszg1ltvrXLsokWL+OGHH/j444+rjBq2bdvGjBkzLqrdmuoCOHToEP3797/EV6TQm4W/pDLt7S3uco+QNlzeLbBilY7ZwO+ujm7EHjZNfjs80j2dZDIaWPhLqnsqTeE5dB8hSCmLgaBztt2ld7sNxbhx49i4cSPXXHMNy5cv55dffiEvL4/33nsPgPfee4+YmBjuv/9+wsPDGTZMm5aaPHkyf/7znzl69GiNd/QjRozgwIEDFBUVERYWxuLFi7nuuutqrAtg3bp1vPjii/q/cMV5VBd0tfZANv/+KZlP5gzF22yknY+Z0ABvyuwOvExG5l6t+ZhujevaYqZ+9KDy6H5Al3asO3iSK6M7AGB3ONmZeVq9fx5ARSrXkwceeIBXX32Va665hunTpzN9+vRqj7Pbq3dgb926lXnz5lW7rybncE11ZWdnU1JSwoABA+rQc4UnSUzP585F8W4/wOu3x3DToC6YjQYsJgM5Z8ro2t6XaYO7Mm1w1/POb2lTP3pQ+T0a2VNbZCKl5PYF8ezIKECqgLd6owxCPbnssssYPXo0DocDo9F40ef/85//9Fhfjh49yiuvVLtgS6ETRWV2PtiSxrH8ErcfAGBjci43DerCiB7BjOhR/xVyiupxOCVCaDEX5ctVyyOlFRdPsxe3awrcfffdl2QMPM3gwYOJiYlp7G60eA6cKGTHUW3po8kgeGtdCk6XHIRRgLfZwLS42uNmFPXHZDTw1IQ+bv+C0Wigb+e2jd2tZosaISgUtSClJLuwjE7ttKXIDy7bQZC/hU/mDMPbbGTjU2No52NmqvIDNArl/oWNh3N4b3MaS+PTGd0rpLG71SxRBkGhqAYppVvC4ekvdrP+YA6bnxqDwSB4Zdogt3EAaOejZaJTfoDGo/y9HxIZRFigWnZ9qSiDoFBUIjE9nw+3pLExOZefHruadj5mJsZ0JjY8EIeUGBAMDDs/GFHRNBjWXVvQKKXk+VV78TIbGde3kzLUdUQZBEWrJ+dMGQs3pNK/c1v+8Pku90qhzcm5TBgQyvDuHRq5h4qL5cd92by3OR0BvL85Ta08qiPKqVxP6ip/fc899zBo0CAGDhzI1KlTKSoqumC9eXl5jB49Gn9/fx544IEq+xITExkwYADR0dE89NBDbrnixx9/nLVr13r+RbZAsgtLOeJSBRUCPtiSxnd7TlSJGC5XDVU0Pw6fLMIgqLLySFE7rc8gZGyDDa9o/z1AXeWv58+fz86dO9m1axfdunXjzTffvGC93t7e/PWvf+Vf//rXefvmzp3LggULOHz4MIcPH+b7778H4MEHH+Sll17yyOtqiZQbTqdTcuMbG/nnDwcA6ODvReKfruXeEVHulULmSrr+iuZH5RwNJqOB0Ha1a5MpWppBWHID7FiqPXfYtPJO1126tRjeHgHvXQ9rX4D3b9LK+1Zq+8/maccf/E4rn8k+v/5qWLp0KZMmTQI0+esePXoAVeWvAdq21ZbCSSkpKSmpNc+xn58fV111Fd7eVb/IWVlZFBYWMmzYMIQQzJgxgy+//BKA8PBw8vLyOHHiRJ363pr490+HuW1BPKApaf5jygAeG9fLvd/Py1RF119NMTRvKj7LnnRr78tb67WlwYoL07p8CKWnwWEHJDhc5XpwsfLXs2bN4ttvv6Vv376XHEB27NgxwsLC3OWwsDCOHTvmLl9++eVs2rSJKVOmXFL9LYWjecV8lpjBQ2O1PMAd23oR1cEPq92JxWRgTO+O1Z6nVgq1HMo/yyujg/ExGzEYLnwTpmhpI4RZ38Bld2rPjWatPOg2rWzxhSmLwOQNwghGi1buO1Hb7xekHd9rglZuU/0FozK1yV8vWbKkivjckiVLOH78OH369KniX7gYyqc9KlN5tNGa5a/X7Mvm1R8Pkpiez6HsM7y1PoU9xzSjf9vgbrw0ZSAWU8v6yitqJ6ZrAL06tSExPZ/5Px5S+RQuQOv6dXQdAjNXwphntP9dh9SruouVvwYwGo3cdtttfP7555fUZlhYWJVsa5mZmXTu3Nldbq3y19/vyeLeDxJ446dk7lwUT1sfE1v/OJbLuqm7fYW2nPi2d7bw+k+HVXrOC9C6DAJoRmDEY/U2BlB3+WspJcnJye7nq1atonfv3gCsWLGCp59+us5thoaG0qZNG+Lj45FS8sEHH7h9GNC65K/f/jmFt9anAJCScxZBxaqS7Wn5BKnUixWkbYK1f4N0l/S23QrFp1xTqIC9TCs7HVXL6Vs8ugijsYhPzcMpK9KNqlVH1dO6fAg6UBf564EDBzJz5kwKCwuRUjJo0CDeeustAFJSUtwO53OJiIigsLAQq9XKl19+yerVq+nbty9vvfUWv/3tbykpKWHChAlMmKBNc9lsNpKTk4mLi2uQ197QOJyS/VmF9O/SDoA9x05TPoE2NCoIL7PBnQ2vRa4QOroV0jZA5EgI7gWZCRA6CPw6wKlU2L4YBt8D7aPgaDx8/XuYvBBsxfDhzeCwwqZ/w2+/hsLj8NlMmLsFOvaFfV/BF7PhwV8hqDvs/gy+mqdNsTpsYDCAbwjctx78QzQDk/wjjHwCLH5QkAFnT2qG5uhmiBjhkZsuT1G+6qhFfz88gDII9aSu8tebNm2qdntSUhLz58+vdl9aWlq12+Pi4tiz5/wkc19//TVTp07FZGqZH+sbaw/z5tpkNj81hpC23rx2WwwmVwYxT2fDazSk1AIjSgpg5ycQNQpC+mjPV9wHGMD0L7j+ZVj5INzxieb3OpsLCUugxzjNIFj8tf8Gk2ZEnK6RgNOulfveDOP/AW06adtDY7Syr+tC2SUOeozXLvrSoaUq8wnQ6gXI2gmb34SrXaPbpGWw/u8VBkQYNIMwYyUYTVBaqBkOQ+OIQFb+fnRq6007n5b5G6kv6l2pJ/WVv/7oo4881he73c5jjz3msfoam4xTxfzl6308PLYH/bu0Y8rlYfQIaUOArwXAbQzKaRYrhDK2aRfkbsMg9xB07A9hcdr0zH+GwKgnYchs7W7++ydhwj81g5CXjBYu59T2nT4Gs76HEG3qkbDB8EylxQSd+sPtriXYZYVg9NLOM1q0u/eg7tqjnOCe2qOckN4w8jE48nPFeTe9pi3OABj2Oxg6tyK35cBpkH8Edi3XDIh0QuExzRgA/PBHSF0Pv3fdyBz5RTMaRov2fjTAiCI2PJD+Xdpyxd9/4qroDrz5m8t1ba85ortBEEKkAWfQFnrapZRxQoj2wKdABJAGTJNSNlsvz913393YXQA4L21nc+TE6VKKymxEh7ShrbeZ/VmFHCsooX+XdnRt70vX9r6N3cW6cyYb7CUQGAFOJ7wZBwXp2ijAaNHu1ofO1QyCTyD0vhE6aHEs+AXD48nadBBod/6b36y4OEePrXoBvVBcS/liiou98NZ2XuU220dC3N2w98uKPk5eWLG/z0ToXEmaff0/oDgP8tO044UBxr+oGUMd8TIZeWd6LL07KYns6hDVLWP0aAOaQYiTUuZW2vYycEpK+ZIQ4ikgUEr55IXqiYuLkwkJCVW27d+/nz59+ujQ65ZHU36v3KknI9vz0CdJRIf48/7d2sWnsupok2f/Ku3i1t8VA/LaQOgSC7cu0coLx8CxXwGpLX2+8iEY/aeKu+jaKB9dNLH5+SrUtY9nc2HDq7D1bW1EAZo/5L5ftOd7PoeuQ6FdF126WV2605aKECJRSlknx2JjTRlNAq52PX8fWA9c0CAoWiavrTnEv386DIDFZOAP1/VibJ+KGJAmZQwytmlz6tHXahe7X/4Jp9Lg5v9o+7cvhrIzFQZhwj/Ar5Iu//iX4P2JFXfQva6vuzEArc2magjKqWsf/TpAv5sh4V3X+2GG0c9o+85kw//uhrHPwohHNZ/Erx9o02pRo+r9HiSm53PHQi3dqbfJwNLZKiq9nIYwCBJYLYSQwDtSygVARyllFoCUMksIUW02CyHEHGAOQLduKgNVS+Fw9hnCg/ywmAwcOHHGnXbSZndSYnMSHuTXuB0sx26F3IPQaYBmDN4dr93NbnpDm0qxW7UpoXKmLAbvdhXl8iDHci516qalUtP74R8C87ZXvJeJ78G3jwMGbQlsPWOI4lPzsLkUba0OlXKzMg0Rh3CllPJyYAIwTwgxsq4nSikXSCnjpJRxwcEqL21LICmjgGvn/8K3u7MAmH1VJN7mJiIoV1KgaVk5bFp502ua3lVJgXbRKp9edVi18phnYOq7Fef7BdV+x+/BOJgWQXXvhxCag7tcLeBsLtqlyuVQ3/ImfHqXNhq7BMqXKBuFNipVS1Ar0N0gSCmPu/6fBFYAQ4BsIUQogOv/Sb37oRd1lb8u58EHH8Tf37/Wem02GzNnzmTAgAH06dOHF198EdCC30aOHIndbvf4a9EDKSXvbjzCZwkZAAzs0o7nJ/ZjZE/NwMdGtG88QbmzedpURLmQYcpP8PHtcGK3Vu53C0x7v2JljsmrQvYkYkTD9bO1Ez226nvfriuU5FcsgT2xB2ylF66jEkrEsGZ0nTISQvgBBinlGdfzccBfgJXATOAl1/+v9OxHZZJOJpGQnUBcxzhiQuqfkL46+esePXpw/PhxYmNjue6669x6RwkJCW457Nr47LPPKCsrY/fu3RQXF9O3b1/uuOMOIiIiGDt2LJ9++il33nlnvfuvF6eLbbTzNSOEYPW+E3Tw9+LWuK4YDIKZwyOqHKv7ctFyR2doDGQlQfex2oqXwmPaWv4pi2HAVIgaDbO+g5C+2nkdelSs+lHTPY1Hde99ebyG3QofTYHwYXDre3V2aseGB1JYYuPZlXv4ZM4w/L3UCnzQ34fQEVjhcgyagGVSyu+FENuB5UKIe4CjgEfWS876fhaToidxc/TN2Jw25qyew+Qek7mp+02U2EuY8e0MUk6n4HA6MBvNRLaN5L5B93FN+DXkl+bz6PpHmdlvJld3vZrcklw6+NSeKWvp0qUsW7YM0OSvy6ksfx0QEIDD4eCJJ55g2bJlrFixotZ6hRCcPXsWu91OSUkJFovFHdF888038/TTTzdZg7DglxTeWJvMlqfH4u9lYvHMwfg1xg/OboWvH9Gibp0OzXFpt4LJRzMIIX21ueryi75vewgfXnN9zcGp21I5970vX2xgNMPkBeDlrxmD92/SZDdMXjBz1QU/L1+LkbbeZvKKypRBcKHruyClTAUGVbM9DxirZ9vVccZ2BrvTjkRic9o4Y7u0OchyLkb++s0332TixImEhobWqe6pU6fy1VdfERoaSnFxMfPnz6d9+/YA9O/fn+3bt9er756k2Grn88RMxvbpSOcAH4Z370Cx1eHWjmlQY5D4nja1cPld2sXiwDcun4BL8nzU41pQFWjz/ZWDsRTNDyG0lUegOZztVrTP2uXnuYBBuCIqiGXKf1CFFmUWl4xf4n5uNpirlH1MPrw04iVmr56NzWnDbDDz0oiX3NNGgd6BVY6vy+igNvnr999/H4PBwPHjx/nss89Yv359nV/Ltm3bMBqNHD9+nPz8fEaMGME111xDVFQURqMRi8XCmTNnaNOmTZ3r9CSJ6flsScllWPcOhLTx4tmVe7E5JHdfFUn/Lu3cekO6c/hHLeJ32DytvPdLzRBcfpd2sbh9KXw0tVJA17UN0y9Fw1Pu56kckf3rh9D5Mi1yuxoqf4+VL6GFGYTaiAmJYeG4hR7zIdRV/nrHjh0kJycTHR0NQHFxMdHR0W4F1OpYtmwZ48ePx2w2ExISwpVXXklCQgJRUVEAlJWVnZdNraFITM9n2ttbcEiJtzmZpfcO5cdHRxHVoQGWi2bthMOrNVE1gEM/wMFv4Yq5mgDb7Us1zZxyIq5Sc/+thXN9DZ0GwPIZEHU13PL2eYcnpudz+4It2BwSL1Myy1Q8QusyCKAZBU84k6Gq/LW3t3eN8tc33HBDlbSW/v7+bmOwYsUKtm3b5l5FVE63bt1Yu3Yt06dPp7i4mPj4eB555BEA8vLyCA4Oxmw2e+R11AWnU7Ijo4DY8MBqpYTnjY72bIPlzsGgnnAqRZM0sPhpip/r/wEx06FtKIz9sxYAVp6IyFKNUVJz/62Hcz/ruZsrlgsXHNVE+AxmiBxBfGp7HK4gGLuKRwBaoUHwNHWRv46JqdkA1SR/PW/ePGbNmkX//v2RUjJr1iwGDhwIwLp167j++ut1eT018cGWNJ5btY8fHhmpr9S0tRji/6tFATtsmjqmw6pJQESOgJg7IOY3mhMRwFtp0igugG/7iudrntMkMYQBfvFi7HUf8YaSxK6CMgj1pK7y15UpKipyP69J/trf35/PPvus2vOXLVt23ojC05RYHby3OY3BEYHERbTn5su6EOhnoXuwHyajwbNS04XHtYt/YDgU58Lav0J5uhsncNXvNWMA4NU4PhNFCyCkL4gVmhKrw0rvM/EsvXce8al5XBHZvtWPDqA1ZkzzMJXlry+Fjz76iIuJwrZardx888306tXrktqrjfIhtBCweOMR1h/MASDA18KkmC5V8g/MGx19aT8iKbXAItAMwX+ugI2vauWAbjDxjaq5r3s17GhI0UKJHKnJgAujNvLc8l9izeks23qUb1yR862dZj9CaApqmA0pf22xWJgxY8ZFnVNXRdtXfzzEhsM5fDF3ON5mIz/+fiSBfpZL6WZ1nahYO770VrCXapm7jGa4+b8Q3Lvi2MtnaOVW6Aj2ROCkp4MvWwyVnc5BPTWhwuDeTIvLoFcnNfKEZm4QvL29ycvLIygoqNGNQlNFSkleXl61K5KklGxPy+fybgGYjAbC2/tSGBZAmd2Jt9noOWMQ/xYkvg+/26IZhYG3aX6BcvrcdP45zcwRXNNFuPINy4mzJ8grzaNfUD8A4rPiOV50nMk9JgPw8raXWXZgGVJKLEYLE7tPxImTZ4c9C8BbO9/iVMkpnhmqqYK+seMNim3FPDlEEwp+LfE1ss5msfboWqwOKwZh4Dd9fsMTg59wt+dr8mVgsOaLOl12Gl+TL2Zj1cUJLdqgVP5e9dW+dw+PCtO+o/YHwOSh73wzpVkbhLCwMDIzM8nJyWnsrjRpvL29CQsLO2/75pQ87ly0lTfuuIybBnVmSmwYU2LPP65OVJYMAFj/oib85hMIbbtAWCxYizQfwMDmm8in8gX+UP4hduXsIjogmtmrZ1PmKEMgeH/C+8SExPBq4qt8fuhzNt2hpU9dtHsRP6T9wIbbNwDw/ZHv+TnzZ7dB2Je3D4crN4DNaSP1dCpBPhWOzrPWs1WCKc/azlJkrfBHFdmKSC9Mx+qw4sSJUzrZdGyT2yD8a/u/CPUP5Y0xbwDw2+9/S0TbCOaP1nxY836aR6BXID+k/YDVYcVoMPLMFc8wpecUXd7LJsOhH5A//QURFqdNVbbCkWk5zdogmM1mIiMjG7sbzYLyAJzCUjv9OrdlUkwXhkUF8cqtg7i2b8faK7gQh9fAx7dpzjqjl5abtyBDe/gEQt+J2qOJce6dsM1pI7c4lw6+HTAbzOzN3ct3R77jdzG/w9fsy/KDy3lp20tsuH0DfmY/Nh7byPzE+cwdNBerw4p0/W0/sZ2YkBgGdxyMj9HH3d60XtMY022Mu/x43OP8YfAf3OVHYh+pEjj58OUPV7lDf3zw41X6/9SQp6qU/zT0TySdTKpSx3PDn3Pvn3/1fCQV04ez+s+inaUigDDIO4jcktwKg+J0siJ5BVN6TkFKyZRVU5gcPZnpfbWFE2uPrqVP+z6E+odW+342F/50KJr4sn/w2slS+v84rSKwrZ4y282RZm0QFHVje9op7lq8FavdiQSGRrZnUkwXDAZx6SOCU0c0h3BwTzi6uSKJu8OqrRR6MOHC5zcCUkryy/LxNfly4NQB7vnhHqxOKxajhcXjFnOs6BhPbXiKryZ9RVRAFGmFaXxy8BOm9ZpGN3M3egb2ZHrf6e67+MnRk7kx6kaOFR1jyZ4l7ovw4E6DARgRNoIRYRWqqD0De9IzsEIqw99SVfXWE4GTF6qja9uuVY6d2L2qkf7LlX8h6WQS209sx+a0YTKYmD1AS2lpd9rpHdjbHcFfaC3k4XUP82jso8zqP4utx7cy+0ftWC+jFwvHLWRAhwEYDRefZ7whSUzPZ3liBjYZxuqvP6efsQxRLrNdi/RFS0QZhBbOV0nHeHblXqx2J04JRgFX9ahdlqNabKVg9taE4hZfq0UB3/oe9BwPW/6jGQijpWKJaCNzqvQUXxz+gtFdR9M9oDu/nvyV337/W96+5m32n9qP3WXE7A47CdkJjI8Yz7PDniXQW1s5NT5iPNdHXu+eIjo3qDHAW5MtCfEN8VgEvCcCJ+tTR00GxWw08/cRf3cf52vyZfmNy2nvra3z35y12T36sDltfJ/2PfevuZ9XRr3ClV2upNReitVppa2lacWNxKfmYXdoN0qbHb15yGTEJJ3aYodWKHGuDEILJOdMGRajgXa+ZmOmanAAACAASURBVMKD/OjVsQ1JGQXYHeUBOJdgEFY9Asd3wH0/a0v2Ji+AIFd0ctchmrKkznOv505J2J12kk4mEewbTHjbcHJLcpn53UzmDJzDpOhJlNnLeP3X1wnyDqJ7QHeiA6L5w+A/ENEuAj+zHxajxX1XH9cxjrA2YUxtM9Xd3sXc3XoyAr6xqctrMRlM9AmqyNE9uutolu1f5n4/Y4JjsDlshLcNB2DjsY08uv5RPr3xU/oE9SGnOIcz1jNEtItgV86uRptqGhoVhMUVnLbH2Jvk6z+hd0mStkS1ssx2a0FK2SwesbGxUlE7p4rKZJ//+07+47v9VbYnpJ2Sb649LBPSTtWtopR1Ui69TUq7VSvvXC7lz/+U0uHwaH/rgt1hlzuyd8jYD2Nl//f6y5gPYuSO7B3SarfKQe8Pkq8nvu4+7on1T8iNmRullFI6nU55puxMjfXuyN4hF+5aKHdk72iQ19HSudD7eaTgiHw76W1ZZi+TUkq5cNdC2f+9/nJDxgYZ92GcHPjeQBn7YWyjfBZvr0+W4U9+LZdvO1qx0emU8odnpPzmce15MwZIkHW8zqoRQgsg41Qxvx7NZ1KMFk38x+v7MLx71TD8WpPQlJ7WlEJ7TdBy2lqL4VQqnM6E9pENtjIorySP02WniQrQRPymfzudyHaRhLcNx+ZKbelwOkjITnBPb0S20xYWGA1GXh71srsuIcR58/SVaUl39U2BC72fEe0iuG/Qfe7y9ZHXE9YmjAP5B9xO7DJHGduythETEsNZ21n8zA2TW/u6fp0AGNW7UoCoENrooPzRSkYJyiC0AN76OYVVSce5pk9H/LxMTB8afuETKmcQ6zRAMwCFWbDqIRBvatLRvSZoD51/CNtPbCfrbJbbwfno+keRSD6Y8AEAo8JG0cGnA5HtIs+b4gHcDlxF86Kzf2c6+3cm6WQSFqPFvcx1SKg23Th79WzC/MOqGHi9iOjgx32jup+/Y9zftP9CQHo8HN3U4pejClnHKNZLqlyIrsAHQCc0VZoFUsrXhRDPAbOB8gCCP0opv71QXXFxcTIhoemtXGkMjheU8OJ3B3h4bA+iQ/w5WViKU0KndnWQw87YBu9P1FZRSAf0vVnLGwyQvQ9C+njECFSe7x8UPMjtmP0m9Rs2HdvkdlD+36b/Y9OxTaydthaAbVnbMBvNXBZy2QXrVHf2LYdzP1cpJR8f+JhA70AmRE7A7rQz87uZTO87nQmRE6o9pz5Y7U6KrXb8vUxuaZYq7P8GPr0TEK5MbM1rOaoQIlFKGVeXY/UeIdiBx6SUvwoh2gCJQogfXfvmSyn/pXP7LYoyuwMvkxGLycCWlDzG9+tEdIg/IW3rmBdh7d/g4PcVxgAD+FUaJnfs65F+rj26lid/eVKLljUY8DJ68cttv2AxWsgtySXldAo2hw2z0cwjlz/CH6/4o/vc8jvE6lBTPC2Tcz9XIQS/6fMbd7mgrIA2ljZYjFoU8c8ZP/PwuofdEd0Lxy2s1/fil0M53PtBAqseuIoBYdUkdsraAUjqmomtOaOruJ2UMktK+avr+RlgP9BFzzZbKo98soN5S3cA0MHfi81PjeGGgbWk4yzIgK3vVOjBG8zQtrO2NFQYtbudgdPq1S+H08GBUwc4azsLwKqUVTy87uEqwU3d23WnxF4CwMx+M/n0xk/dcglBPkH4mHxqrF+h6ODTgbevfZux3bSsu6vTV+OQDpw4sTltbDm+pc56XdXRs2Mbnr2pL6EBNdxY9Rin5eEuF1tswctRG0ztVAgRAVwGbHVtekAIsUsI8a4QolpvpxBijhAiQQiR0JrkKRLT8/nPusN8uv2o+4se0zWA2PBAd9liquGjKy105ZUFktfAd3+A3MNa+eon4c7l2pB3zDOXNPS1OqxuDR6ApJwkbl11K9uytgEQ1zGOu/rchdloxiiMWIwWnhj8BO28GiilpqLFc2vPW/EyemEURswGMykFKUz7eho2p+2S6sspKqPY6iA9r7j6A8pF8UY/A0Pnokmzt0x09SG4GxHCH/gZeEFK+YUQoiOQizYO+ysQKqW8oGRoa/EhJKbnc+eieMpsWrDMXyb1Y8awiLqdnL0PFo7R1EP7T4ayM5rMdEC3S+6P3WlnzdE1dPbrzMDggeSW5DJ6+Wh3hGqZo4wf039kWOiwKro7ar5foSeVv18nik9wpOAIc2PmAppGVFynuDrlRU9Mz+fOhfFYHU4sJi3PR42r8cqKNKn26DGaRHszoSn5EBBCmIHPgaVSyi8ApJTZlfYvBL7Wux9NHSklm1Py+HpXlltiQgD5Z601n+R0wk/Pa8tCY38Lwb1gyL0VUtJebWpNKFPdhfvzQ5/jZ/ZjfOR4DMLAX7f8lXER4xgYPJAOPh1YPG6xOyjJy+jFjVE3nlevmu9X6ElN36/80nye3vg00/tM57G4x4AL35zEp+ZR5vq9We21pNH08oe7v4O2lyj30gzQ1SAIbWnJYmC/lPLVSttDpZTlGSluAfbo2Y/mgMMp+eOK3XTw93JHTppNBq7qcU7ynLN5cHKvFklpMEBmgpZbALQI4vKlcnWgXAitzFGGURhZMn4JMSExfH74czr4dHAbhGU3LKOzf2f3eRdy/CoUjUmgdyBfTvrSHcOwInkFz216DqBaB3R5pLLV7sRsrEMazfLRdmkhlBbUa/TdFNF7hHAlcBewWwiR5Nr2R+AOIUQM2pRRGnBf9ae3bHZnnuaDLWm8OHkAJqOBxTMH07W9D3uOFRKfmsdY/zR6H30XGA7hw7STVj8DB76FJw5rTuEZX4Hx4j7Gn9J/Ij4rno5+Hd0qnXZpdwd7vXPtO/ibKwK6yuUHFIrmQOXva8KJBJw4AU1jqVyJtpzY8ECWzb7IdLBSwns3aIKO/adq2l0tZNWRrgZBSrmR6j0wF4w5aOlIl6b+sYIS1h08SVreWaJD2hAdol2EY8MDiTUchveng70McMJty6DPDXDVozDsAc0YQJ2MQdLJJD479BnPDX8Os8HMkcIjxGfF839D/6/aYK82FpU9StEyuLXnraxOW+3+jq/LWIdEMmfgHPcxfUPb0qmdN8H+XnWrVAgYME3L/b3uBfil5Uhlq0jlBuRsmZ0HP97B1b2CmTEsguv6dWRUz2B8LJVE1GwlsPMTOLnflVVMu7vh6GbNIAT3rLbuyqQXpvPhvg+5d8C9dPLrRG5JLpuObeJ40XHC24Yzq98s7h1wL4DHVDoViqZIZfXWQcGDWJWyyp0DQkotf8Wm5NwLxyFUh9OqjRCko0XFJiiD0ACcLrbRzteMr8WIySAwuKJ2hRCaMZBSWxHk3Vb7kv3wDPSdpK15Lk/W0XdSzfWXnWbJniWM7jaaQcGDsDlsrEpZxeiuo+nk14nRXUcztttYd7RwZRVP5fxVtHQqf8crS52sz1jPwt0LefLyF3l5ykC6BF5EPEzECO13aS8FYWgxsQnKIOjMf9cns2jDEX5+4mraeJtZMKOa1V+f3Kmll5y5UlsV9LstmrMqbhakbSApMJSEgl3EeVmICYnB4XSwZO8SotpFMabbGMwGM8sOLKOjX0cGBQ+ie0B3Nt6xEbNBC/5q6klKFIrGQCLxNfvSt2NnBoWacUpn3ZdLl8cmfD4bulzeIkYHoAyCLiSfLCLIz0Kgn4WrojtQanW4784BOLEHdi+Ha57X5iN7X6/5CspVFQNdTrGuQ0jyslSsBDIYWXKdthLoi8NfcFWXqxjTbQy+Zl823L4BL6M2ByqEwCzM1fRMoVCUM6bbGMZ0G8PZMjtHThfw6Ma7OHY2E6fTWTdJjK5DtMyAxpbzW1MGwcOcLCxl/Gu/MGdkFH8Y35uBYQEMDAvQAsRsPlrGsRO7YftiuGwGdIiGy6afV8/e3L0czD/IqdJTFSuBnBUrgT6f+HkVyYdyY6BQKC6OLSl5zF76Mz1jJA6nA4nE5rS5f2sXpNwYnMnWVIObuUx2g0lXtEQ0iYlkvt2VxYodmQCEtPXmlWmDuOeqyIoDcw/DK71h7wqt3H8yPHZAMwYuTpedZnXaarc0xTdHvuHl7S8TExyDxWjBKIx4G73dK4GU/o9C4RmcUnJ932huj3zMLYlhEAb3lGutpG+G+f0gZa2+HW0AGkS6whM0NemKcokJq92JEAIvo4Ftf7oGfy+TNvWz9W0w+0LsTK287gVtzXKIFkUspeRwwWG6tumKj8mH5QeX89f4v7oTvOeV5OFl9MLf4q9kIBQKnaj8O7aYDDx/qz+FHOCb1G/o5N+Jt8a+VXW6tzrsZdrve/C9TTJQrUlJV7RECoqt/P3b/RWJ65HMHB6O/5k08IrWho2HfgDvdppBEALG/IlSeykOVyaoxOxEZv0wi9dHv86YbmO4Nvxa+rTvQ0S7CIAqukBqJZBCoQ/xqXnu37HN7iQ3L5R5o0dwV9+7KHOUIYSg0FpIqb2UEN+Q6isxecG1f2nYjuuEmjK6BOxOyf6sQowGQZzhEPPMK7kn5x/wzggtpB3g9mUw7X3sTjugTQmN/HQkyw8uB2BQyCCeG/Ycg4IHAVrI/YDgARiE+kgUioZiaFQQRoNrOXYl6Qpvk7dbofeF+Be445s73BLuNbJrOXzyGy0JVTNFTRnVkWVbj5KUkc/LU7ULeOGJVOSqR/DL2oJR2hFGMwx/GEY8pjmOgbt/uJuubbry/PDnAVi4ayFXhF7BwOCBjfY6FApFVdbsy2blzuNMvTyMkb2Cz9ufUpDCvrx93NT9pporydgGSyZocUQmb5i5qsksRVVTRh7C4ZTuu4dTZ8vIKSiktOAE3gGdaOvjBdlbwWknyctEgo83GQWJnN74FK+Nfg2AIZ2GVJn6mT1wdqO8DoVCUTPX9O3INX071ri/e0B3ugdoOZd3nNzBP7f/k6GhQxkZNrJiKjdtA0iXqoDD1mwjl9X8RA2k5hQxbv7PbE7JBWDuqCiWFD+M95pntAPadWHrDS9wf6dg7u0UwhsBbVlZlEqpvRSn64tx/6D7ubXnrY31EhQKRR04XWJjz7HTlFgdtR67NWsre3L3sHj3Ymavnk3SSZdmZ8QIMHo1+6xqyiCcw+kSLetS5wAfRvik0S1pPgBGo5HTw+byaadITpedBiCnXSd2tQnEZjDgFAIJxHWKU34AhaIZsf3IKW58YyPJJ4tqPdZkMCEQ7vSdP2f8rO0oj1yOnQlhcdBpgM691gd15arEk//bxax31uF0OPA2G3luUCHG1GXk5B4EICNyGH9L+ZQtWVsAmBAxgf9e+w4Wo7c7nV95nIBCoWgeDOzajoUz4ugW5FvrsXEd49xxQQCfHfqMvJI8bWfXIdDnJjiVCqeO6Nll3Wj1TuWUnCIigvwwGgSbNqxl8Pq7cExdgE+fGzhTdJKRX4xjVv+7eejyh5BSklKQQveA7lXWJqs4AYWi9VD+ew/1CyXzTCZzBs6puB44ndoy8yYUsXwxTuVWbRB2ZxTw+ttvMOPKaEZefwfYrcz59FradOjJK9ctBODr1K8Z1GEQXdt29WjbCoWiaXC62EZKbhG9OrbBz+vS1tmcOHuC/NJ8d2pZoEKbrJG5GIPQ6qaM8orKSEg9CUD/Lm0ZEfoFX5x8XdtpsnBVzD1cEXGt+/gbo25UxkChaMFsTzvF5P9uJjXn7CXX8edNf+aRdY9gc9ggey+8OQQytnqwlw1Doy07FUKMB14HjMAiKeVLDdHuh0se57RzDf3mbMPHpw2mYXdzNncnpfZSvE3ezOg3oyG6oVAomggx3QJ4b9ZgwjvU7kOoieeHP09uSS5moxnahUHbztoIoZnRKCMEIYQR+A8wAeiLlmO5rx5t/W/Nm8x9ezgffPciAF36deOrADOpp/YAcGfsgyy8bhHeJm89mlcoFE2c9Lxi9h4v5HB27auMaiLUP5QBwdrKov/s/4hnO3Um6dCXzS5qubFGCEOAZCllKoAQ4hNgErDPk4189fMiXsx4G6uPID57Ke1+DubmUY8wwTEXf4t/7RUoFIoWTWJ6Pr9ZGI/N4cRiNLB09lBiwwMvvb7sRN7Z9Q5SOvlWShbu+IiYO1Y0myC1xvIhdAEyKpUzXduqIISYI4RIEEIk5OTkXHQjiWk/YC93/rvKZqNZGQOFQgFUFbezOpzEp+bVq74dJ3cg0JzJNiFIMBu1qOVmQmMZhOpc7+dNuEkpF0gp46SUccHB52uM1EZsxHWYJRilxCy1skKhUJQzNCoIi8mAQYClkrjdpRLXMQ6LwYxRSkxSkmk2Y+s2zEO91Z/GmjLKBCov3QkDjnu6kUmj7gW0kUFs5HXuskKhUADEhgeybPZQ4lPzGBoVVK/pItCk6hde9y4JB1dgzD/Ka4ZExplguIf6qzeNEocghDABh4CxwDFgO/AbKeXems5pbLVThULRMskrKmNfViExXQNo4+3B/MhSciRzC5Fhwxo1HqHJxyFIKe3AA8APwH5g+YWMgUKhUOjFjqMF3LV4G2m5xZ6tePsiIhdPgKJs9uXtY29u07/EtepIZYVCoSgotpJ8sog+oW0vOVK5WnKTIW0Djj4TmfLjPfhb/Plwwoe1p+T0MCofgkKhUNSRAF8LcRHtPV9xh2joEI0RePXqVwnwDmhwY3CxKIOgUChaNTlnyth9rIC4iPa09aQPAeBsLhRlE9WxHwBSSj7a9xFlzrImKYbZ6rSMFAqFojI7Mwq4+70E0j3tQwBY9TAsn+ku/jfpv7yc8DL//vXfVRPsNBGUQVAoFK2awZHt+WrelXQP8fN85Vc+AkPmwIZXIGMbZoMZgUAisTltJGQ3Lb+omjJSKBStmnY+ZgZ1DdCpdgk//hkcVjBaGHLzq3gZvbA5bZgMpiaXUEuNEBQKRatmzb5sHvlkB78cvHh5nFpJXQ/2MpAOcFiJyc9i4biF3NbrNnxNvu78600FNUJQKBStlsT0fOYuTcTmkHy75wQf11Pc7jxCL0NTUhNgtEDECGJCYugZ2JOss1n4mHw815YHUAZBoVC0WuJT83A4tVgsh0vczqMGIXosjH8Jik5Crwlu1VNfsy//HvNvz7XjIZRBUCgUrZZycTub3YnZVH9xu/MwGGDo3Bp3l9pLWbxnMZO6TyKsTZhn274ElA9BoVC0WmLDA3nj9suZMCCUd6bHenZ0UM6xRDgaX+2ugrICPtj7Aesz1nu+3UtAjRAUCkWrRgj4elcW943srk8Da54DuxXu+eG8XZ38OrHqllWE+Ibo0/ZFogyCQqFo1QztHsSaR0cRFqiTg3fCP8FYcwR0uTFYk76GQ/mHGN55eKNFMCuDoFAoWjX+XiaiQ3TMohjSu9ZDfsn8hd+v/z0AS/YsYeG4hY1iFJQPQaFQtGqOF5TweWIm+Wet+jSQnwa7/6dNG9XAofxDCFciycaMYFYGQaFQtGr2HS/ksc92kplfok8Dqevh83vgbM2Bb3Ed4/AyemEURswGc6NFMKspI4VC0aoZHh3EL0+MpmM7L30a6DMRLP6QtAyiRrljESoTExLDwnEL+enoT2ScyaB7gE4O7lrQbYQghPinEOKAEGKXEGKFECLAtT1CCFEihEhyPd7Wqw8KhUJRG74WE92CfPEyGfVpIC8ZvnoA1r8I70+EjG3VHhYTEsOEyAlsOraJ3bm79elLLeg5ZfQj0F9KORAtf/LTlfalSCljXI/7deyDQqFQXJDM/GI+3naUvKIyfRpI/hHspW49I9I21Hho36C+/DTtJ4Z3Hq5PX2pBN4MgpVztyp0MEA80fhieQqFQnMOBrDM8/cVujheU6tNAh56ABAxuPaML0dbSFoBimw75GWqhoXwIdwOfVipHCiF2AIXAn6SU1ZpMIcQcYA5At27ddO+kQqFoffh5GZk7qjvFVnvtB18KfSbBrSY4eUDTNqrGh3AuryW+xpqja/hq0lcYDTpNZVVDvQyCEGIN0KmaXc9IKb9yHfMMYAeWuvZlAd2klHlCiFjgSyFEPyll4bmVSCkXAAsA4uLiZH36qlAoFOeSmJ7PrPe2Y7U7WbL5CEvv9bDaKYDJAv1ugX51P+XyjpdjMVqwOW3NxyBIKa+50H4hxEzgRmCslFK6zikDylzPE4UQKUBPoGmlDlIoFC2e+NQ8rHYnTgk2uw5qpwAl+bBvJUSOhPaRdTplZNhIRoaN9Gw/6oCeq4zGA08CE6WUxZW2BwshjK7nUUAPIFWvfigUCkVNDI0KwmjQAsKMRh3UTkGTvl71EBz/9aJPXX5gOS/Ev9BguZeF68bd8xULkQx4AXmuTfFSyvuFEFOAv6BNIzmAZ6WUq2qrLy4uTiYkqEGEQqHwLPGpuWw4nMuonsEMidTBIDhsmlHwCQSLb51PS8hOYNb3swDwNnpfspyFECJRSlmnSDfdnMpSyugatn8OfK5XuwqFQnExDI3qwNCoDvo1YDRDuy4XfVrSySQMGHDidMtZ6K1vpKQrFApFqyY97yyLNqSSc0anOISSfNi2EPJSLuq0uI5xWIyWBpWzUNIVCoWiVXM4u4i/fbOfKyKDCG6jg3xF0Un49nGY+i4E1V2SolzOIiE7gbiOcQ2ifqqbD8HTKB+CQqHQA5vDSanNga/F5HYwexSHXRsleLUBs7fn66+Fi/EhqCkjhULRqjEbDbTxNutjDACMJsg/AvH/qVHHqKmgpowUCkWr5kjuWb7bk8XU2DBC2uhwB394DXx8u6ZlZPSCmSvrFK3cGKgRgkKhaNWknCzi5e8Pkn1aJ6dyyk/gtIF01ipu19gog6BQKFo1o3uHcOCv4+nXua0+DfSZCCZvEMY6ids1JmrKSKFQtGqMBqGvXlD4MJi5ShsZRIxostNFoEYICoWilZOaU8Traw6TXaiT/PXZPEjfBL1vbNLGAJRBUCgUrZzUnLPMX3OIk4U6+RCKc2HNc5C9R5/6PYiaMlIoFK2aQF8zj4/ridXu0KeBoB7wzAkwmPWp34Mog6BQKFotien53Ll4K1a7E4vJoE8+BIMBDD6erVMn1JSRQqFotVSXD8HjnM2Fn1+Gk/s9X7eHUQZBoVC0WhokH0JxHqx7AU7u83zdHkZpGSkUilZNYno+8al5DI0K8vx0EYCUWlAaQps+amCaRD4EhUKhaA7EhgfqYwjKEUILSmsGqCkjhULRqjmcfYa/f7ufrNMl+jRQlAM//RWy9+pTvwfRM6fyc0KIY0KIJNfj+kr7nhZCJAshDgohrtOrDwqFQlEbmfklfLglnbwiqz4NlJyCjfMh56A+9XsQPXMqPwcUSSn/dc72vsDHwBCgM7AG6CmlvOAiYOVDUCgUzZaMbY0mXdHUfQiTgE+klGXAESFEMppx2NIIfVEoFAp9ydgG70/UlE6NllYtf/2AEGKXEOJdIUS516YLkFHpmEzXtvMQQswRQiQIIRJycnJ07qpCoWiNHDxxhudW7uVYgU4+hEPfg71Uy4fQkuWvhRBrhBB7qnlMAt4CugMxQBbwSvlp1VRV7byVlHKBlDJOShkXHBxcn64qFApFtWSdLmHFjmMUFOvkQwjpi3aJM7Rs+Wsp5TV1OU4IsRD42lXMBLpW2h0GHK9PPxQKheJSubpXCDufHadfAwOmQkC3ZiF/rZsPQQgRKqXMchVvAcql/lYCy4QQr6I5lXsATTvRqEKhUNSHrkOatCEoR08fwstCiN1CiF3AaOD3AFLKvcByYB/wPTCvthVGCoVCoRf7swr544rdZOYX69PAmWz47kk4nqRP/R5EN4MgpbxLSjlASjlQSjmx0mgBKeULUsruUspeUsrv9OqDQqFQ1EbOmTJW782msMQOwI8//khUVBRCCI88ekV2Jn/9W0weO7hOx0dFRfHjjz82ynuhtIwUCkWr5lwto6ioKI4cOdKofYqMjCQ1NdUjdTX1OASFQqFoEiSm53Pnovgq+RAa2xgAjdYHpWWkUChaLZXzIVh1yofQyV/w3xu8iQ1t+pfbpt9DhUKh0ImhUUGYXJLU5hryIUgp6/XIOnKIuaO6kPDTVxc8rimgfAgKhaJVc64PQYiqsbMNdY3Uq13lQ1AoFIo6ons+hGaEmjJSKBStmj3HTvPwJzvIOKVTHEJhFnw1DzIT9anfg6gRgkKhaNUUltjYmVFAsVWn+FhbMRz8Hhw2kPc26YhlNUJQKBStmuHRHVj/xGh6dWqjTwPFeWA9C7v/p8lgZzRdpR5lEBQKhUJP0jZostctXf5aoVAomju7Mgv43dJE0vPO6tNAcG/tv2jh8tcKhULR3Ckqs3M4u4gyu1OfBoJ7g38IRF0NcXc3aR+CikNQKBSKSrTmOAQ1ZaRQKBQKQBkEhULRyknKKODe97eTlquTD+H0MVg+E45u1ad+D6IMgkKhaNWU2RxknS7F5tDJh+Aog5P7oaxQn/o9iG4+BCHEp0AvVzEAKJBSxgghIoD9wEHXvngp5f211ad8CAqFoiFozT4E3VYZSSlvq9ShV4DTlXanSClj9GpboVAo6sq54natGd2XnQrN7E0DxujdlkKhUFwMien53LFQS5DjZTKwbPZQzzdyOhO+fQKGPwjhwz1fvwdpCB/CCCBbSnm40rZIIcQOIcTPQogaozSEEHOEEAlCiIScnBz9e6pQKFoV8al52F2+A5tDnwQ5OGxwOkPTNGri1GuEIIRYA3SqZtczUsqvXM/vAD6utC8L6CalzBNCxAJfCiH6SSnP87hIKRcAC0DzIdSnrwqFQnEuQ6OCsJgM2OxOzKbqE+TUm/aRcP9Gz9erA/UyCFLKay60XwhhAiYDsZXOKQPKXM8ThRApQE9AeYwVCkWDEhseyNJ7hyofggu9p4yuAQ5IKTPLNwghgoUQRtfzKKAHkKpzPxQKhaIGJBsP5xLoa9an+oIM+GgKpDX9UYLeTuXbqTpdBDAS+IsQwg44gPullKd07odCoVBUi5TgcEp0m5OWDs0oJH2sidspLaP6o+IQFApFQ+DxeICMbVoeBIdVMwgzV1ZrFJpCHIKKVFYoFAo9xL6+ZAAACstJREFUUfkQFAqFonmwPe0UU97aTPLJIn0aCOoBSJUPQaFQKJo6BiHwMRsxiNqPvSRCB0FIX+h8GVw+Q/kQPIHyISgUioagNWsZqSkjhUKhUADKICgUilbO1tQ8Jr65keSTZ/RpID8dFl8Hqev1qd+DKIOgUChaNRaTgSA/C0aDTpdDIcDkBVo8bpNG+RAUCoWiEsqHoFAoFIpWjzIICoWiVbMlJY/xr/3C4Wy9fAhpsGA0pKzVp34PogyCQqFo1Rw9dRaHU3JIL4MgjOAbBEYvfer3ICowTaFQtFoS0/N5duVerHYnj322k07tfDzfSEBXmP4/z9erA2qEoFAoWi3xqXlY7U6cEmx2nTKmNSOUQVAoFK2WoVFBmFzLTU1GnTKmnToCb10Jh9d4vm4PowyCQqFotcSGB/LCzf3p3akN86fF6JMxzWiGwAiw+Hm+bg+j4hAUCoWiEioOoX6N3SqE2CuEcAoh4s7Z97QQIlkIcVAIcV2l7eNd25KFEE/Vtw8KhULRpMnYBhte0f43YTwxZbQHmAz8UnmjEKIvWgrNfsB44L9CCKMrn/J/gAlAX+AO17EKhULR4GxKzmXEy2s5cKJQnwb2roDF4+Cnv2mZ05qwUai3QZBS7pdSHqxm1yTgEyllmZTyCJAMDHE9kqWUqVJKK/CJ61iFQqFocNr5mBkc3h4/i06r8I8nARJwtuqMaV2AjErlTNe2mrYrFApFg9O/SztevS2Gru19q90vhKjXY9jdL1Bsk9gckmKrnWG/ebLa45oCdTKJQog1QKdqdj0jpfyqptOq2Sap3ghV6z0RQswB5gB069atDj1VKBSKpkV8poOxHxRzdYSR9WkO4jMdjd2lGqnTCEFKeY2Usn81j5qMAWh3/l0rlcOA4xfYXl27C6SUcVLKuODg4Lp0VaFQKC6KDYdzuOLva9ifpY8PoXug4KNbfNid7ayzMYiMjNSlL7Wh55TRSuB2IYSXECIS6AFsA7YDPYQQkUIIC5rjeaWO/VAoFIoaCfLzYnSvENp46+NDKLHD5gw7ucV1W0YaGRnJO++8o0tfaqPe74AQ4hbgDSAY+EYIkSSlvE5KuVcIsRzYB9iBeVJKh+ucB4AfACPwrpRyb337oVAoFJdC385teWnKQHdZr7iDu3Sp1bOowDSF4v/bu98YOeo6juPvj9feVdvaQottpZe2R0mkooGWXGoUHkBFqZiKYtLEhD7QkKgk+MAHJQ2KkcRIAg9MjEQjBhAtyp9wEUmt0Coxodj/FM7SK7baa0NbbCtGgwhfH8zvcN3c3l5vdzo7c59XcpnZ38ztfL/328339jezvzGrMN8gx8xsnH738gmWf3szLx3N6XsIrx2Euy+BP/06n+dvIxcEM5vU5r23h099aAGz3jM1nwN0T4el18CMefk8fxt5yMjMrMI8ZGRmZmfNBcHMJrUt+49z6Tc3sW/4TD4HODkEd10Eg7/K5/nbyAXBzCa1M/98kw/Mn8nw6X/lc4CeGbBsDczq/Bl6XBDMbNLacfgU6x/by86/nOLWjbvYcfhU+w8ycz5cfw+8//L2P3ebuSCY2aTleyr/PxcEM5u0VvbNoXvKu+gSTJ2S0z2VTx6A7/TCS50/Q09OE4CbmXW+FYvO46EvreS5V15jZd+cfO6pPG0WLF2V3Qdh5nzo7W//MdrEBcHMJrUVi87LpxCMOHUI9j+V3Rxn54OwbqBji4KHjMzM8nTo2awYxFuT+o5pZma2+Ero6gZ1ZcvFVxYdUUMeMjIzy1NvfzZMdOjZrBh06HARuCCYmeWvt7+jC8EIDxmZmRnggmBmZklLBUHS5yW9KOltSVfUtH9c0g5JL6Tl1TXbtkraL2l3+nlfKzGYmVl7tHoOYR/wWaD+jtAngU9HxFFJl5LdP7l2ZqcvRIRvbmBm1kFaKggRMQggqb59V83DF4Fpknoi4o1WjmdmZvk5F+cQPgfsqisGP0nDRbervprUkHSzpO2Stp84cSL/SM3MJrGmt9CU9Ftg/iibNkTEE2mfrcDX64eBJH0QGACujYiDqe3CiBiWNBN4FPhpRDzQNFDpBHC4eUqjmks2jFUVVcsHqpdT1fKB6uVUtXxg9JwWRcQF4/nlpkNGEbFqIlFJWgg8Dtw0UgzS8w2n5euSfgb0A00LwngTahDL9vHeU7QMqpYPVC+nquUD1cupavlA6znlMmQkaTbwJHBbRPyhpn2KpLlpfSpwPdmJaTMzK1irl53eIOkI8BHgSUmb0qZbgKXA7XWXl/YAmyTtBXYDw8CPWonBzMzao9WrjB4nGxaqb78TuLPBr61o5ZgT9MMCjpmnquUD1cupavlA9XKqWj7QYk5NTyqbmdnk4KkrzMwMcEEwM7Ok0gVB0ifTvElDktYXHc9ESTqU5oXaLWl7ajtf0mZJB9Iyx3sAtk7SfZKOS9pX0zZqDsp8L/XbXknLi4t8dA3yuUPScM2FFKtrtt2W8tkv6RPFRN2YpF5JWyQNpvnJbk3tZe6jRjmVsp8kTZP0vKQ9KZ9vpfYlkralPnpYUndq70mPh9L2xU0PEhGV/AG6gINAH9AN7AGWFR3XBHM5BMyta7sLWJ/W1wPfLTrOJjlcBSwH9jXLAVgNPAUIWAlsKzr+ceZzB9kXNOv3XZZefz3AkvS67Co6h7oYFwDL0/pM4OUUd5n7qFFOpeyn9LeekdanAtvS3/4XwNrUfi/w5bT+FeDetL4WeLjZMar8CaEfGIqIVyLi38BGYE3BMbXTGuD+tH4/8JkCY2kqIn4P/K2uuVEOa4AHIvMcMFvSgnMT6fg0yKeRNcDGiHgjIv4MDJG9PjtGRByLiJ1p/XVgkGxCyjL3UaOcGunofkp/63+kh1PTTwBXA4+k9vo+Gum7R4BrxpoqCKo9ZHQh8Neax0cY+8XQyQL4jbKpxG9ObfMi4hhkL3ygjNOIN8qhzH13SxpCua9mGK9U+aShhcvJ/gOtRB/V5QQl7SdJXZJ2A8eBzWSfYk5HxH/SLrUxv5NP2n4GmDPW81e5IIxWCct6je1HI2I5cB3wVUlXFR1Qzsradz8ALgIuA44Bd6f20uQjaQbZHGNfi4i/j7XrKG1lyam0/RQRb0XEZcBCsk8vl4y2W1qedT5VLghHgN6axwuBowXF0pKIOJqWx8m+CNgPvDryET0tjxcX4YQ1yqGUfRcRr6Y37Ntk38AfGW4oRT5pOplHgYci4rHUXOo+Gi2nsvcTQEScBraSnUOYLWnkS8a1Mb+TT9o+iybDnFUuCH8ELk5n4LvJTqoMFBzTWZM0XdnMsEiaDlxLNv/TALAu7bYOeKKYCFvSKIcB4KZ0JctK4MzIsEUnqxtDv4H/zdM1AKxNV30sAS4Gnj/X8Y0ljS3/GBiMiHtqNpW2jxrlVNZ+knSBsnnikPRuYBXZeZEtwI1pt/o+Gum7G4FnIp1hbqjoM+c5n5VfTXZlwUGy6boLj2kCOfSRXfmwh+xmQxtS+xzgaeBAWp5fdKxN8vg52cfzN8n+c/lioxzIPup+P/XbC8AVRcc/znweTPHuTW/GBTX7b0j57AeuKzr+UfL5GNlwwsg8Y7vT+6fMfdQop1L2E/BhYFeKex/wjdTeR1a4hoBfAj2pfVp6PJS29zU7hqeuMDMzoNpDRmZmdhZcEMzMDHBBMDOzxAXBzMwAFwQzM0tcEMzMDHBBMDOz5L+dn76j2aMacgAAAABJRU5ErkJggg==\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": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAD4CAYAAAD2FnFTAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nOzdd3xUVfr48c+ZyUwaAZIQIJCQEHoPJDSVziKiggXXAogIiq513WL97bpVd/0iunZFAQULLouiIkWxgBIggdCLEBISCARCqClT7vn9cSeTBELNhJnA8369MHPv3HvnzJjcZ057jtJaI4QQQpzM4u8CCCGECEwSIIQQQlRLAoQQQohqSYAQQghRLQkQQgghqhXk7wKci0aNGunExER/F0MIIeqUjIyMg1rrmAs9v04EiMTERNLT0/1dDCGEqFOUUjk1OV+amIQQQlRLAoQQQohqSYAQQghRrTrRB1Edp9NJXl4epaWl/i5KwAsJCSEuLg6bzebvoggh6pA6GyDy8vKIiIggMTERpZS/ixOwtNYUFhaSl5dHy5Yt/V0cIUQdUmebmEpLS4mOjpbgcBZKKaKjo6WmJYQ4b3U2QAASHM6RfE5CBI4PV+5m3Lsr+XDlbn8X5azqbBOTEELUNR+u3M1T8zYAsOyXgwDc0buFP4t0RnW6BiGEEHXJi99sO+N2oJEAUQMlJSUMGDAAt9tNZmYmffv2pVOnTnTt2pVPPvnEe9zEiRPp1q0bXbt2ZfTo0Rw/fvyM1121ahXJyckkJyfTrVs35s2bB0Bubi6DBg2iQ4cOdOrUiZdfftl7zu9//3uWLl1aO29UCOETR4qdZ9wONJdVgMjIKeK173aQkVPkk+u999573HTTTVitVsLCwnj//ffZtGkTCxcu5NFHH+Xw4cMATJ06lXXr1rF+/XpatGjBq6++esbrdu7cmfT0dDIzM1m4cCGTJ0/G5XIRFBTElClT2LJlC2lpabz22mts3rwZgIceeojnn3/eJ+9LCFE7gq2WM24HmsAu3Xm49a0VfJqeC4DTbXDrWyuYtzYPgBKHmxEv/8htb69gyuJtjHknjREv/8jCjfkAHDrh4Na3VvDN5v0AFBw7txE/s2fPZtSoUQC0bduWNm3aANCsWTMaN27MgQMHAKhfvz5gDjktKSk5a6dxWFgYQUFm91Bpaan3+NjYWHr06AFAREQEHTp0YM+ePQAkJCRQWFjIvn37zqnsQoiLr2nD0DNuB5pLJkCczdFSFy63xtBmADla6qrR9RwOB1lZWVSXZXbVqlU4HA5atWrl3TdhwgSaNm3K1q1beeihh856/ZUrV9KpUye6dOnCm2++6Q0Y5bKzs1m7di29e/f27uvRowc//fTThb8pIUSt6tys/hm3A80lEyA+mdyXW1LjAbBZLXwyuS83do8DINRu5eXbuhNss2BVYAuy8PJt3RneORaAqHA7n0zuy9COTQBoHBFy1tc7ePAgDRs2PGV/fn4+48aNY/r06VgsFR/v9OnT2bt3Lx06dKjSP3E6vXv3ZtOmTaxevZrnnnuuyjyG48ePc/PNN/PSSy95aycAjRs3Zu/evWe9thDCPwpPOM64HWh8EiCUUtlKqQ1KqUylVLpnX5RSaolS6hfPz0jPfqWU+o9SaodSar1SqocvynA2KQmRzJ7Uh8eGtWP2pD6kJETW6HqhoaGnTD47evQo1157LX//+9/p06fPKedYrVZuvfVW5s6de86v06FDB8LDw9m4cSNgphi5+eabGTNmDDfddFOVY0tLSwkNDewqqxCXs+hw+xm3A40vaxCDtNbJWutUz/YTwLda6zbAt55tgGuANp5/9wJv+LAMZ5SSEMkDg1rXODgAREZG4na7vUHC4XBw4403cuedd3LLLbd4j9Nas2PHDu/jL774gvbt2wMwb948nnzyyVOuvWvXLlwuswksJyeHbdu2kZiYiNaaiRMn0qFDBx577LFTztu+fTudO3eu8XsTQtSOy7IGcRqjgJmexzOBGyrtf1+b0oCGSqnYWixHrRk2bBjLly8HYM6cOfz444/MmDHDO0Q1MzMTrTXjx4+nS5cudOnShfz8fP70pz8BsHPnzipNROWWL19Ot27dSE5O5sYbb+T111+nUaNG/PTTT3zwwQcsXbrU+xoLFiwAzJrFjh07SE1NPeV6QojAUNdqEL6aSa2BxUopDbyltX4baKK1zgfQWucrpRp7jm0O5FY6N8+zL7/yBZVS92LWMGjRIjBnGj744IO8+OKLDB06lLFjxzJ27Nhqjztdx3FmZiZTp049Zf+4ceMYN27cKfuvuuoqtNbVXuvLL79k9OjRp3RmCyECR12rQfjqbnKl1nqvJwgsUUptPcOx1Y3xPOWu5wkybwOkpqZWf1f0s+7duzNo0CDcbjdWq/W8z581a5bPyuJyufjd737ns+sJIXzvsqxBaK33en4WKKXmAb2A/UqpWE/tIRYo8ByeB8RXOj0OqLNDb+6++25/FwGgSr+HECIw1bUaRI37IJRS4UqpiPLHwDBgIzAfGO85bDzwuefxfOBOz2imPsCR8qYoIYS4lF3TOfaM24HGFzWIJsA8z2zfIOBDrfVCpdRqYI5SaiKwGyj/irsAGAHsAIqBCT4ogxBCCB+rcYDQWmcB3arZXwgMqWa/Bh6o6esKIURd8/XG/FO2Jd23EEIIOsVWHdYe6E1MEiBqoLbSfS9ZsoSUlBS6dOlCSkpKlTTew4cPp1u3bnTq1In77rsPt9sNSLpvIQJdRk4R7/20y7tttSjaNY3wY4nO7vIKELmrYNkU86cP1Fa670aNGvHFF1+wYcMGZs6cWWVOxJw5c1i3bh0bN27kwIEDfPrpp4Ck+xYi0P288yBOd8WIfcPQpGUV+rFEZ3fpBIjp18La2eZjt9PcXuf5Fu8ohjf7wYwRsPQfMPN6c3vzfPP5E4Xm8du+NreP7T+nl6ytdN/du3enWbNmAHTq1InS0lLKysqqXMvlcuFwOLzXknTfQgSujJwivttaUGXCl9Wq6JMU7bcynYtLJ0CcTekRcLtAu80AUnqkRper7XTf5ebOnUv37t0JDg727rv66qtp3LgxERERjB492rtf0n0LEXgycooYMy2NzNzDWACL5/uh5SxfFAPBpRMgJnwF3ceYj602c7vbrea2PQxungZBIaCsYLWb2x1Hms+HR5vHt7vG3I5octaXq+103wCbNm3i8ccf56233qqyf9GiReTn51NWVlal30HSfQsReJb/cgCHy8DQZsoIw1ONcLsNaWIKGPG9YPx8GPy0+TO+V40uV9vpvvPy8rjxxht5//33q9REyoWEhDBy5Eg+//xz7z5J9y1EYDEMzU87DgKYa9FYFUGeKkSQ1RLwTUyXV2a3+F41DgzlKqf7DgkJOWO67507d9K6detq032vWrWK5557rsq1Dx8+zLXXXstzzz3HlVde6d1//Phxjh07RmxsLC6XiwULFtCvXz/v89u3b5eUG0IEiIycItKyDtKuaX06N29AdL1g+iRFs23fMb7emM81nWN9svRAbbq8AoSPlaf7Hjp0qDfdd2FhITNmzABgxowZdO3alfHjx3P06FG01nTr1o033jCXwDhduu9XX32VHTt28Le//Y2//e1vACxevBitNSNHjqSsrAy3283gwYO57777AEn3LUQgycgp4o530nC6DexBFu8iZRk5Rfz1y004XAarsw/RrmlEQAcJCRA1UFvpvp955hmeeeaZas9ZvXp1tfsl3bcQgeP173dQ5jIAcLrMvoaUhEjSsgq9/RGV9weqy6cPohZUTvd9IWbNmkVMTIxPyiLpvoUIHCO7NsNqUWa/Q1BFX0OfpGis0gdx+ZB030IIgNXZh/hkdS6392rBqO7NiYsKIy2rkD5J0d5aQkpCJH8Z2Vn6IIQQ4nKRkVPE7W+n4TI089ft5aN7zD6HkwNAXeuDkCYmIYSoobSsQgzPcsBnmt9QXR9EIJMAIYQQF2h19iHufG8V3eMbYg+ynNLncLI+SdEyD0IIIS5V5vwGs2/heKmLvYdLiI8KY/akPqf0OZwsJSGSZ+tQH4TUIGqgttJ9O51Oxo8fT5cuXejQoYN3Ip3D4aB///64XK5afV9CiOqV51WasngbY6alUT/UxteP9CM+KoyUhEgeGNT6jDf98j6In3Yc5K9fbiIjp+gilv78XVYBIrMgk2kbppFZkOmT69VWuu9PP/2UsrIyNmzYQEZGBm+99RbZ2dnY7XaGDBlyzrmchBC+lZZVSKmzah+CzXrut1Hpg/CTCQsn8NmOzwBwGk4mLJzAFzu/AKDEVcIt82/h7kV388qaV5i0eBK3zL+Fb3K+AaCotIgJCyfwfe73ABwsOXhOr1lb6b6VUpw4cQKXy0VJSQl2u917jRtuuIHZs2ef68cihPABrTWlTjd9kqIJDrJgOUtfw+mYfRDmbVf6IALIMecxXIYLjcZpODnmPFaj611Iuu8FCxbQsWNHpkyZcsZrjx49ms8//5zY2FiKi4uZOnUqUVFRAHTu3Pm0s6mFEL6ntebhjzNxGwav3dGDD+85e1/D6aQkRPLRvRd+/sV2yQSI6cOnex/bLLYq26FBoTzf73nuWXwPTsOJzWLj+X7Pk9w4GYDIkMgqxzcKbXTW1ztbuu+ZM2eeku7b7Xbz0EMP8cknnzBhwoTTXnvVqlVYrVb27t1LUVER/fr1Y+jQoSQlJWG1WrHb7Rw7doyIiMBerlCIuqxyZ3S3uAbeYazVzW+4VPksQCilrEA6sEdrfZ1SqiXwMRAFrAHGaa0dSqlg4H0gBSgEbtVaZ/uqHKeT3DiZd4a9Q/r+dFKbpHqDw4WqSbrvF1544YwB4sMPP2T48OHYbDYaN27MlVdeSXp6OklJSQCUlZUREhJSo/ILIU5vxc6DjHt3FYbWVZLt1VT5hDqH2yA4yMKH9/jmurXFl30QjwBbKm3/C5iqtW4DFAETPfsnAkVa69bAVM9xF0Vy42QmdZlU4+AAVdN9A2dM971jxw7v45PTfT/55JOnXLtFixYsXboUrTUnTpwgLS3Ne05hYSExMTHYbLYavwchRPVWZh3CZWifdyanZRXiMswkfq7LZcEgpVQccC0wzbOtgMHAfz2HzARu8Dwe5dnG8/wQdbZe2wBVnu4b8Kb7njFjBsnJySQnJ5OZmYnW2jtktUuXLuTn5/OnP/0JOH267wceeIDjx4/TuXNnevbsyYQJE+jatSsA3333HSNGjLh4b1KIy0SJw827y3fhNjT92sYQcg4T385Xn6Toc5pQFzC01jX+h3mjTwEGAl8CjYAdlZ6PBzZ6Hm8E4io9txNoVM0178Vsskpv0aKFPtnmzZtP2XexrVmzRo8dO/aCzx8zZowuKCg4r3NuvPFGvXXr1vN+rUD4vIQIZF+t36sTHv9SL9t+QGutdXr2If3q0l90evYhn75ObV23OkC6rsG9vcZ9EEqp64ACrXWGUmpg+e7qYtE5PFexQ+u3gbcBUlNTT3k+EFRO9221Ws/7/FmzZp3X8Q6HgxtuuIF27dqd92sJIU71046DLNy4jxu6N+eazk356uGr6NSsAVB7ndFrdhfxwYpsWjeu5/Nr+5ovmpiuBEYqpbIxO6UHAy8BDZVS5QEoDtjreZyHWaPA83wD4NCFvLDW/o8bd9999wUFhwtht9u58847z/u8QPichAg0GTlFjHt3JR+k5TDmnTTW7D7sDQ61+ZovLNzGvqNlPPzR2kt/JrXW+kmtdZzWOhG4DViqtR4DfAeM9hw2Hvjc83i+ZxvP80v1BdzBQkJCKCwslJvfWWitKSwslFFPQnjsPVzCiTJXlQ5i50XqMK5rndS1OQ/iceBjpdTfgbXAu5797wIfKKV2YNYcbruQi8fFxZGXl+edrSxOLyQkhLi4OH8XQwi/Kzxexq9e/IGxfRMY1rEp9iALTpdx0TqMyzupL+Zr1oSqC9/AU1NTdXp6ur+LIYSoo/YdKaVpA7MWPSsthwFtY4iPCqsyGe5izUd464edTFuWxd9v7MLVnZrW6msppTK01qkXev4lM5NaCCEqK7/5HytxMv3nbBY+2p+WjcIZ2yfBe4w/ZkWH2q00CLNjrQOj+yVACCEuOauzDzF22kqcbgOb1cJ1XWNpUj/Y38UiI6eIfy7YgsNl8OBHa3w2Q7u2XDLZXIUQAsAwNI98vJYyT1ptl9sgKaYeYXb/fx+WdN9CCOEHhcfLALBYFNd1bYbNqgJuxnKfpGissuSoEEJcPD/vOMhdM1Yza2JverWM4qkRHbi6U9OAS6udkhDJn6/rxKyVOfz+6nYBU67TkQAhhKiTDENTeMJBTEQw3VtEckevFrSICvM+H6hpucf2TWBs34SzHxgAJEAIIeqcjJwinpi7Hreh+eaxAYTarTw7spO/i3VO/DG09kJJgBBC1Bm5h4rZf7SUse+upMxpEGRVrNldRGpilL+Ldk4ycoq47e0VON36slsPQgghas2GvCMMnvI97/20C4fLQGM2M63cdUGp3PwiLasQt2FOTq4LqTYkQAghAlaJw82W/KMAdGpWn4cGt+Gm7nF1a02FSuraehCSakMIEbDueT+djXuO8MMfBmEPqvg+W5fa8U92McsuqTaEEJcE88Z5kBCbldt7tSDMHsSDg1pT6nRXCQ4QuCOUzsXKrIO88X0WSY3C/V2Us5IAIYTwu4ycIsZMS/POMs49VMyzIzvTLb6hv4vmUxk5Rbz07Q4cLoNHP8mkcf2QgA500gchhPCrHQXHmfHzLm9wsCiICvd/3qTakJZViMtdd9aDkAAhhPCrl7/9hWW/HPR23tqDLFzZupG/i1Ur6lontTQxCSEuqsLjZbz63Q7u7Z9EbINQnh7RAZu1I9mFxXW24/lcpSRE8sDA1rz+/U6m3NIt4N+nBAghxEVV7HDz8apcusY14Mbucd6FfKLrBQf8DdMX+rWNoeBYGb2TAn9ynwxzFULUumnLssg9VMxfRnUG4HCxg4Zhdj+X6tInw1yFEAFp1a5CVmcX0ScpmgPHy9h7pBS3obFa1GUdHOrSHA4JEEIIn/t0dS5/mLsei6fTedbE3qReE/hNKrVNcjEJIS5LDpfBviOlAOQdLgbwrpxWl/Il1SbJxSSEuCzd/k4aD3+8Fq01/ds2JsRWd4ZzXiyX3TBXpVQI8CMQ7Lnef7XWf1ZKtQQ+BqKANcA4rbVDKRUMvA+kAIXArVrr7JqWQwhxcTndBos37WdEl6YopbinX0tCbFbAHM45e1KfOtPWfrHUtc/FFzWIMmCw1robkAwMV0r1Af4FTNVatwGKgIme4ycCRVrr1sBUz3FCiDrm6437eODDNSzfcRCA4Z1jGdiuMUqZay6nJETywKDWAX8TvNjyiop5+dtfaFQv8Dvqa1yD0OY42eOeTZvnnwYGA3d49s8EngXeAEZ5HgP8F3hVKaV0XRhvK8RlKiOniBU7D1LmMuga15BfdWzCiM5NaXB3L666RGc91xaHyyA5viFZB0+QEB3YCft8MopJKWUFMoDWwGvATuCw1trlOSQPaO553BzIBdBau5RSR4Bo4OBJ17wXuBegRYsWviimEOICVE6kpzX0SYriVx2bEGS1MKBtjL+LV6dk5BTx/z7fiMNlcP+sw8yedBmMYtJau7XWyUAc0AvoUN1hnp/qDM9VvubbWutUrXVqTIz8EgrhD99tLeDhj9ZUSaR3VRupMVyotKxC72fpdF1mo5i01oeB74E+QEOlVHkNJQ7Y63mcB8QDeJ5vAMgYOCECRKnTTYnDDUBwkIVQmxWbtfLIGwkQF6pPUjRWi/kdOcga+KOYahwglFIxSqmGnsehwFBgC/AdMNpz2Hjgc8/j+Z5tPM8vlf4HIQLDkWIn/f/9He/9tAuAvq2iWfLYAD68pw+PDWsX8E0igS4lIZLnb+pK36Qo3hqXEvCfZY1zMSmlumJ2QlsxA84crfVflVJJVAxzXQuM1VqXeYbFfgB0x6w53Ka1zjrTa0guJiFqz7FSJxvyjnCFp7P5xcXb6Nc2hp6JMvO5rqtpLiZJ1ifEZe6JueuZv24vK58aQkSIzd/FueQZq6fDls9RHUehUifU6mtJsj4hxHn5YVsBb/ywk7uuSGR451juH9iKO3q3kOBQm3JXQfYyKNiK2jDHHJaT9R2gIPUuPxfu9CRACHGZ0FqzZvdh7v0ggzKXQUZOER/fG9hrItdp5UEhNBoWPg6uUnO4pgalQGsoXvYKYRIghBD+9NcvNnO01EnLRuE4PWsiG4YmLatQAkRtyEmD968HtwssFjAM71Oq0kB/Z+kJPxTu3EmyPiEuUbsOVtx86oUEERESRJ+WUXUqWVydsn4OfP6gWXPI+QncDsAwg4PFQvnttnK3r9Gki1+Keq6kBiHEJWj+ur08/NFavnjwKrrENeCxX7X1PleXksUFpPKmo2YpYA+D+F7mvnmTQRuw4b8w/HkICjZrEFa7uV1SiCrYit4wxwwSCiK7Xuvvd3NGEiCEuAQYhmbp1gKi6tnp0SKSQe1ieOKa9iQ0Cjvl2JSESAkMF0Jr2DgPPv+NWTtQCpQFnthtBoxybgeUFML4L839if3MIALw5aOApw8CYF/mRX8b50MChBCXAJeheeazjfRsGUWPFpFEhNi4b0ArfxerbstdBTuXmjf4xCshczZ8/oAZFLQBWCH5NrDYzGOswWZwsNorgkJ5YPBS3lxDqtJ/A5UECCHqoIycImb+nM3ewyV8el9fc1nPSb1JiD61xiDOk+GGPRkw4zpwl4F1Ctz1FSQNgisfgZVvgdtpBoLu48AaZAaC8fNPrTGcrGm3KonnVNNuF+UtXSgJEELUIUdKnGzNP8r46asocxqg4LttBQxu34TWjev5u3h1T3l/QmI/aNwR3uoPqRPMmoDhSUZtuM1j+v0OfvVXaH9d9YGg2hpDVXvy82iqwarArWFffp43zXUgkgAhRB2RdeA417+ynP5tY8zU25j5bbbkH2Nw+yb+Ll7dk7sK3htuNhcFhZg1gKQBENUKwhuZNYTKTUblziEQnM62o3aaYXZnWDzbEiCEEBdk275j7D1SwqB2jWnZKJyxfRNo36Q+320rwOkyZKjq+Up7A/ZtgBteN2sB2gC0GQiyl8F1UyuOPZcmo/PUrr4DjZk23a0V7eo7fHLd2iIBQogA9tcvN5F/uJSBbWNQSvHkNeZSKy2iw2So6tnkroI178PRfBjzqTkXofQonDhgzk1I7GfWHKqrJUCNagqn0zx5GMbalzAMJ1htNE8e5tPr+5oECCECyJrdRUxZvI03xqZQP8TG32/oQsNQm3ed53IyVPU0Du2CjBnmzf6TseAqNfdv/xraXwsDH6849lw7ln1Mo1C66ozqQCUBQgg/K3W6cbgN6ofYsFst5BWVsLuwmM7NG9CyUWCvWew3lSerHcmFFn2gURs4cRBWvGr+dDswEx9Z4cBWM0CcrBZqCWeUvQyL4TTnQbhd5nu4mK9/niRACOEHGTlFpGUV0j2+Ib+dk8mo5OY8NaIDnZs34LvfDcRiqQNfL/3BMGDNDPj6CXOUkdVm1hKGPgtX/Raa94A/7jIDwsa5p28+8pfQaCpWWDY824FLAoQQF9nXG/J5+OO1uA2NPcjCr1PjGdqhYhSSBIeTHD8Ax/Ihtqu5vegZc34CgBvo+yBcac5QxmKFkPp+az46G3OYq8KqNIZWAT/MVZL1CXGRTVu+C6dbexeub1I/hF4tZfU2clfBsimQswIKd1bsnzMO5j9kPrZYPHmOQsymI6sdOo6qvkE/vpc5dyFAggPACndHXFhxa4ULKyvcHf1dpDOSGoQQtSzrwHGe+N8G/jKyEx1i63P/wFY8MHsNLrcMU/XauRQ+usPTbwDYQuDx3eYs5SF/BltoxbEpd0Lj9gFXOzgXnZvXR60F5Wlm6ty8vp9LdGYSIISoBSUON4eKHTRvGEp0eDBFJxwcOFZGh1gY2qEJH95zmWdULV8fwWKBzI/gs/s9GewMM9dRm6s9cxSAhL6nnn+xO5d9pH3pOrRyowA7Bu1L1wFD/V2s05ImJiF8TGvNja//xFP/2wBAgzAbi3/bn/5tY7zHpCRE8sCg1pdncMhfB1PaQc5yczuuJySPNZuLlNVMetfnfgiy+7ectSGxHwQFo7GY7y9QOs9PQ2oQQvjAzgPHmZ+5l0eHtkEpxSND2hBdL9j7/MnzGC4L3qGo3eGHF6DbbZAy3kxl0bI/2D25oxq1hhteNZuO6mCz0XmJ70Vmpyc4umYunQaOo1GAv08JEEJcIK21mVPHoli96xBv/rCTUcnNSIqpxzVdYv1dPP/65E7Y9pWZdMhqh9huFf0IwfVg9LunnlNHm43OS+4qum18HmV1wPI/QavuAf2ea9zEpJSKV0p9p5TaopTapJR6xLM/Sim1RCn1i+dnpGe/Ukr9Rym1Qym1XinVo6ZlEOJiysgp4t8LtzLo/77ni/V7Abihe3NWPDmEpJjLNKPq2tnw7V8rtgt/MbOgarfZ8dx2GHT9tf/KFyiyl6HcZSgMcDmqLjQUgHzRB+ECfqe17gD0AR5QSnUEngC+1Vq3Ab71bANcA7Tx/LsXeMMHZRCi1h0udvDJ6t2MmZbGmz/sZPehYvKPmKkcQmxWosIvwTbzcuVDUHNXmdt56VUDwr71kPVDxYLL179cdShqgLe1XyxbQ7rh0BYMDQ5tYWvIJb4ehNY6H8j3PD6mlNoCNAdGAQM9h80Evgce9+x/X2utgTSlVEOlVKznOkIErIc+Wsu63MM4XAZGeU5/Q5/9xLoudxXMvB5cZeY6y+O/MDuaV74FvSZDRBO4+p/mJLVyATpRzd827jlKy0qryG3cc5T2Pf1YoLPw6SgmpVQi0B1YCTQpv+l7fjb2HNYcyK10Wp5n38nXulcpla6USj9w4IAviynEOdm89ygPzF7D8TJz4Zg/Xt2eZ0d2wh5kwaq4tOcwOEth+2I4sse8ybs8eY3K02InjzFTWkR4ZoBXDg7lAnCimr/1tW7GihuLAisGfa2b/V2kM/JZJ7VSqh4wF3hUa330DKM2qnvilK9hWuu3gbcBUlNTL4OvaSIQlDrdON0GESE2ylxuVu4qZEfBcZLjG9IlrpDIB6oAACAASURBVAFd4hqQEB1+ac5hOJRl/oxKMlNif3gLDPu7Z2imvWKZzcR+YJelTS9EebpvDCeWoMsk3bdSyoYZHGZrrf/n2b2/vOlIKRULFHj25wHxlU6PA/b6ohxC1MSJMhcD/+97bkmJ44/D29O9RSQ/PzEEe1DVinadT7VdPvw0rhdEJkDDFubN/60B0OlGGPkfaBgPdy2A5inmrObxX0hzkS/E96K0/9OwaR62HndgCfDPssYBQplVhXeBLVrrFys9NR8YDzzv+fl5pf0PKqU+BnoDR6T/QfjL5r1H2bDnMLf2bEF4cBATrkwkNaEiL9LJwaHOq5LSQkOTTnDfcjMr6s3TzJTZ5RKvrHh8OQxBvRhyV2H/8XlwO7B8+2eI6xHQn6svahBXAuOADUqpTM++pzADwxyl1ERgN3CL57kFwAhgB1AMTPBBGYQ4Z1pr78S1j1bt5ov1exmV3JwQm5XfDGzt59L5mOGu6B9Y9LS5mI7bYQ4/VRZo0rXi2LZX+6WIl5XsZVi1A6UMtNt56a8HobVeTvX9CgBDqjleAw/U9HWFOF8ZOUV8tnYP32zZzwcTe9G6cQSPDG3D74e1I8RWTSdrXbfhv/DVY/BwJoRFQeshZlPSmplmmmyrHVLv8ncpLy+J/dDKitIG2hKECvDhvzKTWlzycg8Vs3Z3EX+cux6Hy0wAl55TROvGETSqlA6jzstfbya9G/kfs+8gujV0uqli2c1Wg81/XUZLf4KfZBhteMPxGAPIYJGrL7812pDi70KdgQQIcUlzuQ1ueuNnGoTaqsxfKDzu8HfRaiZ3FfyyGHavhF73QMeRUL85hEaC2xyWS7NkaPbSqedKf4LfpGUVcsgdSr4lmhJtJS2rMKAHPEiAEJecRZv2sWjjPqb8uhtBVgsv/robJQ43D3+8FqerDq/BoDV8/zy4SmDl256+BMNcZrPjSAiPhru+9HcpxRkMqZfNRPs/sOHCiY3sel2AwO33kgAhLgn5R0qIqRdMkNVCwbEyNucf5XCxk8hwO/3amGm2Z0+qg2swbPkSju6B3pPN9RKyl4OzuFJHs2eJTVEnmOtBuFBoLLhkPQghatv6vMNc9a/vWLJ5PwB39GrB14/0I/Kk3Eh1Yg2Ggq2w6p2K7e1fw6q3K3IcjZ8P1/yr0toJkueoTpH1IISoXVprPsvcQ3CQlRFdYunUrAEPD25D1/iGAFgtdWDthfLJarHJUFIEHUaaN4ztC+Hbv5gT1sIbwdXPmesmlGcmsFglz1FdVsfWg1BaB34Wi9TUVJ2enu7vYgg/K3W6vcNRb3z9JyLD7Lx3VwBnOquO1pD5oTn81O00b/huhzlTuWV/KD5kHhNeB/tIxNnlrkLPuM78fx4UjBr/Ra0GeKVUhtY69ULPlxqECFgZOUXePoONe47w6nc7+PEPgwi1W3nnzlSiwupIeu3SI2Ym1HqN4eAv8PlvMKcOaTCA1LshwTNrOSzqDBcSdV72MnA7UGh0+XoQAVyLkD4IEZBWZhVy29srmLJ4G2OmpWGzKq7tEkup0w1Ao3rBWAK1KUlrMyiAmQX1xY6w3DPctFEbGPikmTa7vA+h2+3VZ0MVl5ytId0o00G4tKJUB13660EI4UvlaTCWbNmP0202fzpdBkXFTp4d2cnPpTuDyiktZlwH9nAYM8fsVxj+PDTtbD6nFAx8wpywJn0Il51vjyfyreNp+li2sFJ3YPDxRNr7u1BnIDUIETCe/N96/jx/EwDXdI4N3HUXTl5d7du/wet9K0YaJd8OnW+uOL7HOGjWveo1ZK2Ey1KfpGiaWw/T0ZJNhMUZWL/X1ZAahPCbUqebVbsO0b+tOU+hXnAQNqv5nSUlIZKP7gnAeQu5q2DGtZ5ORk8a7KadzW2306wxdB/r71KKAJVi+YXu9tewGE5GWDOxWAYCgfslQQKE8Jt3l+/i/xZv44ffD6JFdBhPX9uxyvMBs+7C/s2w+BkY/pynk9GTyqI8G2e/35nDUoU4m+xlKG32oynDJZ3UQpTbd6SU+2dlsGrXIQB+nRrPrIm9iY8K9XPJTlJSBAv+CFnfm9vBEXB4Nxzf753oJJPUxAVJ7Idb2XFpC9pqC/jfH6lBiFpV7HBRcLSMxEbhNAi1sXXfMfKPlAAQExFMTEQAZFPVGlZPg3pNzJxG9nqw+TNz6c2kgebqag9Vmocjk9TEhYrvRcm1r+Le8F9Cr7iH4AD//ZEAIWrVbW+nYbNamHv/FYTarXz72AD/DU8tn72c2A8MFxzLNzuTlYKMmRDTzgwQVhs8tuX0Q08lG6qogYgjv0D2Qki91d9FOSsJEMJnMnKKmLcmj71HSnjnzp5YLYrf/qotEcEVv2Z+Cw5bv4JP7zKHo1rtEN8TinLM9RKUMrOghjasOF7mJYjakLsK908vobCg592PtUFcQH/ZkAAhauxIsZNNe49w98zVlDkNNPD1xnyu69qMQe0a+6dQbifkroQWV4DFAmlvetZhxvzZrAfc/nFFjqPKwUGIWrInczFNXE6sysDlcrAnczHNAzhASCe1qJHcQ8X0fu4bpv+cjcNlBgergpzC4otfmKP5UHbcfLxpnjkcNX+tud37vqqdy+2vNSezCXERrXB3xIUVt1Y4sbLC3fHsJ/mRBAhx3uav28snq3cDEBcZyuT+rRjhj4ltbldFQNi/CV5sD9sWmNuth8Kts6BRO3O7w7Uw/ksY/LTZyRzA39rEpatl90FM0yOxKs0Txm9o2X2Qv4t0RpLNVZyTo6VO6ofYAJg0czWHi5389/4rqhxTOblerc1fKE9p4SyFqR0hdaJ50zcMSHvdrBlEtayd1xbCBxZl7uL79Tu5tk9nrmrbtFZfq6bZXH1Sg1BKvaeUKlBKbay0L0optUQp9YvnZ6Rnv1JK/UcptUMptV4p1cMXZRC158OVu+nzz285dMJsw/+/W7oxZ3LfU47z+YI8J6e0+PA2mDvJfGwLgT6/gURPFlSLBa54UIKDCHghhZuI3DaHVmXb/F2Us/JVJ/UM4FXg/Ur7ngC+1Vo/r5R6wrP9OHAN0MbzrzfwhuenCBAnylzMSc9lQNsYkmLq0TMxkjv7Jnqfb3gx0mznroLp15jDUYNCzWahFr3N9Bbl+v++9sshhC/lrqL/z3fT3+aAzz+HhrW7HkRN+aQGobX+ETh00u5RwEzP45nADZX2v69NaUBDpVSsL8ohaqbMZaYAKHG6eW7BVu8Snm2aRPDENe2JCq/lwLB9Ecy83kyRnb3MbE4Cc9RR9jK46rfQ5/7aLYMQtSl7GcqzHoQqT9USwGqzk7qJ1jofwPOzfLxjcyC30nF5nn1VKKXuVUqlK6XSDxw4UIvFFAD3fZDBwx+ZI34a1Qtm6e8HMHlAq9p90QPbYd79cNjz62C4wXGiUkqLEElpIS4tif0wgoIxkFQbp1PdTKlTesq11m8Db4PZSV3bhbrcLNm0n0/Sd3P/QLPPoFfLqCr/E+Iiw3z/oqVHzJQWSQOheQqgYfvX0PXXZjqL9iPMf2BuS0oLcamJ78WcDq+ye80SrhwwiisD/Pe6NgPEfqVUrNY639OEVODZnwfEVzouDthbi+UQHoZhhoC1uYe5f3YGLkOz7JeDfHhPH+6+qhY6dw0DtnpmKLfsD5Yg+OHf5s/mKdCoLfxhp6S0EJeNjJwiZq0t4ipl4bdLi3kjqSgwMhafRm02Mc0Hxnsejwc+r7T/Ts9opj7AkfKmKFF7cg8VM+TFH1i8eR9pWYUYnuHNLrdBWlahj15kFXz9BKx809xWCpb8CVa9Y27bw+F32+DKRyqel5QW4jKSllXIUFbwRNBH9HKv993fXi3xSQ1CKfURMBBopJTKA/4MPA/MUUpNBHYDt3gOXwCMAHYAxcAEX5RBnCoz9zDHSp30axNDs4ahtGsSQUSIjT5J0diDLDhdRs0ntZUdh4LN5uOZI8FVAigzlUV8Lxg3DxrEVRwvKS3EZWxIvWwSrF/i0ooXbG+SU28A0NrfxTotnwQIrfXtp3lqSDXHauABX7yuOJVhaG9CvGfnb8LQmn5tYrBaFG+OS/EeN3vSBa7WpjUU7oToVmYN4PvnzBpCv8cqch0pS8VCKDIvQQiv9qXrMJQLCxoLbtqXrgOG+rtYpyWpNi4h/83IY+D/fU+p0xwe+n+3dGP2pOqnmJzXpDZHsTn0FGDdx/BqChzcbm73GA9jPoWWA8zRRjLqSIjTS+yHstrQKFRQ4I9ikgBRhzndBgs25HPgWBkAzRuG0i2+IUdLnQC0blyPCE96jPNWPgdh30b4d0vYscTcThoI174I4eY60sS0haQBkNDXHHUkuY6EOL34XqgrHzXnQdzwRsD/nUgupjpIa41SiqwDxxk85QeeubYDk/ol+ebijhPwVn/oPg6uetRMm/3Ns5A8BpoEduZJIeqCnQVH+fmXAkZ2b0GDWs5KUNNcTLIeRB2iteaRjzNpGGbjr6M6kxRTj7n39yU5/gKGyVVeXW31uxDSAEb82xxplDQQoj0dZ1YbXP0PX74NIS5reRt+JH/pfIrrj6FBlwH+Ls4ZSYAIcN9vK2Demj3ceUUiKQmRNG0QUmWFtpSEqPO/6MKnKoaiWu3m5LTQSkHm2ik1LLUQolq5q+j30wT625zozz6Dhl8GdDOTBIgAVN7st2b3YSbNTMdlaBZu2seH9/ThqREdzv+Cu36EtbPhhjfMrKcFm0FXynPUpBP0+50P34EQojp7MhfT1O3EojRul5N8WVFOnI8dBce45uVl3rUVLmhCW1E2LPkznPAcf2wf5PwER/eY24OeMjOkyogjIS6qFe6OOLDh0hacBAX8inJSgwgAG/KO4DIMureIpFnDUBqE2nC4jXOf0FZ2HDZ/BnE9IaYdFBfCilfN9Bath0Dnm6HLLRXrL8f3kjxHQvhBy+6DuDP9aXrqzWSoTvxRVpSruUtxFFP5SCTD0Aya8j0tosL4YOKpcxaqXaXNMCBvtbnGcrNkKD4EL7SCQU+bayQYBjiOQ0j9i/yuhBBns2H1D5RmfkpQv0fp3r52Z1HLKKY66P0V2XyyOpcvHrwKi0Xx2h09iI+qPntqiuUXUoKWQVkXyG8KsV3NmsCcO825B7fMgLAoeDAdojxDXS0WCQ5CBKguJ1ZA/kfgHkIgp9kACRAXRanTzcKN+/hVxyaEBwcRUy+YNo3rcazMRYNQG52bNzj1JMMwm40++40nhYWGyER4eK0ZIG7/sCIggJn6QggR2HJXYSybCoaBmncfqn7zgG7ilU7qWlTefLdp7xEe/SSTxZv3AXBNl1heuq07DUJPmuVceqTi8aInYd5kMziUjzhqVSm1VfOUqkNThRCBL3sZuB1YMMxJqJfxinKXLafbYNy7K5n6zS8A9GgRyZzJfRnV7aSF8wzD/Aewfg78K7FidbWut8JVvzMnqikrWIPNhXWE32UWZDJtwzQyCzL9do2anO+L8osLVCkXE7Ki3KWvvBO5VUw4Wpu1A5vVQlxkGDH1zGn0Sil6tTxpQtu+DTDrZrjxLWg1yKwR9P+D+UsD0LyH+a/1YBlt5COZBZmk708ntUkqyY2TT3n+hPMEJa4SGoU2AiD7SDYnXCfoFN0JgNX7VrPhwAbeWPcGDrcDq8XKhE4TeKjHQwDM3T4Xl+Hi1va3AvDexvewKivjO5nLoryU8RIhQSH0ie3DPYvvodRdilVZmTF8BsmNk3n252eJDY9lcrfJADyz/BkSGyQyqcskAJ5c9iTto9rTLaab9/wgSxDTr55OcuNk/pH2D5IbJ3Nt0rUA/GfNf0hunEz/uP4AzNw0k1BrKC+kv+At/9+v+jsjWo5Aa82uo7toFNqI+vZT+6/O9tmJcxTfi71dHqDJulfI6fcirQL8b1pqEDWQnn2IMdPSmLJ4Gw98uJZHP8mk2OEC4LmbujCub2LFwWXHYPoISH/P3I5KMm/65Z3J0a3M+QkRTau+SHwvcxJbgP8i1YaTv+k63U4OlR7yNt3tOb6Hn/f87D1+bcFaZm6a6d3+Kusrnln+jPdaExZO4OU1L3PP4nvILMjknyv/yfC5w73H/yPtH4xdMNa7/Vrmazzx4xPe7dlbZjNz80wcbgcGBk7DyWc7P/M+/+3ub1mUs8i7vf7AetYdWOfdzj+Rz/7i/aTvT8fhSY1uaIP0/eYIvWJXMWXuMu/xp2w7iyl1lVY53224veen708n+2i29/g52+ewZv8a7/aU9CkszFlYpfzzfpkHQJm7jFGfjeLTbZ8CcMxxjO7vd+fDLR+SWZDJpMWTeHnNy9y96G4yCzI57jjOK2tfYeuhrQCUukrJLMjkSFlFM6nUVE6VkVPE4PRetCmZyYglUWTkFPm7SGckNYgLtDr7EHdPX43DZWBosKC568qWhNkrfaRfPwFh0TDgDxAcASENweYZrWQPh9Hv+qfwteRs3zKLncXsPb6XFvVbYLfayTmaQ9reNK5rdR3htnBW5a/iv9v/yzN9nyHrcBYTFk3AZbgItgYzbdg01hSsYWrGVFbesZIwWxhf7vySVzNfZc3YNdisNn7e+zNvrnuTcR3HYVEW9hfvZ1PhJsC8ebo9fTlOw0n6/nR6Nu1JZEhFP84NrW/gyuZXercndZlEiavEu/1076fZeHAjf/zxjzgNJ0GWIP7V71/e518f+nqV9/vSoJeqbP+r/7+8n5PdasdpOLFZbKQ2MUch/rv/v6sc/+LAF6tsvzz45TOeP2/UvCrHL79teZXttDvS2FS4id8c+A1Ow4lVWRnbwQyIVmXl3/3/TdvItt7t8Z3G0yG6A+n703G6zQzB5QEpMiSSaRumkdQgifZR7ck7lse4r8fxwoAXGJ44nPk75vP0T0+jUARbg/lnv3+y/sB6RrcdTUL9BIqdxRSVFdEkrAlBliDv+7rUaylpWYU4XAYasyk6LaswoJcclQBxjtyG5vttBTSpH0Ln5g1IiA6jdZN62Pam05NNZKhO3GbPh28+gaHPmiedKKiYnAbmyKM6zGW4OFhykPr2+oTZwjhYcpDvcr+jf/P+5J/IZ+KiiTgMB3aLnXevfpcTzhM8vuxx3h32Lu2i2rFszzJ+/8Pv+d/I/9Emsg0bD27k7yv/Tq/YXrRs0JKisiI2H9pMsbPYvKEbVW/oVzS7gqd6P4XVs0zp9a2up3dsbyzKrAhP7DyRiZ0nojA/87s7383dne8GILVJKsHW4Co31eTGyfwq4Vfe99crtmotrV1UuyrbMWExDGoxiHeGvVOjG1ly4+QaXeNCzw+zhdGzac9qz7VZbVzT8poqxz6a8igACnVKQEqon8DacWsxtNmHFlsvljeGvkG7SPMz++Ww2f+m0TgNJ8vzlvNl1pcMaTGEhPoJpO9P54FvH2DWiFl0i+nGnG1z+MfKf4AGu9XO1IFTiYuIIz4i3vv/+1LQJymaO2zfM1p9yxv6ZvokXeHvIp2RTJQ7C6fbwGa1UOp00+e5bxnWsQn/Ht3NfDLjfYwvfwvaAKsdS/sRULQLJi015yIEKK016w6sIy0/jQbBDRgUP4im4U0pKi1i+qbpXJ1wNZ0adWL30d08+v2jPJbyGFc1v4pNBzdx21e38crgVxgYP5B1B9YxdsFYXhvyGtuLtvPKmlcwMLBg4aEeDzEofhAfb/2Y8Z3GExcRx74T+8gsyKRvs740CG5AsbOYYlcxkcGRp9wEMgsyuWfxPd6b0jvD3qnxt8rL4RtqbTnfz666/3/dYrqh0ViUhX0n9vHz3p8Z0mIIDYIb8P+W/z9vc51VWekf15/vcr9j8c2Lia0Xy897fuaHvB94pMcjhNnCWJm/kvUH1tOzac+69f8ydxXG9BEow4m2BmO5q3aT9clEuVr01y82sy7vMHPvv4IQm5VPx7Qkaf8SKEsym4y2f41Fm30OGE5o3BFume7TMpzLH6ahDSzKgqENVuxdQdPwprRq2AqH28E/V/6TAXEDGNRiEEfKjnDN/67hhlY38On2T71t0XnH8vhDzz/gNJzM2jyLVg1a0alRJ8JsYcTXiyfEGgJAXEQcf+77Z9pEtgGgQ1QHvhn9DVEhUdS31z/lW2arhq14us/T3nI2DW/K8JYVbf5htjDCbNVPEKzpt+zTXbNO3UwCyPl+dqf7/1deu2sa3pSb2tzkPf7mtjezMHuh9/fnuqTrGNJiCE3CmwCQdSSLBbsW8IeefyCzIJPJSybj1m5v82O4LRxDG1VqfQH5hSB7GUob5qdguCqW5g1QEiAqKThayry1e5h4VUuCrBY6NQ6mw/GtuA41JygqgTaWfFj8JDRuC62HQu/7YedSczyz1W6urOYD5Wk4MgsyuXvR3bgNN3arnXeGvcOCXQtoF9mOm9veDMDATwZyXdJ1/L7n71EoHlr6EGM7jOWx1MewWWws37OcVg3NSXQR9giuS7qOE84T3uBgwYLdYo62igmNIWNsBsrTLNYotJG33RugQXADRrcd7d22W+3eP2C5oYuTnc//v7P9/oztOJYxHcaglCJ9f7q3actluEjfn872Q9tZd2Adi0abgwReXfsq7218r8rfTkD8LiX2w61sYGgsQTaUDHMNbG5D4zIMgoOsrNl9mFkLf+CKyCN06dqDmzuEwte/hfi/mKurtegLj6wzZzQDJPWH8V+c1zDUrCNZlLnK6BBtpu1+b+N72C12xnY0OwvHfDWGZvWa8cKAF8zOQcPsHCxvh99SuIVwW7j3ere2u9V7LaUUM4fP9N60lVJ8c8s33mMtysJTvZ8isyCTBbsWeL+tDYgf4D2+JuSGLmribL8/5b+f1fUnXZ1wNfuL93uP/Srrqyp/O6+sfYUbWt/A9a2ur903cTbxvQga8S/ImG6OWgzg2gNc5gHi0AkHN/3nOx7sFcHoIVcwpF00w+o/i2XX9dC1B9RvBhO+hmbdzROstorg4PGTxcnPIQa/CraTDCzJWcL+E/u9N/xnf36WguIC7wiXf678J2WuMj4Y8QEAGfszCA0KZSzm8cMSh9EwuCFg/iHYLXZchsv7h1A+Jr7c/cn3V9nuEtPlrO+7Nr7tC3GxnO73N75+vPeY5/o9x6TFk7x/O/tP7GfjwY3eAPGnn/7E0ISh1LfXv/h/B2FRcLzAzLwc4PzWSa2UGg68DFiBaVrr5093rC87qX/cfoCiQwcZ1cf81p3zQj8ahtlo8MBS84Dti9BRrVCNzCRa24u280vRL97JRx9u+ZCf9v7Ea0NeI7Mgk/ELx2NogxBrCO8Me4dPt3/K+gPr+eLGLwD4YPMHFJUW8XCPhwHYeHAjSinv5KuzCch2VCHqgJP/dlyGiyBLEEcdR7njqzsYEDeAOdvm4HA7UErxfL/nq/SR1ZaNe44wf91e7hvQiqhwWZP6FEopK/Aa8CsgD1itlJqvtd7s69f6/IdprM76mp5J1zBqwCSKFjxO8+KlOFO3YQsK5sDA2/mgcB2Pe6qrM537eGXh06TdkUaQJYhF2YuYtmEawxOHY7VYzRTd2kBrbU5Q8sTX8iagZ694liBV8bGO6ziuSnk6N+p8XuWXZhshLszJfzvl8y3q2+vz5Y1f8s76d7x9cWhYsXcFw1sOp6C4gN1Hd2NRFtYUrPH5l7NNK5dgX7OYLaGjuHLQtT67bm3wVxNTL2CH1joLQCn1MTAK8GmA+PyHafwl6yWcCr7O2gbAkW6JPLM7giXH82naMJFdDWJYuHMT95UdoVFoIzpEdWBsh7HeiVC3t7+dm9rc5B1rf3v727m9/e2ApwnopJE7NovttOURQgSOnk17ev9+gyxBjGw9EoD5O+fz8pqXzX4OtxOb1ca0YdN8EiS2rv6GG9ZNxmZ14fj+f2yt9zHtew6t8XVri78CRHMgt9J2HlBltRyl1L3AvQAtWrS4oBfJyF6ESwFK4UKTkb2Ie2+aStOWfYgIjwFgdJvR3NL2Fu85vWJ7VZkwVZ6XpzrSli9E3XW6v9/b299O9pFsvtj5BQYGZe4y/rrir8wdObfGAzmKNi+lDW4sCoK0m6LNS0ECxCmq+5SrdIZord8G3gazD+JCXiQl8WoWZG3BhSZIQ0rLq4mLiCMuIq6iIDJyR4jLVnV/v+G2cEa3Hc2i7EU4DScKRfuo9t57xYxNMyhxltC3Wd/z/tuP7DgYR9Y72LQLJ0FEdhzss/dSG/wVIPKA+ErbccBeX7/IqAHmiJ+M7EWktLzauy2EEGdyutrFkuwlTEmfgkLx3sb3znt+RfueQ5mT9ya5axbTd/Aorgjg2gP4L0CsBtoopVoCe4DbgDtq44VGDZgkgUEIcd6qq11kH83GgsWbDffrXV9z3Hmcq5pfdc7X/XWPWHCVQWqKr4vsc35JGKS1dgEPAouALcAcrfUmf5RFCCHOVXnHtlVZsVls5BzN4allT1HsLD739OYlh2H/JqiUyj1Q+W2inNZ6AbDAX68vhBDn6+Smp47RHdl1ZBfbi7Zzz+J7KHOXYbfazzjqadtxO9lBg+hdsJuGDS9sAM7FErgpR4UQIgAlN05mUpdJJDdOxm610y6qHen70ylzl6HRONwO7yJOp8hdReuvxzBk3zvUn3Mz5K66uIU/TxIghBCihsrzQ1mwYLfaSW2SytqCtac2OWUvw+p2EISBxe0087gFsMs6F5MQQvjCyU1Pxx3H+c23vwEg2BpcMdopsR+GxYoyDLQlCItkcxVCiEtf5VFPL2W8hPZM7SpPw5PcOJkMow3/cvw/UvUmMlyd+KPRhkAeyyRNTEII4WMD4wcSYg3xjnZKjjEDR1pWIatdrXndPYrV7takZRX6uaRnJjUIIYTwscpNTmXuMp5c9iTXt7qeJtHd6W/bzAS+4E9Mpk9StL+LekYSIIQQohaUNznN3T6XgpIC3t3wLnarnT8PGEuHdS5eHZ5M14RIfxfzjCRACCFELSoqKwKNd/b1ekc+hFzBFUGH/F20s5I+CCGEqEXlywJYsACaoRs+YPiBd2n46WiZKDGjwQAACJpJREFUByGEEJez8v6I61pdh9KahWF2NgYH1Yl5EBIghBDiNJYsWUJSUhJKqRr9696kO1PvmorL6WZuRDiTmjZmpcVC3zseP+u5SUlJLFmyxC/vXwKEEEKcxuTJk9m1a5dPrhXePhxtURhK4cDCAzvCSMtzn/W8Xbt2MXnyZJ+U4XxJgBBCiNPwVXAAOLH1BNql0RoMLGxbe/bgUBvlOB8SIIQQ4iIo2VnCrn/vIqjITZvjDppE1/N3kc5KAoQQQpwjrXWN/k3/3yKMhnZ2RgQRemcYH6/78bTHBgIJEEIIcZEszvoZrQy0ApSbxVk/+7tIZyQBQgghLpKRkdHYMEBr7NpgZKSk2hBCCAGMshzBebCIuRHh3Fd0lAGtj/i7SGckNQghhLhYEvvR2q0ZUlxCA4sVZD0IIYQQAJnBdibGNsVpOLFbbUwLtlP9ytWBQWoQQghxkaTvT8epXWgFTsN9+rWrA4QECCGEuEhSm6RiU1aUBpsliNQmqf4u0hnVKEAopW5RSm1SShlKqdSTnntSKbVDKfX/27u/GLnKMo7j35/rbqvQUJG21rboVKqxJrrUDaFRuFCkf24WDIl7JRcaEoVYLryoaWLwggs1emFCpBRq0BhLRZtuYrWAoiYa2y6ybbdsFhYoYW3drTH80cTi0seL826dLGdmujs7e+YMv08ymXPec3bO8+xztu+c9z0zHZO0pap9a2obl7SzmeObmZVJ78peHvrE3Xztnat48IbvXvwvSttVs3MQI8Dngd3VjZI2AgPAx4D3A09I+nDafB/wOWACOCZpMCKeaTIOM7OWk9T0a9zwhc1c3ftufrRjB38eHFmAqFqnqSuIiBiNiLGcTf3Avog4HxEvAuPAdekxHhEvRMQbwL60r5lZx9u87Rpe3/oax9/3d/5zS7B52zVFh1RXq+Yg1gAvV61PpLZa7W8h6Q5JQ5KGzp0716IwzcwWzweuvYwLABLTytYvRaVSaWlctTTsICQ9IWkk51HvnX/edVjUaX9rY8QDEdEXEX0rVqxoFKaZWdt76el/0x3QFUF3ZOuNVCoVdu/e3XC/Vmg4BxERN83jdSeAdVXra4EzablWu5lZW2nFl+Yd/MODPHX6MJ+sbKH/0JcX/PUXUquGmAaBAUlLJFWADcBR4BiwQVJFUg/ZRPZgi2IwM2s7E1ec5yDPsvTqdY13Llizt7neKmkC2Az8StJhgIg4BewHngF+A9wZEW9GxDRwF3AYGAX2p33NzDre8NQwe0/uJQh2/WkXw1PDRYdUV1O3uUbEAeBAjW33AvfmtB8CDjVzXDOzMhqaHGL6wjRBMH1hmqHJobb+LIQ/SW1mtkj6VvXR09VDl7rofkd323+S2l/WZ2a2SHpX9rLn5j0MTQ7Rt6qvra8ewB2Emdmi6l3Z2/YdwwwPMZmZWS53EGZmlssdhJmZ5XIHYWZmudxBmJlZLncQZmaWS634MqqFJukc8FITL3EV8I8FCqddOKdy6MScoDPz6sScPhIRy+b7w6X4HERENPV935KGIqK9P7I4R86pHDoxJ+jMvDo1p2Z+3kNMZmaWyx2EmZnlert0EA8UHUALOKdy6MScoDPzck6zlGKS2szMFt/b5QrCzMzmyB2EmZnl6ugOQtJWSWOSxiXtLDqe+ZJ0WtJJScMzt61JulLS45KeS8/vKTrORiTtlTQlaaSqLTcPZX6QandC0qbiIq+tRk73SPpbqtewpO1V276RchqTtKWYqOuTtE7Sk5JGJZ2StCO1l7ZWdXIqba0kLZV0VNLxlNO3UntF0pFUp0ck9aT2JWl9PG3/YMODRERHPoAu4HlgPdADHAc2Fh3XPHM5DVw1q+07wM60vBP4dtFxXkIeNwKbgJFGeQDbgV8DAq4HjhQd/xxyugf4es6+G9N5uASopPOzq+gccuJcDWxKy8uAZ1Pspa1VnZxKW6v0+748LXcDR9Lvfz8wkNrvB76Slr8K3J+WB4BHGh2jk68grgPGI+KFiHgD2Af0FxzTQuoHHk7LDwO3FBjLJYmIPwL/nNVcK49+4MeR+QuwXNLqxYn00tXIqZZ+YF9EnI+IF4FxsvO0rUTE2Yj4a1p+HRgF1lDiWtXJqZa2r1X6ff8rrXanRwCfAR5N7bPrNFO/R4HPSlK9Y3RyB7EGeLlqfYL6J0Q7C+AxSU9JuiO1rYqIs5Cd/MDKwqJrTq08yl6/u9Jwy96q4b/S5ZSGIa4le3faEbWalROUuFaSuiQNA1PA42RXOq9ExHTapTruizml7a8C7633+p3cQeT1jGW9p/dTEbEJ2AbcKenGogNaBGWu3w+BDwG9wFnge6m9VDlJuhz4BXB3RLxWb9ectrbMKyenUtcqIt6MiF5gLdkVzkfzdkvPc86pkzuICWBd1fpa4ExBsTQlIs6k5yngANmJMDlzGZ+ep4qLsCm18iht/SJiMv3hXgD28P+hidLkJKmb7B/Sn0bEL1NzqWuVl1Mn1AogIl4Bfk82B7Fc0sz37FXHfTGntP0KGgyPdnIHcQzYkGb0e8gmZQYLjmnOJF0madnMMnAzMEKWy+1pt9uBg8VE2LRaeQwCX0x3yFwPvDozvNHuZo2/30pWL8hyGkh3k1SADcDRxY6vkTQu/RAwGhHfr9pU2lrVyqnMtZK0QtLytPwu4CayuZUngdvSbrPrNFO/24DfRZqxrqnomfgWz/JvJ7tb4XlgV9HxzDOH9WR3UxwHTs3kQTZ2+FvgufR8ZdGxXkIuPyO7jP8v2buZL9XKg+xy+L5Uu5NAX9HxzyGnn6SYT6Q/ytVV++9KOY0B24qOv0ZOnyYbejgBDKfH9jLXqk5Opa0V8HHg6RT7CPDN1L6erDMbB34OLEntS9P6eNq+vtEx/FUbZmaWq5OHmMzMrAnuIMzMLJc7CDMzy+UOwszMcrmDMDOzXO4gzMwslzsIMzPL9T9BN1yTpOPzKgAAAABJRU5ErkJggg==\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": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}