diff --git a/ipynb/Advent 2017.ipynb b/ipynb/Advent 2017.ipynb index 3087c8e..8609645 100644 --- a/ipynb/Advent 2017.ipynb +++ b/ipynb/Advent 2017.ipynb @@ -784,7 +784,7 @@ "source": [ "# [Day 5](https://adventofcode.com/2017/day/5): A Maze of Twisty Trampolines, All Alike\n", "\n", - "Let's first make sure we can read the data okay:" + "Let's first make sure we can read the data/program okay:" ] }, { @@ -795,7 +795,7 @@ { "data": { "text/plain": [ - "[0, 2, 0, 0, -2, -2, -1, -4, -5, -6]" + "(0, 2, 0, 0, -2, -2, -1, -4, -5, -6)" ] }, "execution_count": 18, @@ -804,17 +804,17 @@ } ], "source": [ - "def jumps(): return [int(x) for x in Input(5)]\n", + "program = mapt(int, Input(5))\n", "\n", - "jumps()[:10]" + "program[:10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now I'll make a little interpreter, `run`, which takes a list, `M` for memory, as input,\n", - "and maintains a program counter `pc`, and does the incrementing as described in the puzzle,\n", + "Now I'll make a little interpreter, `run`, which takes a program, loads it into memory,\n", + " and executes the instruction, maintaining a program counter, `pc`, and doing the incrementing/branching as described in the puzzle,\n", "until the program counter is out of range:" ] }, @@ -835,16 +835,17 @@ } ], "source": [ - "def run(M):\n", - " pc = 0\n", - " for steps in count_from(1):\n", + "def run(program):\n", + " memory = list(program)\n", + " pc = steps = 0\n", + " while pc in range(len(memory)):\n", + " steps += 1\n", " oldpc = pc\n", - " pc += M[pc]\n", - " M[oldpc] += 1\n", - " if pc not in range(len(M)):\n", - " return steps\n", + " pc += memory[pc]\n", + " memory[oldpc] += 1\n", + " return steps\n", " \n", - "run(jumps())" + "run(program)" ] }, { @@ -853,7 +854,7 @@ "source": [ "**Part Two:**\n", "\n", - "Part Two seems tricky, so I'll include an optional argument, `verbose`, to print out info as we go, to make sure I've got it right on a small example:" + "Part Two seems tricky, so I'll include an optional argument, `verbose`, and check if the printout it produces matches the example in the puzzle description:" ] }, { @@ -889,15 +890,16 @@ } ], "source": [ - "def run2(M, verbose=False):\n", - " pc = 0\n", - " for steps in count_from(1):\n", + "def run2(program, verbose=False):\n", + " memory = list(program)\n", + " pc = steps = 0\n", + " while pc in range(len(memory)):\n", + " steps += 1\n", " oldpc = pc\n", - " pc += M[pc]\n", - " M[oldpc] += (-1 if M[oldpc] >= 3 else 1)\n", - " if verbose: print(steps, pc, M)\n", - " if pc not in range(len(M)):\n", - " return steps\n", + " pc += memory[pc]\n", + " memory[oldpc] += (-1 if memory[oldpc] >= 3 else 1)\n", + " if verbose: print(steps, pc, memory)\n", + " return steps\n", " \n", "run2([0, 3, 0, 1, -3], True)" ] @@ -926,7 +928,179 @@ } ], "source": [ - "run2(jumps())" + "run2(program)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Thanks to [Clement Sreeves](https://github.com/ClementSreeves) for the suggestion of making a distinction between the `program` and the `memory`. In my first version, `run` would mutate the argument, which was OK for a short exercise, but not best practice for a reliable API." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# [Day 6](https://adventofcode.com/2017/day/6): Memory Reallocation " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I had to read the puzzle description carefully, but then it is pretty clear what to do. I'll keep a set of previously seen configurations, which will all be tuples. But in the function `spread`, I want to mutate the configuration of banks, so I will convert to a list at the start, then convert back to a tuple at the end." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "banks = vector('4\t10\t4\t1\t8\t4\t9\t14\t5\t1\t14\t15\t0\t15\t3\t5')\n", + "\n", + "def realloc(banks):\n", + " \"How many cycles until we reach a configuration we've seen before?\"\n", + " seen = {banks}\n", + " for cycles in count_from(1):\n", + " banks = spread(banks)\n", + " if banks in seen:\n", + " return cycles\n", + " seen.add(banks)\n", + " \n", + "def spread(banks):\n", + " \"Find the area with the most blocks, and spread them evenly to following areas.\"\n", + " banks = list(banks)\n", + " maxi = max(range(len(banks)), key=lambda i: banks[i])\n", + " blocks = banks[maxi]\n", + " banks[maxi] = 0\n", + " for i in range(maxi + 1, maxi + 1 + blocks):\n", + " banks[i % len(banks)] += 1\n", + " return tuple(banks)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 4, 1, 2)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spread((0, 2, 7, 0))" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "realloc((0, 2, 7, 0))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These tests look good; let's solve the problem:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "12841" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "realloc(banks)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Part Two:** Here I will just replace the `set` of `seen` banks with a `dict` of `{bank: cycle_number}`; everything else is the same, and the final result is the current cycle number minus the cycle number of the previously-seen tuple of banks." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def realloc2(banks):\n", + " \"When we hit a cycle, what is the length of the cycle?\"\n", + " seen = {banks: 0}\n", + " for cycles in count_from(1):\n", + " banks = spread(banks)\n", + " if banks in seen:\n", + " return cycles - seen[banks]\n", + " seen[banks] = cycles\n", + "\n", + "realloc2((0, 2, 7, 0))" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "8038" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "realloc2(banks)" ] } ],