\n",
@@ -2834,9 +2826,9 @@
" /* Instantiate the Animation class. */\n",
" /* The IDs given should match those used in the template above. */\n",
" (function() {\n",
- " var img_id = \"_anim_imgae252cea00e945ac938aee942061fc97\";\n",
- " var slider_id = \"_anim_sliderae252cea00e945ac938aee942061fc97\";\n",
- " var loop_select_id = \"_anim_loop_selectae252cea00e945ac938aee942061fc97\";\n",
+ " var img_id = \"_anim_img1e0a1734c1b64f26ba0a7f3051a934a3\";\n",
+ " var slider_id = \"_anim_slider1e0a1734c1b64f26ba0a7f3051a934a3\";\n",
+ " var loop_select_id = \"_anim_loop_select1e0a1734c1b64f26ba0a7f3051a934a3\";\n",
" var frames = new Array(3);\n",
" \n",
" frames[0] = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA8AAAALQCAYAAABfdxm0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90\\\n",
@@ -5608,14 +5600,14 @@
" /* set a timeout to make sure all the above elements are created before\n",
" the object is initialized. */\n",
" setTimeout(function() {\n",
- " animae252cea00e945ac938aee942061fc97 = new Animation(frames, img_id, slider_id, 200.0,\n",
+ " anim1e0a1734c1b64f26ba0a7f3051a934a3 = new Animation(frames, img_id, slider_id, 200.0,\n",
" loop_select_id);\n",
" }, 0);\n",
" })()\n",
"\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 140,
@@ -5848,42 +5840,42 @@
"\n",
"\n",
"
\n",
@@ -5893,9 +5885,9 @@
" /* Instantiate the Animation class. */\n",
" /* The IDs given should match those used in the template above. */\n",
" (function() {\n",
- " var img_id = \"_anim_imge46cbc345c6d4e34aba519ec8cb115f1\";\n",
- " var slider_id = \"_anim_slidere46cbc345c6d4e34aba519ec8cb115f1\";\n",
- " var loop_select_id = \"_anim_loop_selecte46cbc345c6d4e34aba519ec8cb115f1\";\n",
+ " var img_id = \"_anim_img4e5b0d0e6fe24e0ea5f0bb1cec42557f\";\n",
+ " var slider_id = \"_anim_slider4e5b0d0e6fe24e0ea5f0bb1cec42557f\";\n",
+ " var loop_select_id = \"_anim_loop_select4e5b0d0e6fe24e0ea5f0bb1cec42557f\";\n",
" var frames = new Array(1);\n",
" \n",
" frames[0] = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA8AAAALQCAYAAABfdxm0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90\\\n",
@@ -6573,14 +6565,14 @@
" /* set a timeout to make sure all the above elements are created before\n",
" the object is initialized. */\n",
" setTimeout(function() {\n",
- " anime46cbc345c6d4e34aba519ec8cb115f1 = new Animation(frames, img_id, slider_id, 200.0,\n",
+ " anim4e5b0d0e6fe24e0ea5f0bb1cec42557f = new Animation(frames, img_id, slider_id, 200.0,\n",
" loop_select_id);\n",
" }, 0);\n",
" })()\n",
"\n"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 143,
@@ -6612,7 +6604,7 @@
{
"data": {
"text/plain": [
- "Puzzle 14.2: 1.8776 seconds, answer 6876 ok"
+ "Puzzle 14.2: 1.8645 seconds, answer 6876 ok"
]
},
"execution_count": 145,
@@ -6731,7 +6723,7 @@
{
"data": {
"text/plain": [
- "Puzzle 15.1: .0241 seconds, answer 1563092 ok"
+ "Puzzle 15.1: .0248 seconds, answer 1563092 ok"
]
},
"execution_count": 150,
@@ -6808,7 +6800,7 @@
{
"data": {
"text/plain": [
- "Puzzle 15.1: .0296 seconds, answer 1563092 ok"
+ "Puzzle 15.1: .0299 seconds, answer 1563092 ok"
]
},
"execution_count": 153,
@@ -6830,7 +6822,7 @@
{
"data": {
"text/plain": [
- "Puzzle 15.2: .0418 seconds, answer 1582688 ok"
+ "Puzzle 15.2: .0415 seconds, answer 1582688 ok"
]
},
"execution_count": 154,
@@ -6987,7 +6979,7 @@
{
"data": {
"text/plain": [
- "Puzzle 16.1: .1477 seconds, answer 103512 ok"
+ "Puzzle 16.1: .1473 seconds, answer 103512 ok"
]
},
"execution_count": 162,
@@ -7051,7 +7043,7 @@
{
"data": {
"text/plain": [
- "Puzzle 16.2: .8603 seconds, answer 554 ok"
+ "Puzzle 16.2: .8496 seconds, answer 554 ok"
]
},
"execution_count": 165,
@@ -7101,6 +7093,16 @@
"()\n",
"('Program', 2, 4, 1, 7, 7, 5, 0, 3, 4, 4, 1, 7, 5, 5, 3, 0)\n"
]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "Computer(A=52042868, B=0, C=0, prog=[2, 4, 1, 7, 7, 5, 0, 3, 4, 4, 1, 7, 5, 5, 3, 0])"
+ ]
+ },
+ "execution_count": 167,
+ "metadata": {},
+ "output_type": "execute_result"
}
],
"source": [
@@ -7115,27 +7117,7 @@
" case ['Program', *vals]: kwds['prog'] = vals\n",
" return Computer(**kwds)\n",
" \n",
- "computer = initialize(parse(17, atoms))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 168,
- "id": "68cecfae-c690-45f1-8a23-94733624a774",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "Computer(A=52042868, B=0, C=0, prog=[2, 4, 1, 7, 7, 5, 0, 3, 4, 4, 1, 7, 5, 5, 3, 0])"
- ]
- },
- "execution_count": 168,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
+ "computer = initialize(parse(17, atoms))\n",
"computer"
]
},
@@ -7151,7 +7133,7 @@
},
{
"cell_type": "code",
- "execution_count": 170,
+ "execution_count": 169,
"id": "d98f88cc-c435-43fc-bcb5-52e6dd70fdb1",
"metadata": {},
"outputs": [],
@@ -7160,21 +7142,20 @@
" \"\"\"Run the program on the computer, yielding each output.\"\"\"\n",
" A, B, C, prog = computer\n",
" pc = 0\n",
- " def combo(v): return A if v == 4 else B if v == 5 else C if v == 6 else v\n",
" while pc < len(prog):\n",
" op, val = prog[pc:pc+2]\n",
" pc += 2\n",
+ " combo = (A if val == 4 else B if val == 5 else C if val == 6 else val)\n",
" match op:\n",
- " case 0: A = A // (2 ** combo(val))\n",
- " case 6: B = A // (2 ** combo(val))\n",
- " case 7: C = A // (2 ** combo(val))\n",
+ " case 0: A = A // (2 ** combo)\n",
+ " case 6: B = A // (2 ** combo)\n",
+ " case 7: C = A // (2 ** combo)\n",
" case 1: B = B ^ val\n",
" case 4: B = B ^ C\n",
- " case 2: B = combo(val) % 8\n",
- " case 5: yield combo(val) % 8\n",
+ " case 2: B = combo % 8\n",
+ " case 5: yield combo % 8\n",
" case 3: \n",
- " if A != 0:\n",
- " pc = val"
+ " if A: pc = val"
]
},
{
@@ -7187,7 +7168,7 @@
},
{
"cell_type": "code",
- "execution_count": 172,
+ "execution_count": 171,
"id": "860f24e5-92ad-4361-8920-102ebc573598",
"metadata": {},
"outputs": [
@@ -7197,7 +7178,7 @@
"Puzzle 17.1: .0000 seconds, answer 2,1,0,1,7,2,5,0,3 ok"
]
},
- "execution_count": 172,
+ "execution_count": 171,
"metadata": {},
"output_type": "execute_result"
}
@@ -7214,19 +7195,53 @@
"source": [
"### Part 2: What is the lowest positive initial value for register A that causes the program to output a copy of itself?\n",
"\n",
- "In Part 2, we find that register A has been corrupted, and we need to restore it to the value that will make the program output a copy of itself. I was afraid of this! In past years there were problems where you had to actually understand what a program was doing in order to answer; you couldn't use brute force. I'll try looking at some possible values, but I'm pretty sure it won't work. Another day I'll try to figure it out."
+ "In Part 2, we find that register A has been corrupted, and we need to restore it to the value that will make the program output a copy of itself (a [Quine](https://en.wikipedia.org/wiki/Quine_%28computing%29)). I was afraid of this! AoC always has a problem where you have to write an interpreter for a program in some obscure language, but then in Part 2 you have to actually understand what the program is doing; you can't use brute force. (I tried brute force up to A=10,000,000 with no luck.)\n",
+ "\n",
+ "To try to understand my program, here it is in pseudocode:\n",
+ "\n",
+ " top: B = A % 8 # 2, 4\n",
+ " B = B ^ 7 # 1, 7\n",
+ " C = A / 2 ** B # 7, 5\n",
+ " A = A / 2 ** 3 # 0, 3\n",
+ " B = B ^ C # 4, 4\n",
+ " B = B ^ 7 # 1, 7\n",
+ " output B # 5, 5\n",
+ " if A: goto top # 3, 0\n",
+ "\n",
+ "I can summarize that as:\n",
+ "\n",
+ " top: B and C are defined in terms of the last octal digit of A, and prior value of B\n",
+ " A is shifted to eliminate the last octal digit\n",
+ " output B \n",
+ " if A: goto top \n",
+ "\n",
+ "So I realized that one octal digit of `A` is eliminated on each pass through the loop, and when `A` hits zero, we exit. Each pass outputs one octal digit, so `A` in octal has to be the same length as my program; somewhere in the ten trillion range. Good thing I gave up on brute force. \n",
+ "\n",
+ "I realized that I should go right-to-left, outputing one octal digit at a time, and appending one octal digit at a time to `A`. After some trial and error I got the following:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 173,
+ "id": "bb745303-dd20-486f-bbdd-7ae77f995c2c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def run_with(computer=computer, **kwds) -> Ints: \n",
+ " \"\"\"Run the program with registers set to the given values.\"\"\"\n",
+ " return list(run_program(computer._replace(**kwds)))"
]
},
{
"cell_type": "code",
"execution_count": 174,
- "id": "809bcd1b-e428-4449-971e-0bc63aca08fb",
+ "id": "54ac9b9c-70d9-4356-91c5-40fb285634b4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "'No answer!'"
+ "[2, 4, 1, 7, 7, 5, 0, 3, 4, 4, 1, 7, 5, 5, 3, 0]"
]
},
"execution_count": 174,
@@ -7235,8 +7250,279 @@
}
],
"source": [
- "first(i for i in range(100_000)\n",
- " if list(run_program(computer._replace(A=i))) == computer.prog) or 'No answer!'"
+ "computer.prog"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 175,
+ "id": "36d89d9a-c8fc-41be-820b-cb13e40793c0",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[0]"
+ ]
+ },
+ "execution_count": 175,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "run_with(A=0o7)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 176,
+ "id": "892fef38-5f9b-4370-a242-f7a10df5487b",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[3, 0]"
+ ]
+ },
+ "execution_count": 176,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "run_with(A=0o72)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 177,
+ "id": "b3d11d5e-30c2-419f-bc11-3f26fbdddfbb",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[5, 3, 0]"
+ ]
+ },
+ "execution_count": 177,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "run_with(A=0o726)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f40f775f-bb9d-459c-80c3-ac09b6b42904",
+ "metadata": {},
+ "source": [
+ "That is, my program ends in `5,3,0`, and an octal `7` outputs a `0`; an octal `0o72` outputs `3,0`, and octal `0o726` outputs `5,3,0`. So here's my approach for finding the Quine program:\n",
+ "- I'm going to keerp a set of candidate values for `A` as the set `As`.\n",
+ "- The set starts with just `{0}`.\n",
+ "- On each iteration I try appending each octal digit to each element of the set `As`.\n",
+ "- I keep the candidate `A` values whose output matches the tail of the program's output.\n",
+ "- Iterate this for each digit and return the set of `A` vcalues that produce the whole program.\n",
+ "- Take the minium of the set."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 179,
+ "id": "a49c6de9-4e6b-47e5-bcf0-2972a95c1af3",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Puzzle 17.2: .0232 seconds, answer 267265166222235 ok"
+ ]
+ },
+ "execution_count": 179,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "def quine(computer) -> Set[int]:\n",
+ " \"\"\"Find the values of `A` that cause the output to match the program.\"\"\"\n",
+ " As = {0}\n",
+ " for d in reversed(range(len(computer.prog))):\n",
+ " tail = computer.prog[d:]\n",
+ " candidates = {(A << 3) + i for A in As for i in range(8)}\n",
+ " As = {A for A in candidates if run_with(A=A) == tail}\n",
+ " return As\n",
+ "\n",
+ "answer(17.2, 267265166222235, \n",
+ " lambda: min(quine(computer)))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c7f9e900-45be-401f-a6f2-a1d7b751fae6",
+ "metadata": {},
+ "source": [
+ "# [Day 18](https://adventofcode.com/2024/day/18): RAM Run\n",
+ "\n",
+ "In today's narrative, we're inside a computer, on a 2D memory board, and bytes are falling down, at specified (x, y) positions, as given in our input:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 181,
+ "id": "d14e1966-2feb-4553-9a0a-12595ef4f7d7",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "────────────────────────────────────────────────────────────────────────────────────────────────────\n",
+ "Puzzle input ➜ 3450 strs:\n",
+ "────────────────────────────────────────────────────────────────────────────────────────────────────\n",
+ "40,65\n",
+ "17,1\n",
+ "34,45\n",
+ "31,51\n",
+ "29,43\n",
+ "25,9\n",
+ "14,27\n",
+ "5,29\n",
+ "...\n",
+ "────────────────────────────────────────────────────────────────────────────────────────────────────\n",
+ "Parsed representation ➜ 3450 tuples:\n",
+ "────────────────────────────────────────────────────────────────────────────────────────────────────\n",
+ "(40, 65)\n",
+ "(17, 1)\n",
+ "(34, 45)\n",
+ "(31, 51)\n",
+ "(29, 43)\n",
+ "(25, 9)\n",
+ "(14, 27)\n",
+ "(5, 29)\n",
+ "...\n"
+ ]
+ }
+ ],
+ "source": [
+ "falling_bytes = parse(18, ints)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1229cec7-a456-4dd6-a668-8a00591c63f7",
+ "metadata": {},
+ "source": [
+ "### Part 1: What is the minimum number of steps needed to reach the exit?\n",
+ "\n",
+ "When a byte falls it creates a barrier. Our task is to find a path that avoids the barriers, from the start in the upper left to the exit in the lower right of a 71 x 71 grid that is the memory board. \n",
+ "\n",
+ "This is another search problem, like the maze in Day 16, but without the complications; this time we can take a step in any of the four cardinal directions at unit cost. The problem description says that we should first consider just the first kilobyte (1024 bytes), and I was worried that if I just hand those points to my `Grid` class, it wouldn't cover the whole 71 x 71 grid. Therefore, I created a grid with empty spaces, and then updated with the falling bytes. The function `memory_path` returns a path, and we can then ask for its length to get the answer."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 183,
+ "id": "83af4751-38c9-4830-a2fa-78515b59bc97",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def memory_path(falling_bytes: Tuple[Point], width=71, height=71) -> Grid:\n",
+ " \"\"\"Make a Grid of the given size with the points as obstacles.\"\"\"\n",
+ " grid = Grid(['.' * width] * height)\n",
+ " grid.update({p: '#' for p in falling_bytes})\n",
+ " problem = GridProblem(grid=grid, initial=(0, 0), goal=sub(grid.size, (1, 1)))\n",
+ " return A_star_search(problem)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 184,
+ "id": "29da25e2-f3c2-43e3-8769-1d4fcecb807b",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Puzzle 18.1: .0138 seconds, answer 344 ok"
+ ]
+ },
+ "execution_count": 184,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "answer(18.1, 344, lambda:\n",
+ " len(memory_path(falling_bytes[:1024])))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "792bc31e-964d-47fe-a09b-6a95724e641e",
+ "metadata": {},
+ "source": [
+ "### Part 2: What are the coordinates of the first byte that will prevent the exit from being reachable from your starting position?\n",
+ "\n",
+ "After 1024 bytes fall there is a path from start to exit, but as more bytes fall we might have to switch to a different path, and eventually there may be no path. We're asked for the first byte position that blocks the last remaining path. I can think of three ways to handle this:\n",
+ "1) Add falling bytes one at a time and repeat the A-star search each time. **Slow!**\n",
+ "2) Add falling bytes in binary search fashion: We know adding no bytes is good for getting a path and adding all of them is bad; try half way and then update good or bad depending on whether we found a path. **Pretty fast.**\n",
+ "3) Optimize (2) by checking which of the falling bytes intersects with the current path; if *n* bytes are ok, and byte *n*+1 is not on the path for *n*, then *n*+1 is ok. Might be a bit faster, but in my opinion not worth the code complexity. We could also incrementally add and remove "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 186,
+ "id": "4c0a8dcb-c3af-45e7-9273-8776e8c3ea1d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def memory_blocker(falling_bytes) -> Point:\n",
+ " \"\"\"Which falling byte is the first to block a path to the exit? Do binary search.\"\"\"\n",
+ " good = 0\n",
+ " bad = len(falling_bytes) - 1\n",
+ " while bad - good > 1:\n",
+ " mid = (good + bad) // 2\n",
+ " path = memory_path(falling_bytes[:mid + 1])\n",
+ " if path == search_failure:\n",
+ " bad = mid\n",
+ " else:\n",
+ " good = mid\n",
+ " return falling_bytes[bad]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 187,
+ "id": "22371144-5d51-440a-918f-a63de73b13ad",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Puzzle 18.2: .0348 seconds, answer 46,18 ok"
+ ]
+ },
+ "execution_count": 187,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "answer(18.2, '46,18', lambda:\n",
+ " cat(memory_blocker(falling_bytes), ','))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "552f1c6e-052e-43d0-b13f-e8f66f274d63",
+ "metadata": {},
+ "source": [
+ "I admit I initially had an off-by-one **bug** here. I wanted `bad` to be an index into the falling bytes, but it is also natural to have it be the end of a range; I was confused about my choice."
]
},
{
@@ -7246,12 +7532,12 @@
"source": [
"# Summary\n",
"\n",
- "So far, I've solved all the puzzles except 17.2. Most of them run in well under a second (the median is less than a hundreth of a second), but four of them take over a second."
+ "So far, I've solved all the puzzles. Most of them run in well under a second (the median is less than a hundreth of a second), but four of them take over a second."
]
},
{
"cell_type": "code",
- "execution_count": 176,
+ "execution_count": 190,
"id": "34813fc9-a000-4cd8-88ae-692851b3242c",
"metadata": {},
"outputs": [
@@ -7259,43 +7545,46 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "Puzzle 1.1: .0002 seconds, answer 1830467 ok\n",
- "Puzzle 1.2: .0001 seconds, answer 26674158 ok\n",
- "Puzzle 2.1: .0004 seconds, answer 257 ok\n",
- "Puzzle 2.2: .0021 seconds, answer 328 ok\n",
- "Puzzle 3.1: .0006 seconds, answer 156388521 ok\n",
- "Puzzle 3.2: .0004 seconds, answer 75920122 ok\n",
- "Puzzle 4.1: .0328 seconds, answer 2401 ok\n",
- "Puzzle 4.2: .0272 seconds, answer 1822 ok\n",
- "Puzzle 5.1: .0007 seconds, answer 5762 ok\n",
- "Puzzle 5.2: .0009 seconds, answer 4130 ok\n",
- "Puzzle 6.1: .0016 seconds, answer 5329 ok\n",
- "Puzzle 6.2: 1.9934 seconds, answer 2162 ok\n",
- "Puzzle 7.1: .0214 seconds, answer 1985268524462 ok\n",
- "Puzzle 7.2: 1.0915 seconds, answer 150077710195188 ok\n",
- "Puzzle 8.1: .0029 seconds, answer 220 ok\n",
- "Puzzle 8.2: .0031 seconds, answer 813 ok\n",
- "Puzzle 9.1: .0193 seconds, answer 6332189866718 ok\n",
- "Puzzle 9.2: 2.7416 seconds, answer 6353648390778 ok\n",
- "Puzzle 10.1: .0047 seconds, answer 744 ok\n",
- "Puzzle 10.2: .0059 seconds, answer 1651 ok\n",
- "Puzzle 11.1: .0016 seconds, answer 194482 ok\n",
- "Puzzle 11.2: .0595 seconds, answer 232454623677743 ok\n",
- "Puzzle 12.1: .0307 seconds, answer 1402544 ok\n",
- "Puzzle 12.2: .0434 seconds, answer 862486 ok\n",
- "Puzzle 13.1: .0002 seconds, answer 29598 ok\n",
- "Puzzle 13.2: .0004 seconds, answer 93217456941970 ok\n",
- "Puzzle 14.1: .0001 seconds, answer 216027840 ok\n",
- "Puzzle 14.2: 1.8776 seconds, answer 6876 ok\n",
- "Puzzle 15.1: .0296 seconds, answer 1563092 ok\n",
- "Puzzle 15.2: .0418 seconds, answer 1582688 ok\n",
- "Puzzle 16.1: .1477 seconds, answer 103512 ok\n",
- "Puzzle 16.2: .8603 seconds, answer 554 ok\n",
+ "Puzzle 1.1: .0002 seconds, answer 1830467 ok\n",
+ "Puzzle 1.2: .0001 seconds, answer 26674158 ok\n",
+ "Puzzle 2.1: .0004 seconds, answer 257 ok\n",
+ "Puzzle 2.2: .0023 seconds, answer 328 ok\n",
+ "Puzzle 3.1: .0006 seconds, answer 156388521 ok\n",
+ "Puzzle 3.2: .0005 seconds, answer 75920122 ok\n",
+ "Puzzle 4.1: .0328 seconds, answer 2401 ok\n",
+ "Puzzle 4.2: .0268 seconds, answer 1822 ok\n",
+ "Puzzle 5.1: .0007 seconds, answer 5762 ok\n",
+ "Puzzle 5.2: .0008 seconds, answer 4130 ok\n",
+ "Puzzle 6.1: .0014 seconds, answer 5329 ok\n",
+ "Puzzle 6.2: 1.9643 seconds, answer 2162 ok\n",
+ "Puzzle 7.1: .0213 seconds, answer 1985268524462 ok\n",
+ "Puzzle 7.2: 1.0804 seconds, answer 150077710195188 ok\n",
+ "Puzzle 8.1: .0026 seconds, answer 220 ok\n",
+ "Puzzle 8.2: .0030 seconds, answer 813 ok\n",
+ "Puzzle 9.1: .0197 seconds, answer 6332189866718 ok\n",
+ "Puzzle 9.2: 2.7519 seconds, answer 6353648390778 ok\n",
+ "Puzzle 10.1: .0045 seconds, answer 744 ok\n",
+ "Puzzle 10.2: .0062 seconds, answer 1651 ok\n",
+ "Puzzle 11.1: .0015 seconds, answer 194482 ok\n",
+ "Puzzle 11.2: .0599 seconds, answer 232454623677743 ok\n",
+ "Puzzle 12.1: .0307 seconds, answer 1402544 ok\n",
+ "Puzzle 12.2: .0430 seconds, answer 862486 ok\n",
+ "Puzzle 13.1: .0002 seconds, answer 29598 ok\n",
+ "Puzzle 13.2: .0004 seconds, answer 93217456941970 ok\n",
+ "Puzzle 14.1: .0001 seconds, answer 216027840 ok\n",
+ "Puzzle 14.2: 1.8645 seconds, answer 6876 ok\n",
+ "Puzzle 15.1: .0299 seconds, answer 1563092 ok\n",
+ "Puzzle 15.2: .0415 seconds, answer 1582688 ok\n",
+ "Puzzle 16.1: .1473 seconds, answer 103512 ok\n",
+ "Puzzle 16.2: .8496 seconds, answer 554 ok\n",
"Puzzle 17.1: .0000 seconds, answer 2,1,0,1,7,2,5,0,3 ok\n",
+ "Puzzle 17.2: .0232 seconds, answer 267265166222235 ok\n",
+ "Puzzle 18.1: .0138 seconds, answer 344 ok\n",
+ "Puzzle 18.2: .0348 seconds, answer 46,18 ok\n",
"\n",
- "Correct: 33/33\n",
+ "Correct: 36/36\n",
"\n",
- "Time in seconds: 0.0047 median, 0.2740 mean, 9.0435 total.\n"
+ "Time in seconds: 0.0100 median, 0.2517 mean, 9.0607 total.\n"
]
}
],
diff --git a/ipynb/AdventUtils.ipynb b/ipynb/AdventUtils.ipynb
index 936a0d1..6a18489 100644
--- a/ipynb/AdventUtils.ipynb
+++ b/ipynb/AdventUtils.ipynb
@@ -198,7 +198,7 @@
" comment = (f'' if self.got == unknown else\n",
" f' ok' if self.ok else \n",
" f' WRONG; expected answer is {self.solution}')\n",
- " return f'Puzzle {self.puzzle:4.1f}: {secs} seconds, answer {self.got:<15}{comment}'\n",
+ " return f'Puzzle {self.puzzle:4.1f}: {secs} seconds, answer {self.got:<17}{comment}'\n",
"\n",
"def summary(answers):\n",
" \"\"\"Print a report that summarizes the answers.\"\"\"\n",
@@ -554,7 +554,8 @@
" def neighbors(self, point) -> List[Point]:\n",
" \"\"\"Points on the grid that neighbor `point`.\"\"\"\n",
" return [add2(point, Δ) for Δ in self.directions \n",
- " if add2(point, Δ) in self or self.default not in (KeyError, None)]\n",
+ " if (add2(point, Δ) in self) \n",
+ " or (self.default not in (KeyError, None))]\n",
" \n",
" def neighbor_contents(self, point) -> Iterable:\n",
" \"\"\"The contents of the neighboring points.\"\"\"\n",
@@ -664,8 +665,7 @@
"class GridProblem(SearchProblem):\n",
" \"\"\"Problem for searching a grid from a start to a goal location.\n",
" A state is just an (x, y) location in the grid.\"\"\"\n",
- " def actions(self, loc): return self.grid.neighbors(loc)\n",
- " def result(self, loc1, loc2): return loc2\n",
+ " def actions(self, pos): return [p for p in self.grid.neighbors(pos) if self.grid[pos] != '#']\n",
" def h(self, node): return taxi_distance(node.state, self.goal) \n",
"\n",
"class Node:\n",