AoC
This commit is contained in:
parent
e31786e7e4
commit
778f22dbde
File diff suppressed because one or more lines are too long
@ -13,7 +13,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -24,16 +24,17 @@
|
||||
"from typing import *\n",
|
||||
"from statistics import mean, median\n",
|
||||
"from math import ceil, floor, factorial, gcd, log, log2, log10, sqrt, inf\n",
|
||||
"from functools import reduce\n",
|
||||
"\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import time\n",
|
||||
"import heapq\n",
|
||||
"import string\n",
|
||||
"\n",
|
||||
"import ast\n",
|
||||
"import functools\n",
|
||||
"import heapq\n",
|
||||
"import operator\n",
|
||||
"import pathlib\n",
|
||||
"import re"
|
||||
"import re\n",
|
||||
"import string\n",
|
||||
"import time"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -55,12 +56,12 @@
|
||||
"- **Part 2**: Repeat the above steps for Part 2.\n",
|
||||
"- Occasionally I'll introduce a **Part 3** where I explore beyond the official instructions.\n",
|
||||
"\n",
|
||||
"Here is `parse` and some helper functions for it:"
|
||||
"Here is `parse`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -108,14 +109,19 @@
|
||||
" return lambda section: next(fns)(section)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Functions that can be used as the `parser` argument to `parse` (also, consider `str.split` to split the line on whitespace): "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Functions that can be used by `parse`\n",
|
||||
"\n",
|
||||
"Char = str # Intended as the type of a one-character string\n",
|
||||
"Atom = Union[str, float, int] # The type of a string or number\n",
|
||||
"\n",
|
||||
@ -147,9 +153,16 @@
|
||||
" except ValueError:\n",
|
||||
" return text.strip()\n",
|
||||
" \n",
|
||||
"def mapt(function: Callable, sequence) -> tuple:\n",
|
||||
"def mapt(function: Callable, *sequences) -> tuple:\n",
|
||||
" \"\"\"`map`, with the result as a tuple.\"\"\"\n",
|
||||
" return tuple(map(function, sequence))"
|
||||
" return tuple(map(function, *sequences))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Some tests for the functions above:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -158,8 +171,6 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## TESTS\n",
|
||||
"\n",
|
||||
"assert parse(\"hello\\nworld\", show=0) == ('hello', 'world')\n",
|
||||
"assert parse(\"123\\nabc7\", digits, show=0) == ((1, 2, 3), (7,))\n",
|
||||
"assert truncate('hello world', 99) == 'hello world'\n",
|
||||
@ -196,9 +207,9 @@
|
||||
" def pretty(x): return f'{x:,d}' if is_int(x) else truncate(x)\n",
|
||||
" start = time.time()\n",
|
||||
" got = code()\n",
|
||||
" dt = time.time() - start\n",
|
||||
" secs = time.time() - start\n",
|
||||
" ans = pretty(got)\n",
|
||||
" msg = f'{dt:5.3f} seconds for ' + (\n",
|
||||
" msg = f'{secs:5.3f} seconds for ' + (\n",
|
||||
" f'correct answer: {ans}' if (got == correct) else\n",
|
||||
" f'WRONG!! ANSWER: {ans}; EXPECTED {pretty(correct)}')\n",
|
||||
" answers[puzzle] = msg\n",
|
||||
@ -216,14 +227,10 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 17,
|
||||
"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",
|
||||
@ -242,6 +249,10 @@
|
||||
" result *= x\n",
|
||||
" return result\n",
|
||||
"\n",
|
||||
"def T(matrix: Sequence[Sequence]) -> List[Tuple]:\n",
|
||||
" \"\"\"The transpose of a matrix: T([(1,2,3), (4,5,6)]) == [(1,4), (2,5), (3,6)]\"\"\"\n",
|
||||
" return list(zip(*matrix))\n",
|
||||
"\n",
|
||||
"def total(counter: Counter) -> int: \n",
|
||||
" \"\"\"The sum of all the counts in a Counter.\"\"\"\n",
|
||||
" return sum(counter.values())\n",
|
||||
@ -251,14 +262,6 @@
|
||||
" numbers = list(numbers)\n",
|
||||
" return min(numbers), max(numbers)\n",
|
||||
"\n",
|
||||
"def first(iterable) -> Optional[object]: \n",
|
||||
" \"\"\"The first element in an iterable, or None.\"\"\"\n",
|
||||
" return next(iter(iterable), None)\n",
|
||||
"\n",
|
||||
"def T(matrix: Sequence[Sequence]) -> List[Tuple]:\n",
|
||||
" \"\"\"The transpose of a matrix: T([(1,2,3), (4,5,6)]) == [(1,4), (2,5), (3,6)]\"\"\"\n",
|
||||
" return list(zip(*matrix))\n",
|
||||
"\n",
|
||||
"def cover(*integers) -> range:\n",
|
||||
" \"\"\"A `range` that covers all the given integers, and any in between them.\n",
|
||||
" cover(lo, hi) is a an inclusive (or closed) range, equal to range(lo, hi + 1).\"\"\"\n",
|
||||
@ -275,6 +278,93 @@
|
||||
" \"\"\"The sequence split into two pieces: (before position i, and i-and-after).\"\"\"\n",
|
||||
" return sequence[:i], sequence[i:]\n",
|
||||
"\n",
|
||||
"def ignore(*args) -> None: \"Just return None.\"; return None\n",
|
||||
"\n",
|
||||
"def is_int(x) -> bool: \"Is x an int?\"; return isinstance(x, int) \n",
|
||||
"\n",
|
||||
"def sign(x) -> int: \"0, +1, or -1\"; return (0 if x == 0 else +1 if x > 0 else -1)\n",
|
||||
"\n",
|
||||
"def union(sets) -> set: \"Union of several sets\"; return set().union(*sets)\n",
|
||||
"\n",
|
||||
"def intersection(sets):\n",
|
||||
" \"Intersection of several sets; error if no sets.\"\n",
|
||||
" first, *rest = sets\n",
|
||||
" return set(first).intersection(*rest)\n",
|
||||
" \n",
|
||||
"def naked_plot(points, marker='o', size=(10, 10), invert=True, square=False, **kwds):\n",
|
||||
" \"\"\"Plot `points` without any axis lines or tick marks.\n",
|
||||
" Optionally specify size, whether square or not, and whether to invery y axis.\"\"\"\n",
|
||||
" if size: plt.figure(figsize=((size, size) if is_int(size) else size))\n",
|
||||
" plt.plot(*T(points), marker, **kwds)\n",
|
||||
" if square: plt.axis('square')\n",
|
||||
" plt.axis('off')\n",
|
||||
" if invert: plt.gca().invert_yaxis()\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\n",
|
||||
"\n",
|
||||
"cat = ''.join\n",
|
||||
"cache = functools.lru_cache(None)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"More tests:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"assert multimap(((i % 3), i) for i in range(9)) == {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5, 8]}\n",
|
||||
"assert prod([2, 3, 5]) == 30\n",
|
||||
"assert total(Counter('hello, world')) == 12\n",
|
||||
"assert cover(3, 1, 4, 1, 5) == range(1, 6)\n",
|
||||
"assert minmax([3, 1, 4, 1, 5, 9]) == (1, 9)\n",
|
||||
"assert T([(1, 2, 3), (4, 5, 6)]) == [(1, 4), (2, 5), (3, 6)]\n",
|
||||
"assert the({1}) == 1\n",
|
||||
"assert split_at('hello, world', 6) == ('hello,', ' world')\n",
|
||||
"assert is_int(-42) and not is_int('one')\n",
|
||||
"assert sign(-42) == -1 and sign(0) == 0 and sign(42) == +1\n",
|
||||
"assert union([{1, 2}, {3, 4}, {5, 6}]) == {1, 2, 3, 4, 5, 6}\n",
|
||||
"assert intersection([{1, 2, 3}, {2, 3, 4}, {2, 4, 6, 8}]) == {2}\n",
|
||||
"assert clock_mod(24, 12) == 12 and 24 % 12 == 0\n",
|
||||
"assert cat(['hello', 'world']) == 'helloworld'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Itertools Recipes\n",
|
||||
"\n",
|
||||
"The Python docs for the `itertools` module has some [\"recipes\"](https://docs.python.org/3/library/itertools.html#itertools-recipes) that I include here (some I have slightly modified):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"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",
|
||||
"def dotproduct(vec1, vec2):\n",
|
||||
" \"\"\"The dot product of two vectors.\"\"\"\n",
|
||||
" return sum(map(operator.mul, vec1, vec2))\n",
|
||||
"\n",
|
||||
"flatten = chain.from_iterable # Yield items from each sequence in turn\n",
|
||||
"\n",
|
||||
"def append(sequences) -> Sequence: \"Append into a list\"; return list(flatten(sequences))\n",
|
||||
"\n",
|
||||
"def batched(data, n) -> list:\n",
|
||||
" \"Batch data into lists of length n. The last batch may be shorter.\"\n",
|
||||
" # batched('ABCDEFG', 3) --> ABC DEF G\n",
|
||||
@ -284,68 +374,37 @@
|
||||
" \"\"\"All length-n subsequences of sequence.\"\"\"\n",
|
||||
" return (sequence[i:i+n] for i in range(len(sequence) + 1 - n))\n",
|
||||
"\n",
|
||||
"def ignore(*args) -> None: \"Just return None.\"; return None\n",
|
||||
"def first(iterable, default=None) -> Optional[object]: \n",
|
||||
" \"\"\"The first element in an iterable, or the default if iterable is empty.\"\"\"\n",
|
||||
" return next(iter(iterable), default)\n",
|
||||
"\n",
|
||||
"def is_int(x) -> bool: \"Is x an int?\"; return isinstance(x, int) \n",
|
||||
"\n",
|
||||
"def sign(x) -> int: \"0, +1, or -1\"; return (0 if x == 0 else +1 if x > 0 else -1)\n",
|
||||
"\n",
|
||||
"def append(sequences) -> Sequence: \"Append sequences into a list\"; return list(flatten(sequences))\n",
|
||||
"\n",
|
||||
"def union(sets) -> set: \"Union of several sets\"; return set().union(*sets)\n",
|
||||
"\n",
|
||||
"def intersection(sets):\n",
|
||||
" \"Intersection of several sets.\"\n",
|
||||
" first, *rest = sets\n",
|
||||
" return set(first).intersection(*rest)\n",
|
||||
"\n",
|
||||
"def square_plot(points, marker='o', size=12, extra=None, **kwds):\n",
|
||||
" \"\"\"Plot `points` in a square of given `size`, with no axis labels.\n",
|
||||
" Calls `extra()` to do more plt.* stuff if defined.\"\"\"\n",
|
||||
" plt.figure(figsize=(size, size))\n",
|
||||
" plt.plot(*T(points), marker, **kwds)\n",
|
||||
" if extra: extra()\n",
|
||||
" plt.axis('square'); plt.axis('off'); plt.gca().invert_yaxis()\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\n",
|
||||
"\n",
|
||||
"flatten = chain.from_iterable # Yield items from each sequence in turn\n",
|
||||
"cat = ''.join\n",
|
||||
"cache = functools.lru_cache(None)"
|
||||
"def first_true(iterable, default=False):\n",
|
||||
" \"\"\"Returns the first true value in the iterable.\n",
|
||||
" If no true value is found, returns `default`.\"\"\"\n",
|
||||
" return next((x for x in iterable if x), default)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"More tests:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## TESTS\n",
|
||||
"\n",
|
||||
"assert quantify(words('This is a test'), str.islower) == 3\n",
|
||||
"assert mapt(first, words('This is a test')) == ('T', 'i', 'a', 't')\n",
|
||||
"assert multimap(((i % 3), i) for i in range(9)) == {0: [0, 3, 6], 1: [1, 4, 7], 2: [2, 5, 8]}\n",
|
||||
"assert prod([2, 3, 5]) == 30\n",
|
||||
"assert total(Counter('hello, world')) == 12\n",
|
||||
"assert cover(3, 1, 4, 1, 5) == range(1, 6)\n",
|
||||
"assert minmax([3, 1, 4, 1, 5, 9]) == (1, 9)\n",
|
||||
"assert first('abc') == 'a'\n",
|
||||
"assert T([(1, 2, 3), (4, 5, 6)]) == [(1, 4), (2, 5), (3, 6)]\n",
|
||||
"assert the({1}) == 1\n",
|
||||
"assert split_at('hello, world', 6) == ('hello,', ' world')\n",
|
||||
"assert dotproduct([1, 2, 3, 4], [1000, 100, 10, 1]) == 1234\n",
|
||||
"assert list(flatten([{1, 2, 3}, (4, 5, 6), [7, 8, 9]])) == [1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
|
||||
"assert append(([1, 2], [3, 4], [5, 6])) == [1, 2, 3, 4, 5, 6]\n",
|
||||
"assert batched('abcdefghi', 3) == ['abc', 'def', 'ghi']\n",
|
||||
"assert list(sliding_window('abcdefghi', 3)) == ['abc', 'bcd', 'cde', 'def', 'efg', 'fgh', 'ghi']\n",
|
||||
"assert is_int(-42) and not is_int('one')\n",
|
||||
"assert sign(-42) == -1 and sign(0) == 0 and sign(42) == +1\n",
|
||||
"assert append(([1, 2], [3, 4], [5, 6])) == [1, 2, 3, 4, 5, 6]\n",
|
||||
"assert union([{1, 2}, {3, 4}, {5, 6}]) == {1, 2, 3, 4, 5, 6}\n",
|
||||
"assert intersection([{1, 2, 3}, {2, 3, 4}, {2, 4, 6, 8}]) == {2}\n",
|
||||
"assert clock_mod(24, 12) == 12 and 24 % 12 == 0\n",
|
||||
"assert list(flatten(['abc', 'def', '123'])) == ['a', 'b', 'c', 'd', 'e', 'f', '1', '2', '3']\n",
|
||||
"assert cat(['hello', 'world']) == 'helloworld'"
|
||||
"assert first('abc') == 'a'\n",
|
||||
"assert first_true([0, None, False, 42, 99]) == 42"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -359,7 +418,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -391,39 +450,21 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## TESTS\n",
|
||||
"\n",
|
||||
"p, q = (0, 3), (4, 0)\n",
|
||||
"assert Y_(p) == 3 and X_(q) == 4\n",
|
||||
"assert distance(p, q) == 5\n",
|
||||
"assert manhatten_distance(p, q) == 7\n",
|
||||
"assert add(p, q) == (4, 3)\n",
|
||||
"assert sub(p, q) == (-4, 3)\n",
|
||||
"assert add(North, South) == (0,0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Grid(dict):\n",
|
||||
" \"\"\"A 2D grid, implemented as a mapping of {(x, y): cell_contents}.\"\"\"\n",
|
||||
" def __init__(self, mapping_or_rows, directions=directions4):\n",
|
||||
" def __init__(self, mapping_or_rows=(), directions=directions4):\n",
|
||||
" \"\"\"Initialize with either (e.g.) `Grid({(0, 0): 1, (1, 0): 2, ...})`, or\n",
|
||||
" `Grid([(1, 2, 3), (4, 5, 6)]).\"\"\"\n",
|
||||
" self.directions = directions\n",
|
||||
" self.update(mapping_or_rows if isinstance(mapping_or_rows, abc.Mapping) else\n",
|
||||
" {(x, y): val \n",
|
||||
" for y, row in enumerate(mapping_or_rows) \n",
|
||||
" for x, val in enumerate(row)})\n",
|
||||
" self.width = max(map(X_, self)) + 1\n",
|
||||
" self.height = max(map(Y_, self)) + 1\n",
|
||||
" self.directions = directions\n",
|
||||
"\n",
|
||||
" \n",
|
||||
" def copy(self): return Grid(self, directions=self.directions)\n",
|
||||
" \n",
|
||||
@ -431,14 +472,43 @@
|
||||
" \"\"\"Points on the grid that neighbor `point`.\"\"\"\n",
|
||||
" return [add(point, Δ) for Δ in self.directions if add(point, Δ) in self]\n",
|
||||
" \n",
|
||||
" def to_rows(self, default='.') -> List[List[object]]:\n",
|
||||
" def to_rows(self, default='.', Xs=None, Ys=None) -> List[List[object]]:\n",
|
||||
" \"\"\"The contents of the grid in a rectangular list of lists.\"\"\"\n",
|
||||
" return [[self.get((x, y), default) for x in range(self.width)]\n",
|
||||
" for y in range(self.height)]\n",
|
||||
" Xs = Xs or range(max(map(X_, self)) + 1)\n",
|
||||
" Ys = Ys or range(max(map(Y_, self)) + 1)\n",
|
||||
" return [[self.get((x, y), default) for x in Xs] for y in Ys]\n",
|
||||
" \n",
|
||||
" def to_picture(self, sep='', default='.') -> str:\n",
|
||||
" \"\"\"The contents of the grid as a picture.\"\"\"\n",
|
||||
" return '\\n'.join(map(cat, self.to_rows(default)))"
|
||||
" def to_picture(self, sep='', default='.', Xs=None, Ys=None) -> str:\n",
|
||||
" \"\"\"The contents of the grid as a picture. Youi can specify the `Xs` and `Ys` to include.\"\"\"\n",
|
||||
" return '\\n'.join(map(sep.join, self.to_rows(default, Xs, Ys)))\n",
|
||||
" \n",
|
||||
" def plot(self, markers, figsize=(14, 14), **kwds):\n",
|
||||
" plt.figure(figsize=figsize)\n",
|
||||
" plt.gca().invert_yaxis()\n",
|
||||
" for m in markers:\n",
|
||||
" plt.plot(*T(p for p in self if self[p] == m), markers.get(m, m), **kwds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tests:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"p, q = (0, 3), (4, 0)\n",
|
||||
"assert Y_(p) == 3 and X_(q) == 4\n",
|
||||
"assert distance(p, q) == 5\n",
|
||||
"assert manhatten_distance(p, q) == 7\n",
|
||||
"assert add(p, q) == (4, 3)\n",
|
||||
"assert sub(p, q) == (-4, 3)\n",
|
||||
"assert add(North, South) == (0,0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -452,7 +522,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -480,7 +550,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -545,7 +615,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
|
Loading…
Reference in New Issue
Block a user