Delete Advent 2017.ipynb

This commit is contained in:
Peter Norvig 2017-12-04 00:32:26 -08:00 committed by GitHub
parent d5248633cf
commit 03b18cdd75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,803 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# December 2017: Advent of Code Solutions\n",
"\n",
"Peter Norvig\n",
"\n",
"I'm doing the [Advent of Code](https://adventofcode.com) puzzles, just like [last year](https://github.com/norvig/pytudes/blob/master/ipynb/Advent%20of%20Code.ipynb). But this time, I won't write up my interpretation of each day's the puzzle description; you'll have to follow the links in the section headers (e.g. **[Day 1](https://adventofcode.com/2017/day/1)**) to read those. I just show my solutions.\n",
"\n",
"# Day 0: Imports and Utility Functions\n",
"\n",
"I might need these:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Python 3.x Utility Functions\n",
"\n",
"import re\n",
"import numpy as np\n",
"import math\n",
"import random\n",
"import urllib.request\n",
"\n",
"from collections import Counter, defaultdict, namedtuple, deque, abc, OrderedDict\n",
"from functools import lru_cache\n",
"from itertools import (permutations, combinations, chain, cycle, product, islice, \n",
" takewhile, zip_longest, count as count_from)\n",
"from heapq import heappop, heappush\n",
"\n",
"identity = lambda x: x\n",
"letters = 'abcdefghijklmnopqrstuvwxyz'\n",
"\n",
"cat = ''.join\n",
"\n",
"Ø = frozenset() # Empty set\n",
"inf = float('inf')\n",
"BIG = 10 ** 999\n",
"\n",
"################ Functions for Input, Parsing\n",
"\n",
"def Input(day, year=2017):\n",
" \"Open this day's input file.\"\n",
" return open('data/advent{}/input{}.txt'.format(year, day))\n",
" \n",
"def array(lines):\n",
" \"Parse an iterable of str lines into a 2-D array. If `lines` is a str, do splitlines.\"\n",
" if isinstance(lines, str): lines = lines.splitlines()\n",
" return mapt(vector, lines)\n",
"\n",
"def vector(line):\n",
" \"Parse a str into a tuple of atoms (numbers or str tokens).\"\n",
" return mapt(atom, line.split())\n",
"\n",
"def atom(token):\n",
" \"Parse a str token into a number, or leave it as a str.\"\n",
" try:\n",
" return int(token)\n",
" except ValueError:\n",
" try:\n",
" return float(token)\n",
" except ValueError:\n",
" return token\n",
"\n",
"################ Functions on Iterables\n",
"\n",
"def first(iterable, default=None): return next(iter(iterable), default)\n",
"\n",
"def first_true(iterable, pred=None, default=None):\n",
" \"\"\"Returns the first true value in the iterable.\n",
" If no true value is found, returns *default*\n",
" If *pred* is not None, returns the first item\n",
" for which pred(item) is true.\"\"\"\n",
" # first_true([a,b,c], default=x) --> a or b or c or x\n",
" # first_true([a,b], fn, x) --> a if fn(a) else b if fn(b) else x\n",
" return next(filter(pred, iterable), default)\n",
"\n",
"def nth(iterable, n, default=None):\n",
" \"Returns the nth item of iterable, or a default value\"\n",
" return next(islice(iterable, n, None), default)\n",
"\n",
"def upto(iterable, maxval):\n",
" \"From a monotonically increasing iterable, generate all the values <= maxval.\"\n",
" # Why <= maxval rather than < maxval? In part because that's how Ruby's upto does it.\n",
" return takewhile(lambda x: x <= maxval, iterable)\n",
"\n",
"def groupby(iterable, key=identity):\n",
" \"Return a dict of {key(item): [items...]} grouping all items in iterable by keys.\"\n",
" groups = defaultdict(list)\n",
" for item in iterable:\n",
" groups[key(item)].append(item)\n",
" return groups\n",
"\n",
"def grouper(iterable, n, fillvalue=None):\n",
" \"\"\"Collect data into fixed-length chunks:\n",
" grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx\"\"\"\n",
" args = [iter(iterable)] * n\n",
" return zip_longest(*args, fillvalue=fillvalue)\n",
"\n",
"def overlapping(iterable, n):\n",
" \"\"\"Generate all (overlapping) n-element subsequences of iterable.\n",
" overlapping('ABCDEFG', 3) --> ABC BCD CDE DEF EFG\"\"\"\n",
" if isinstance(iterable, abc.Sequence):\n",
" yield from (iterable[i:i+n] for i in range(len(iterable) + 1 - n))\n",
" else:\n",
" result = deque(maxlen=n)\n",
" for x in iterable:\n",
" result.append(x)\n",
" if len(result) == n:\n",
" yield tuple(result)\n",
" \n",
"def pairwise(iterable):\n",
" \"s -> (s0,s1), (s1,s2), (s2, s3), ...\"\n",
" return overlapping(iterable, 2)\n",
"\n",
"def sequence(iterable, type=tuple):\n",
" \"Coerce iterable to sequence: leave it alone if it is already a sequence, else make it of type.\"\n",
" return iterable if isinstance(iterable, abc.Sequence) else type(iterable)\n",
"\n",
"def join(iterable, sep=''):\n",
" \"Join the items in iterable, converting each to a string first.\"\n",
" return sep.join(map(str, iterable))\n",
" \n",
"def powerset(iterable):\n",
" \"Yield all subsets of items.\"\n",
" items = list(iterable)\n",
" for r in range(len(items)+1):\n",
" for c in combinations(items, r):\n",
" yield c\n",
" \n",
"def quantify(iterable, pred=bool):\n",
" \"Count how many times the predicate is true.\"\n",
" return sum(map(pred, iterable))\n",
"\n",
"def shuffled(iterable):\n",
" \"Create a new list out of iterable, and shuffle it.\"\n",
" new = list(iterable)\n",
" random.shuffle(new)\n",
" return new\n",
" \n",
"flatten = chain.from_iterable\n",
" \n",
"class Set(frozenset):\n",
" \"A frozenset, but with a prettier printer.\"\n",
" def __repr__(self): return '{' + join(sorted(self), ', ') + '}'\n",
" \n",
"def canon(items, typ=None):\n",
" \"Canonicalize these order-independent items into a hashable canonical form.\"\n",
" typ = typ or (cat if isinstance(items, str) else tuple)\n",
" return typ(sorted(items))\n",
"\n",
"def mapt(fn, *args): \n",
" \"Do a map, and make the results into a tuple.\"\n",
" return tuple(map(fn, *args))\n",
" \n",
"################ Math Functions\n",
" \n",
"def transpose(matrix): return tuple(zip(*matrix))\n",
"\n",
"def isqrt(n):\n",
" \"Integer square root (rounds down).\"\n",
" return int(n ** 0.5)\n",
"\n",
"def ints(start, end):\n",
" \"The integers from start to end, inclusive: range(start, end+1)\"\n",
" return range(start, end + 1)\n",
"\n",
"def floats(start, end, step=1.0):\n",
" \"Yields from start to end (inclusive), by increments of step.\"\n",
" m = (1.0 if step >= 0 else -1.0)\n",
" while start * m <= end * m:\n",
" yield start\n",
" start += step\n",
" \n",
"def multiply(numbers):\n",
" \"Multiply all the numbers together.\"\n",
" result = 1\n",
" for n in numbers:\n",
" result *= n\n",
" return result\n",
"\n",
"################ 2-D points implemented using (x, y) tuples\n",
"\n",
"def X(point): x, y = point; return x\n",
"def Y(point): x, y = point; return y\n",
"\n",
"origin = (0, 0)\n",
"UP, DOWN, LEFT, RIGHT = (0, 1), (0, -1), (-1, 0), (1, 0)\n",
"\n",
"def neighbors4(point): \n",
" \"The four neighboring squares.\"\n",
" x, y = point\n",
" return ( (x, y-1),\n",
" (x-1, y), (x+1, y), \n",
" (x, y+1))\n",
"\n",
"def neighbors8(point): \n",
" \"The eight neighboring squares.\"\n",
" x, y = point \n",
" return ((x-1, y-1), (x, y-1), (x+1, y-1),\n",
" (x-1, y), (x+1, y),\n",
" (x-1, y+1), (x, y+1), (x+1, y+1))\n",
"\n",
"def cityblock_distance(p, q=origin): \n",
" \"Manhatten distance between two points.\"\n",
" return abs(X(p) - X(q)) + abs(Y(p) - Y(q))\n",
"\n",
"def distance(p, q=origin): \n",
" \"Hypotenuse distance between two points.\"\n",
" return math.hypot(X(p) - X(q), Y(p) - Y(q))\n",
"\n",
"################ Debugging \n",
"\n",
"def trace1(f):\n",
" \"Print a trace of the input and output of a function on one line.\"\n",
" def traced_f(*args):\n",
" result = f(*args)\n",
" print('{}({}) = {}'.format(f.__name__, ', '.join(map(str, args)), result))\n",
" return result\n",
" return traced_f\n",
"\n",
"def grep(pattern, iterable):\n",
" \"Print lines from iterable that match pattern.\"\n",
" for line in iterable:\n",
" if re.search(pattern, line):\n",
" print(line)\n",
"\n",
"################ A* and Breadth-First Search (tracking states, not actions)\n",
"\n",
"def always(value): return (lambda *args: value)\n",
"\n",
"def Astar(start, moves_func, h_func, cost_func=always(1)):\n",
" \"Find a shortest sequence of states from start to a goal state (a state s with h_func(s) == 0).\"\n",
" frontier = [(h_func(start), start)] # A priority queue, ordered by path length, f = g + h\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(s)\n",
" for s2 in moves_func(s):\n",
" g = path_cost[s] + cost_func(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 bfs(start, moves_func, goals):\n",
" \"Breadth-first search\"\n",
" goal_func = (goals if callable(goals) else lambda s: s in goals)\n",
" return Astar(start, moves_func, lambda s: (0 if goal_func(s) else 1))"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'pass'"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def tests():\n",
" # Functions for Input, Parsing\n",
" assert array('''1 2 3\n",
" 4 5 6''') == ((1, 2, 3), \n",
" (4, 5, 6))\n",
" assert vector('testing 1 2 3.') == ('testing', 1, 2, 3.0)\n",
" \n",
" # Functions on Iterables\n",
" assert first('abc') == first(['a', 'b', 'c']) == 'a'\n",
" assert first_true([0, None, False, {}, 42, 43]) == 42\n",
" assert nth('abc', 1) == nth(iter('abc'), 1) == 'b'\n",
" assert cat(upto('abcdef', 'd')) == 'abcd'\n",
" assert cat(['do', 'g']) == 'dog'\n",
" assert groupby([-3, -2, -1, 1, 2], abs) == {1: [-1, 1], 2: [-2, 2], 3: [-3]}\n",
" assert list(grouper(range(8), 3)) == [(0, 1, 2), (3, 4, 5), (6, 7, None)]\n",
" assert list(overlapping((0, 1, 2, 3, 4), 3)) == [(0, 1, 2), (1, 2, 3), (2, 3, 4)]\n",
" assert list(overlapping('abcdefg', 4)) == ['abcd', 'bcde', 'cdef', 'defg'] \n",
" assert list(pairwise((0, 1, 2, 3, 4))) == [(0, 1), (1, 2), (2, 3), (3, 4)]\n",
" assert sequence('seq') == 'seq'\n",
" assert sequence((i**2 for i in range(5))) == (0, 1, 4, 9, 16)\n",
" assert join(range(5)) == '01234'\n",
" assert join(range(5), ', ') == '0, 1, 2, 3, 4'\n",
" assert multiply([1, 2, 3, 4]) == 24\n",
" assert transpose(((1, 2, 3), (4, 5, 6))) == ((1, 4), (2, 5), (3, 6))\n",
" assert isqrt(9) == 3 == isqrt(10)\n",
" assert ints(1, 100) == range(1, 101)\n",
" assert identity('anything') == 'anything'\n",
" assert set(powerset({1, 2, 3})) == {(), (1,), (1, 2), (1, 2, 3), (1, 3), (2,), (2, 3), (3,)}\n",
" assert quantify(['testing', 1, 2, 3, int, len], callable) == 2 # int and len are callable\n",
" assert quantify([0, False, None, '', [], (), {}, 42]) == 1 # Only 42 is truish\n",
" assert set(shuffled('abc')) == set('abc')\n",
" assert canon('abecedarian') == 'aaabcdeeinr'\n",
" assert canon([9, 1, 4]) == canon({1, 4, 9}) == (1, 4, 9)\n",
" assert mapt(math.sqrt, [1, 9, 4]) == (1, 3, 2)\n",
" \n",
" # Math\n",
" assert transpose([(1, 2, 3), (4, 5, 6)]) == ((1, 4), (2, 5), (3, 6))\n",
" assert isqrt(10) == isqrt(9) == 3\n",
" assert ints(1, 5) == range(1, 6)\n",
" assert list(floats(1, 5)) == [1., 2., 3., 4., 5.]\n",
" assert multiply(ints(1, 10)) == math.factorial(10) == 3628800\n",
" \n",
" # 2-D points\n",
" P = (3, 4)\n",
" assert X(P) == 3 and Y(P) == 4\n",
" assert cityblock_distance(P) == cityblock_distance(P, origin) == 7\n",
" assert distance(P) == distance(P, origin) == 5\n",
" \n",
" # Search\n",
" assert Astar((4, 4), neighbors8, distance) == [(4, 4), (3, 3), (2, 2), (1, 1), (0, 0)]\n",
" assert bfs((4, 4), neighbors8, {origin}) == [(4, 4), (3, 3), (2, 2), (1, 1), (0, 0)]\n",
" forty2 = always(42)\n",
" assert forty2() == forty2('?') == forty2(4, 2) == 42\n",
"\n",
" return 'pass'\n",
"\n",
"tests()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 1](https://adventofcode.com/2017/day/1): Inverse Captcha\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"digits = mapt(int, '3294199471327195994824832197564859876682638188889768298894243832665654681412886862234525991553276578641265589959178414218389329361496673991614673626344552179413995562266818138372393213966143124914469397692587251112663217862879233226763533911128893354536353213847122251463857894159819828724827969576432191847787772732881266875469721189331882228146576832921314638221317393256471998598117289632684663355273845983933845721713497811766995367795857965222183668765517454263354111134841334631345111596131682726196574763165187889337599583345634413436165539744188866156771585647718555182529936669683581662398618765391487164715724849894563314426959348119286955144439452731762666568741612153254469131724137699832984728937865956711925592628456617133695259554548719328229938621332325125972547181236812263887375866231118312954369432937359357266467383318326239572877314765121844831126178173988799765218913178825966268816476559792947359956859989228917136267178571776316345292573489873792149646548747995389669692188457724414468727192819919448275922166321158141365237545222633688372891451842434458527698774342111482498999383831492577615154591278719656798277377363284379468757998373193231795767644654155432692988651312845433511879457921638934877557575241394363721667237778962455961493559848522582413748218971212486373232795878362964873855994697149692824917183375545192119453587398199912564474614219929345185468661129966379693813498542474732198176496694746111576925715493967296487258237854152382365579876894391815759815373319159213475555251488754279888245492373595471189191353244684697662848376529881512529221627313527441221459672786923145165989611223372241149929436247374818467481641931872972582295425936998535194423916544367799522276914445231582272368388831834437562752119325286474352863554693373718848649568451797751926315617575295381964426843625282819524747119726872193569785611959896776143539915299968276374712996485367853494734376257511273443736433464496287219615697341973131715166768916149828396454638596713572963686159214116763')\n",
"N = len(digits)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1158"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum(digits[i] \n",
" for i in range(N) \n",
" if digits[i] == digits[i - 1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Part Two**:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1132"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum(digits[i] \n",
" for i in range(N) \n",
" if digits[i] == digits[i - N//2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This was an easy warmup puzzle. I forgot that Advent of Code was starting at 9:00PM my time, so I started late, but even if I had started on time, I doubt I would have been fast enough to score points. (I created a recurring calendar reminder so I'll be more likely to start on time in the future.)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 2](https://adventofcode.com/2017/day/2): Corruption Checksum\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"rows = array('''790\t99\t345\t1080\t32\t143\t1085\t984\t553\t98\t123\t97\t197\t886\t125\t947\n",
"302\t463\t59\t58\t55\t87\t508\t54\t472\t63\t469\t419\t424\t331\t337\t72\n",
"899\t962\t77\t1127\t62\t530\t78\t880\t129\t1014\t93\t148\t239\t288\t357\t424\n",
"2417\t2755\t254\t3886\t5336\t3655\t5798\t3273\t5016\t178\t270\t6511\t223\t5391\t1342\t2377\n",
"68\t3002\t3307\t166\t275\t1989\t1611\t364\t157\t144\t3771\t1267\t3188\t3149\t156\t3454\n",
"1088\t1261\t21\t1063\t1173\t278\t1164\t207\t237\t1230\t1185\t431\t232\t660\t195\t1246\n",
"49\t1100\t136\t1491\t647\t1486\t112\t1278\t53\t1564\t1147\t1068\t809\t1638\t138\t117\n",
"158\t3216\t1972\t2646\t3181\t785\t2937\t365\t611\t1977\t1199\t2972\t201\t2432\t186\t160\n",
"244\t86\t61\t38\t58\t71\t243\t52\t245\t264\t209\t265\t308\t80\t126\t129\n",
"1317\t792\t74\t111\t1721\t252\t1082\t1881\t1349\t94\t891\t1458\t331\t1691\t89\t1724\n",
"3798\t202\t3140\t3468\t1486\t2073\t3872\t3190\t3481\t3760\t2876\t182\t2772\t226\t3753\t188\n",
"2272\t6876\t6759\t218\t272\t4095\t4712\t6244\t4889\t2037\t234\t223\t6858\t3499\t2358\t439\n",
"792\t230\t886\t824\t762\t895\t99\t799\t94\t110\t747\t635\t91\t406\t89\t157\n",
"2074\t237\t1668\t1961\t170\t2292\t2079\t1371\t1909\t221\t2039\t1022\t193\t2195\t1395\t2123\n",
"8447\t203\t1806\t6777\t278\t2850\t1232\t6369\t398\t235\t212\t992\t7520\t7304\t7852\t520\n",
"3928\t107\t3406\t123\t2111\t2749\t223\t125\t134\t146\t3875\t1357\t508\t1534\t4002\t4417''')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"46402"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum(abs(max(row) - min(row)) for row in rows)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Part Two**:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"265"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def evendiv(row): \n",
" return first(a // b for a in row for b in row if a > b and a // b == a / b)\n",
"\n",
"sum(map(evendiv, rows))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This day was also very easy. In Part One, I was slowed down by a typo: I had `\"=\"` instead of `\"-\"` in `\"max(row) - min(row)\"`. I was confused by Python's misleading error message, which said `\"SyntaxError: keyword can't be an expression\"`. Later on, Alex Martelli explained to me that the message meant that in `abs(max(row)=...)` it thought that `max(row)` was a keyword argument to `abs`. \n",
"\n",
"In Part Two, note that to check that `a/b` is an exact integer, I used `a // b == a / b`, which I think is more clear and less error-prone than the expression one would typically use here, `a % b == 0`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 3](https://adventofcode.com/2017/day/3): Spiral Memory"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"N = 277678"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This one takes some thinking, not just fast typing. I analyzed the problem as having three parts:\n",
"- Generate a spiral\n",
"- Find the Nth square on the spiral. \n",
"- Find the distance from that square to the center.\n",
"\n",
"I suspect many people will do all three of these in one function. That's probably the best way to get the answer quickly, but I'd rather be clear than quick, so I'll factor out each part, according to the single responsibility principle. My function `spiral()` will generate the coordinates of squares on an infinite spiral, in order, going out from the center square, `(0, 0)`.\n",
"\n",
"How to make a spiral? My analysis is that, after the center square, the spiral goes 1 square right, then 1 square up, then 2 square left, then 2 square down, to complete one revolution; the next revolution starts with 3 square going up, and so on. I'll call each of these a `leg`, so `spiral` consists of four calls to `leg`, with increments to the `length` after every two legs. \n",
"\n",
"One thing is less clear than I would like: the variable `square` is modified by the function `leg` (in other words, it is an in/out parameter). A small test confirms that this matches the puzzle description:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[(0, 0),\n",
" (1, 0),\n",
" (1, 1),\n",
" (0, 1),\n",
" (-1, 1),\n",
" (-1, 0),\n",
" (-1, -1),\n",
" (0, -1),\n",
" (1, -1),\n",
" (2, -1)]"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def spiral():\n",
" \"Yield the (x, y) coordinates of successive points in an infinite spiral.\"\n",
" length = 1\n",
" square = [0, 0]\n",
" yield tuple(square)\n",
" while True:\n",
" yield from leg(square, length, RIGHT)\n",
" yield from leg(square, length, UP)\n",
" length += 1\n",
" yield from leg(square, length, LEFT)\n",
" yield from leg(square, length, DOWN)\n",
" length += 1 \n",
" \n",
"def leg(square, length, delta):\n",
" \"Complete one leg of given length, mutating `square` and yielding a copy at each step.\"\n",
" for _ in range(length):\n",
" square[:] = (X(square) + X(delta), Y(square) + Y(delta))\n",
" yield tuple(square) \n",
" \n",
"list(islice(spiral(), 10))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we can find the `N`th square. As this is Python, indexes start at 0, whereas the problem starts at 1, so I have to subtract 1. Then I can find the distance to the origin:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(212, -263)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nth(spiral(), N - 1)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"475"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cityblock_distance(_)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That's the right answer. I was slow arriving at it because I forgot the second `length += 1` in `spiral` and it took a while to debug. (I had the right analysis, but just left out a line of code.)\n",
"\n",
"For **Part Two** I can re-use my `spiral` generator, yay! Here's a function to sum the neighboring squares (I can use my `neighbors8` function, yay!):"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def spiralsums():\n",
" \"Yield the values of a spiral where each point has the sum of the 8 neighbors.\"\n",
" value = defaultdict(int)\n",
" for p in spiral():\n",
" value[p] = sum(value[q] for q in neighbors8(p)) or 1\n",
" yield value[p]"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 1, 2, 4, 5, 10, 11, 23, 25, 26, 54, 57]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(islice(spiralsums(), 12))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Looks good, so let's get the answer:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"279138"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"first(x for x in spiralsums() if x > N)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [Day 4](https://adventofcode.com/2017/day/4): High-Entropy Passphrases\n",
"\n",
"This is the first time I will have to store an input file and read it with the function `Input`. It should be straightforward, though:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"337"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def isvalid(line):\n",
" words = line.split()\n",
" return len(words) == len(set(words))\n",
"\n",
"quantify(Input(4), isvalid)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Part Two:**"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"231"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def isvalid2(line):\n",
" words = mapt(canon, line.split())\n",
" return len(words) == len(set(words))\n",
"\n",
"quantify(Input(4), isvalid2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It took me less than five minutes, and my preparation with `Input` and `canon` helped, but I was still too slow to score any points."
]
}
],
"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.5.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}