diff --git a/ipynb/Advent 2017.ipynb b/ipynb/Advent 2017.ipynb index 8609645..73dfeb9 100644 --- a/ipynb/Advent 2017.ipynb +++ b/ipynb/Advent 2017.ipynb @@ -19,17 +19,19 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "collapsed": false }, "outputs": [], "source": [ "# Python 3.x Utility Functions\n", "\n", + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\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", @@ -40,6 +42,8 @@ "identity = lambda x: x\n", "letters = 'abcdefghijklmnopqrstuvwxyz'\n", "\n", + "cache = lru_cache(None)\n", + "\n", "cat = ''.join\n", "\n", "Ø = frozenset() # Empty set\n", @@ -161,6 +165,11 @@ "def mapt(fn, *args): \n", " \"Do a map, and make the results into a tuple.\"\n", " return tuple(map(fn, *args))\n", + "\n", + "def most_common(seq):\n", + " \"The item that appears most frequently in sequence.\"\n", + " [(item, count)] = Counter(seq).most_common(1)\n", + " return item\n", " \n", "################ Math Functions\n", " \n", @@ -188,6 +197,13 @@ " result *= n\n", " return result\n", "\n", + "import operator as op\n", + "\n", + "operations = {'>': op.gt, '>=': op.ge, '==': op.eq,\n", + " '<': op.lt, '<=': op.le, '!=': op.ne,\n", + " '+': op.add, '-': op.sub, '*': op.mul, \n", + " '/': op.truediv, '**': op.pow}\n", + "\n", "################ 2-D points implemented using (x, y) tuples\n", "\n", "def X(point): x, y = point; return x\n", @@ -264,7 +280,9 @@ { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -359,7 +377,9 @@ { "cell_type": "code", "execution_count": 4, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -388,7 +408,9 @@ { "cell_type": "code", "execution_count": 5, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -450,7 +472,9 @@ { "cell_type": "code", "execution_count": 7, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -477,7 +501,9 @@ { "cell_type": "code", "execution_count": 8, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -543,7 +569,9 @@ { "cell_type": "code", "execution_count": 10, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -598,7 +626,9 @@ { "cell_type": "code", "execution_count": 11, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -618,7 +648,9 @@ { "cell_type": "code", "execution_count": 12, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -663,7 +695,9 @@ { "cell_type": "code", "execution_count": 14, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -690,7 +724,9 @@ { "cell_type": "code", "execution_count": 15, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -719,7 +755,9 @@ { "cell_type": "code", "execution_count": 16, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -750,7 +788,9 @@ { "cell_type": "code", "execution_count": 17, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -775,7 +815,7 @@ "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--the top 20 took less than two minutes each." + "That was easy, but the leaders were three times faster than me." ] }, { @@ -790,7 +830,9 @@ { "cell_type": "code", "execution_count": 18, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -821,7 +863,9 @@ { "cell_type": "code", "execution_count": 19, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -860,7 +904,9 @@ { "cell_type": "code", "execution_count": 20, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "name": "stdout", @@ -914,7 +960,9 @@ { "cell_type": "code", "execution_count": 21, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -955,7 +1003,9 @@ { "cell_type": "code", "execution_count": 22, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "banks = vector('4\t10\t4\t1\t8\t4\t9\t14\t5\t1\t14\t15\t0\t15\t3\t5')\n", @@ -983,7 +1033,9 @@ { "cell_type": "code", "execution_count": 23, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -1003,7 +1055,9 @@ { "cell_type": "code", "execution_count": 24, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -1030,7 +1084,9 @@ { "cell_type": "code", "execution_count": 25, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -1057,7 +1113,9 @@ { "cell_type": "code", "execution_count": 26, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -1086,7 +1144,9 @@ { "cell_type": "code", "execution_count": 27, - "metadata": {}, + "metadata": { + "collapsed": false + }, "outputs": [ { "data": { @@ -1102,6 +1162,432 @@ "source": [ "realloc2(banks)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# [Day 7](https://adventofcode.com/2017/day/7): Memory Reallocation " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First I'll read the data into two dicts as follows: the input line:\n", + "\n", + " tcmdaji (40) -> wjbdxln, amtqhf\n", + " \n", + "creates:\n", + "\n", + " weight['tcmdaji'] = 40\n", + " above['tcmdaji'] = ['wjbdxln', 'amtqhf']" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def towers(lines):\n", + " \"Return (weight, above) dicts.\"\n", + " weight = {}\n", + " above = {}\n", + " for line in lines:\n", + " name, w, *rest = re.findall(r'\\w+', line)\n", + " weight[name] = int(w)\n", + " above[name] = rest\n", + " return weight, above\n", + "\n", + "weight, above = towers(Input(7))\n", + "\n", + "programs = set(above)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the root progam is the one that is not above anything:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'wiapj'}" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "programs - set(flatten(above.values()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part Two:**\n", + "\n", + "A program is *wrong* if it is the bottom of a tower that is a different weight from all its sibling towers:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def wrong(p): return tower_weight(p) not in map(tower_weight, siblings(p))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we define `tower_weight`, `siblings`, and the `below` dict:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def tower_weight(p): return weight[p] + sum(map(tower_weight, above[p]))\n", + "\n", + "def siblings(p): \n", + " \"The other programs at the same level as this one.\"\n", + " return [] if p not in below else [s for s in above[below[p]] if s != p]\n", + "\n", + "below = {a: b for b in programs for a in above[b]}" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'eionkb', 'lsire', 'wiapj', 'ycpcv'}" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "set(filter(wrong, programs))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So these four programs are wrong. Which one should we correct? The one that is wrong, and has no wrong program above it:" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'eionkb'" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def wrongest(programs):\n", + " return first(p for p in programs\n", + " if wrong(p) \n", + " and not any(wrong(p2) for p2 in above[p]))\n", + "\n", + "wrongest(programs) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now what should we correct it to? To the weight that makes it the same weight as sibling towers:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1072" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def correct(p):\n", + " \"Return the weight that would make p's tower's weight the same as its sibling towers.\"\n", + " delta = tower_weight(first(siblings(p))) - tower_weight(p) \n", + " return weight[p] + delta\n", + "\n", + "correct(wrongest(programs))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# [Day 8](https://adventofcode.com/2017/day/8): Memory Reallocation \n", + "\n", + "This one looks easy: a simple interpreter for straight-line code where each instruction has 7 tokens. It is nice that my `array` function parses the whole program." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6828" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "program = array(Input(8))\n", + "\n", + "def run8(program):\n", + " \"Run the program and return final value of registers.\"\n", + " registers = defaultdict(int)\n", + " for (r, inc, delta, _if, r2, cmp, amount) in program:\n", + " if operations[cmp](registers[r2], amount):\n", + " registers[r] += delta * (+1 if inc == 'inc' else -1)\n", + " return registers\n", + "\n", + "max(run8(program).values())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part Two:**\n", + "\n", + "Here I modify the interpreter to keep track of the highest value of any register at any time." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7234" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def run82(program):\n", + " registers = defaultdict(int)\n", + " highest = 0\n", + " for r, inc, delta, _if, r2, cmp, amount in program:\n", + " if operations[cmp](registers[r2], amount):\n", + " registers[r] += delta * (+1 if inc == 'inc' else -1)\n", + " highest = max(highest, registers[r])\n", + " return highest\n", + "\n", + "run82(program)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# [Day 9](https://adventofcode.com/2017/day/9): Stream Processing\n", + "\n", + "For this problem I could have a complex finite-state machine that handles all the characters `'{}'`, but I think it is easier to clean up the garbage first:" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'{{{{{{{},},{{},}},{{{{}},{{{{{}}},{}},},{{{{},{,{{{}}}}},},{{{}},{{}}}}},{{,{}},{{},},{}}}},{{{{{}}},{}},{{{{},},{}},{{{}},{{{},},{}},{}},{{}}},{{,},{{{}},{{{{}},{{{},},{}}},{{{{},},{}}},{{{}},{{}}}}}}},{{{},{{}},{{{{},{}}},}},{{{}},{}},{{},{{},{}}},{{{}},{{}}}}},{{{{},{}}},{{},{{{{},{}},{{{},{{}}}}}},{{}}},{},{{{}}}},{{{{},{{{},{{},{}}}},{{{{},{}},{{}}}}}},{{{},{{{{}},{{},{}}},{{{,},{{}}},{{}},{{}}},{{}},{{{{},{},{{}}},{{{{}},{{{{{},{}}},{{}}},{,{}},{}},{}},{},{{{},{}}}},{{{{{},},{}},{,},{{}}},{,},{{{},},{{}}}},{{,},{{{{}},{}},{}},{{{},{}}}}},{{{},},{{},{{}}}},{},{{{{{},{}},{},{{}}},{,},{{},}},{{},{}},{{{}},{}}}}},{{{},},{{},{,{{,{}},}}}}},{},{{},{{},{}}},{{},{,},{,{}}}}},{{{{{{},{}}},{,{}},{{},{,{{}}}}},{{},{}}},{{{}},{{},{}},{{}}},{{{},{{},{}}},{{,}},{{,{}},{{{}},{}},{,{,{}}}}},{{{},{}}}},{{{{{}}},{}},{{{},{{{}},{}},{{},}},{{{},},{{,{}}},{{},{{},{}}}},{{{{}}}},{{{,},{}},{},{{},{{{},},{}}},{{{{{}},{{{},{{},}},}},{{},{}},{,{{},{}}}},{{,{}},{{,{}},}},{{,{}}}}}}}},{{{{{},},{{{{},{}}}}},{{{},},{{,{}},{{},{,{}}}}},{{{},{{{{}}},{}}},{{},{},{,}},{{},{{},}},{{{{}},{}},{{},{}},{{{{}}},{}}}},{{{}},{{},{}}}},{{{{{{},{}},{}},{{,},},{{,}}}},{},{},{{{,{}},{{},{}},{{{}},}},{{},{,},{}},{{{},{}}},{{{{}},},{{},},{{}}}}},{{{{{}},{,},{}},{{},{{{{{},},{{{},{}}}},{{},{{},}}},{},{{{{},{}},{}}}}}},{{},{{},{}}},{{{}},{{},},{,{{}}}}}},{{},{{{},{{}},{,{,}}},{{{}}},{{{{},{}},}}},{{{{,{}},{{,{}},{}}},{{{,{,}},{,}},{}},{{{{{},{}}},{{},{}},{{},{{{},}}}},{{{}}},{{{},{{},{}}}}},{}},{{}}},{{{,{{},{}}},{{},}},{{{}},{}},{{{},{}},{{{{}},{{},},{{{{}},{},{{{,{}},{}}}}}}},{,}},{{},{{{},{}},}}}},{{{{{{,{}},{{}},{{}}},{{{{{},{}},{{},{}},{{}}},{{}},{}},{{},},{,{}}},{{}},{{{}},{,},{,{}}}},{{{{}}},{{{,{}}},{{{}},{,}},{{},{{{}},{{},}},{{{}},{{},}},{{{,},{{{},{}},{}},{}},{{{}},{{},}},{{{{{{{},}}}},{{{,{{}}},{}},{,},{{{{}}},{{}}}},{{{}},{,{{},{}}}}}}}}},{{{}},{{},{}}},{{,},{,{,{}}}}},{{{}}},{{},{{}},{,}}},{{{{{{{},},{},{{{,{}},{}},}}},{},{{{}}}},{{,},{{,{}}},{{{},},}},{{{}},{{{,}},{}},{{}}}},{{},{,{}},{{},{{}}}},{{}},{{{},{{},},{}},{{}},{{},{}}}},{{{{}},{{},}},{{{{}},{{},{}}},{}},{{{{}},{},{{{{,{}}},{}}}},{{,{}},{{{{{},},{{{{}}},{,{}},{{}}}},{,},{}},{{}}},{{},}},{{{{},{{,{}},}},{{}},{{},}}},{{{},{{,{}}}},{{{},},},{{}}}}}},{{{{},},{{}}},{{{{{},{}}},{{,}}},{}},{{},{},{}}},{{{{{}},{{}},{}},{{},{{},{{{}},{{,},{{{},{}},{}},{{},}},{}},{{{{},{{{}},{{}}}}}},{{{},{,{{}}}},{{{,}}},{{{{{{},},{{{}},}},{}},{{}},{{{{}},{,{}}},}},{{{,}},{{{{}}},{},{{},}},{{{,{}},{,{}}},{{{}},{{{{},{}}},{{{{,},},}}},{{}}},{}}},{{},{{,{{},}},{{},}},{{{},{}},}},{{{},{}},{,{}},{,}}},{{{{},{{,{}}}}},{{}},{,}}}},{{,},{{},{}},{}},{{{{{{},{}},{}},{{{{},{}},{{}}},{,{{},{}}},{{},{,}}}},{{},{}}},{,},{{{},{}},{{},{}}}}},{{}},{{{{{{}},{{},{}},{{{},{{}}},{}}}},{{{{{},{{}}},{}}}},{{{}},{},{,{}}},{{{{}},{}},{{},{{{{},{{}}}}},{{{},{{}},{,{}}}}}}}}},{{{{{}},},{},{}},{}}},{{},{{{,{}},{,},{{}}},{}},{{,{}},{{{},},{}},{{{}},}},{{{{{}},},{{{,{}},},{,},{{{}},}},{}},{{{{}},{}},{{}}}}}},{{{{{,{}},{},{}},{{}}}},{{{,},{{{},{}}},{{{{},{{{},}}},{}},{{{},},{{,}}},{,{}}}},{{{{},{}},},{{,{{}}}},{{},{}}},{{{},}}},{},{{{{},},{{{}},{}},{{{{{}},{{}}},{}}}},{{{,},{{},{}}},{{},{{}}}},{{{},{{},{}}},{{{},}},{{{}},{},{,{}}},{{{},{{},{}},{,{}}}}}}}}\\n'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text1 = Input(9).read() # Read text\n", + "text2 = re.sub(r'!.', '', text1) # Delete canceled characters\n", + "text3 = re.sub(r'<.*?>', '', text2) # Delete garbage\n", + "text3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now I can deal with nested braces (which can't be handled with regular expressions):" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9662" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def total_score(text):\n", + " \"Total of group scores; each group scores one more than the group it is nested in.\"\n", + " total = 0\n", + " outer = [0] # Stack of scores of groups nested outside current group\n", + " for c in text:\n", + " if c == '{':\n", + " score = outer[-1] + 1\n", + " total += score\n", + " outer.append(score)\n", + " elif c == '}':\n", + " outer.pop()\n", + " return total\n", + "\n", + "total_score(text3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part Two:**\n", + "\n", + "At first I thought that the amount of garbage is just the difference in lengths of `text2` and `text3`:" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5989" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(text2) - len(text3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But that turned out to be wrong; that counts all of `'<...>'`, whereas I'm not suppossed to count the opening and closing angle brackets, just the `'...'` in the middle. So that would be:" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4903" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text4 = re.sub(r'<.*?>', '<>', text2) # Delete inner garbage\n", + "\n", + "len(text2) - len(text4)" + ] } ], "metadata": { @@ -1120,7 +1606,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.3" + "version": "3.6.0" } }, "nbformat": 4,