{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Advent of Code - Day 8: Handheld Halting\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part One\n", "\n", "Your flight to the major airline hub reaches cruising altitude without incident.\n", "While you consider checking the in-flight menu for one of those drinks that come\n", "with a little umbrella, you are interrupted by the kid sitting next to you.\n", "\n", "Their\n", "[handheld game console](https://en.wikipedia.org/wiki/Handheld_game_console)\n", "won't turn on! They ask if you can take a look.\n", "\n", "You narrow the problem down to a strange infinite loop in the boot code (your\n", "puzzle input) of the device. You should be able to fix it, but first you need to\n", "be able to run the code in isolation.\n", "\n", "The boot code is represented as a text file with one instruction per line of\n", "text. Each instruction consists of an operation (acc, jmp, or nop) and an\n", "argument (a signed number like +4 or -20).\n", "\n", "- `acc` increases or decreases a single global value called the accumulator by\n", " the value given in the argument. For example, acc +7 would increase the\n", " accumulator by 7. The accumulator starts at 0. After an acc instruction, the\n", " instruction immediately below it is executed next.\n", "- `jmp` jumps to a new instruction relative to itself. The next instruction to\n", " execute is found using the argument as an offset from the jmp instruction; for\n", " example, jmp +2 would skip the next instruction, jmp +1 would continue to the\n", " instruction immediately below it, and jmp -20 would cause the instruction 20\n", " lines above to be executed next.\n", "- `nop` stands for No OPeration - it does nothing. The instruction immediately\n", " below it is executed next.\n", "\n", "For example, consider the following program:\n", "\n", "```\n", "nop +0\n", "acc +1\n", "jmp +4\n", "acc +3\n", "jmp -3\n", "acc -99\n", "acc +1\n", "jmp -4\n", "acc +6\n", "```\n", "\n", "These instructions are visited in this order:\n", "\n", "```\n", "nop +0 | 1\n", "acc +1 | 2, 8(!)\n", "jmp +4 | 3\n", "acc +3 | 6\n", "jmp -3 | 7\n", "acc -99 |\n", "acc +1 | 4\n", "jmp -4 | 5\n", "acc +6 |\n", "```\n", "\n", "First, the nop +0 does nothing. Then, the accumulator is increased from 0 to 1\n", "(acc +1) and jmp +4 sets the next instruction to the other acc +1 near the\n", "bottom. After it increases the accumulator from 1 to 2, jmp -4 executes, setting\n", "the next instruction to the only acc +3. It sets the accumulator to 5, and jmp\n", "-3 causes the program to continue back at the first acc +1.\n", "\n", "This is an infinite loop: with this sequence of jumps, the program will run\n", "forever. The moment the program tries to run any instruction a second time, you\n", "know it will never terminate.\n", "\n", "Immediately before the program would run an instruction a second time, the value\n", "in the accumulator is 5.\n", "\n", "Run your copy of the boot code. Immediately before any instruction is executed a\n", "second time, **what value is in the accumulator?**\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load and Clean Input data\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{note} The input data can be found\n", "[here](https://adventofcode.com/2020/day/8). :::\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "656\n", "[Instruction(operation='acc', argument=-7), Instruction(operation='acc', argument=2)]\n" ] } ], "source": [ "from collections import namedtuple\n", "\n", "\n", "def parse_instruction(instruction):\n", " a, b = instruction.strip().split(\" \")\n", " return Instruction(a, int(b))\n", "\n", "\n", "with open(\"../../data/advent-of-code/2020/day-8-input\") as fid:\n", " data = fid.readlines()\n", " Instruction = namedtuple(\"Instruction\", [\"operation\", \"argument\"])\n", " data = [parse_instruction(x) for x in data]\n", "\n", "print(len(data))\n", "print(data[0:2])" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Instruction(operation='nop', argument=0),\n", " Instruction(operation='acc', argument=1),\n", " Instruction(operation='jmp', argument=4),\n", " Instruction(operation='acc', argument=3),\n", " Instruction(operation='jmp', argument=-3),\n", " Instruction(operation='acc', argument=-99),\n", " Instruction(operation='acc', argument=1),\n", " Instruction(operation='jmp', argument=-4),\n", " Instruction(operation='acc', argument=6)]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "test_data = \"\"\"\n", "nop +0\n", "acc +1\n", "jmp +4\n", "acc +3\n", "jmp -3\n", "acc -99\n", "acc +1\n", "jmp -4\n", "acc +6\n", "\"\"\".strip().split(\n", " \"\\n\"\n", ")\n", "\n", "clean_test_data = [parse_instruction(instruction) for instruction in test_data]\n", "clean_test_data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Solution\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def find_program_status(instructions):\n", " accumulator, pointer = 0, 0\n", " visited = set()\n", " terminated = False\n", "\n", " while not terminated and pointer not in visited:\n", " instruction = instructions[pointer]\n", " visited.add(pointer)\n", "\n", " if instruction.operation == \"nop\":\n", " pointer += 1\n", " elif instruction.operation == \"jmp\":\n", " pointer += instruction.argument\n", "\n", " elif instruction.operation == \"acc\":\n", " pointer += 1\n", " accumulator += instruction.argument\n", "\n", " terminated = pointer == len(instructions)\n", "\n", " return accumulator, terminated" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(5, False)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "accumulator, terminated = find_program_status(clean_test_data)\n", "accumulator, terminated" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1594, False)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "accumulator, terminated = find_program_status(data)\n", "accumulator, terminated" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part Two\n", "\n", "After some careful analysis, you believe **that exactly one instruction is\n", "corrupted**.\n", "\n", "Somewhere in the program, either a `jmp` is supposed to be a `nop`, or a `nop`\n", "is supposed to be a `jmp`. (No `acc` instructions were harmed in the corruption\n", "of this boot code.)\n", "\n", "The program is supposed to terminate by attempting to execute an instruction\n", "immediately after the last instruction in the file. By changing exactly one\n", "`jmp` or `nop`, you can repair the boot code and make it terminate correctly.\n", "\n", "For example, consider the same program from above:\n", "\n", "```\n", "nop +0\n", "acc +1\n", "jmp +4\n", "acc +3\n", "jmp -3\n", "acc -99\n", "acc +1\n", "jmp -4\n", "acc +6\n", "```\n", "\n", "If you change the first instruction from `nop +0` to `jmp +0`, it would create a\n", "single-instruction infinite loop, never leaving that instruction. If you change\n", "almost any of the jmp instructions, the program will still eventually find\n", "another jmp instruction and loop forever.\n", "\n", "However, if you change the second-to-last instruction (from `jmp -4` to\n", "`nop -4`), the program terminates! The instructions are visited in this order:\n", "\n", "```\n", "nop +0 | 1\n", "acc +1 | 2\n", "jmp +4 | 3\n", "acc +3 |\n", "jmp -3 |\n", "acc -99 |\n", "acc +1 | 4\n", "nop -4 | 5\n", "acc +6 | 6\n", "```\n", "\n", "After the last instruction (`acc +6`), the program terminates by attempting to\n", "run the instruction below the last instruction in the file. With this change,\n", "after the program terminates, the accumulator contains the value 8 (`acc +1`,\n", "`acc +1`, `acc +6`).\n", "\n", "Fix the program so that it terminates normally by changing exactly one `jmp` (to\n", "`nop`) or `nop` (to `jmp`). **What is the value of the accumulator after the\n", "program terminates?**\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Solution\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import copy\n", "\n", "\n", "def fix_program(data):\n", " instructions = copy.deepcopy(data)\n", " flip = (\n", " lambda x: x._replace(operation=\"jmp\")\n", " if x.operation == \"nop\"\n", " else x._replace(operation=\"nop\")\n", " )\n", " for idx, instruction in enumerate(instructions):\n", " if instruction.operation == \"nop\" or instruction.operation == \"jmp\":\n", " previous = instruction\n", " instructions[idx] = flip(instruction)\n", " accumulator, terminated = find_program_status(instructions)\n", " if terminated:\n", " break\n", " instructions[idx] = previous\n", " return accumulator\n", "\n", "\n", "fix_program(clean_test_data)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The value of the accumulator after the program terminates is: 758\n" ] } ], "source": [ "print(f\"The value of the accumulator after the program terminates is: {fix_program(data)}\")" ] } ], "metadata": { "author": "Anderson Banihirwe", "date": "2020-12-08", "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.8.8" }, "tags": "python,adventofcode", "title": "Advent of Code - Day 8: Handheld Halting", "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }