diff --git a/ipynb/Advent-2024.ipynb b/ipynb/Advent-2024.ipynb index 09defd4..7450c45 100644 --- a/ipynb/Advent-2024.ipynb +++ b/ipynb/Advent-2024.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "ed82ed5b-a42d-468b-8f6e-288d3c2de20b", "metadata": {}, "outputs": [ @@ -68,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "22e5d621-a152-4712-866f-f8b962b5dd14", "metadata": {}, "outputs": [ @@ -121,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "2dbfa3ae-3d47-4711-8821-7d1b2564bdc8", "metadata": {}, "outputs": [ @@ -131,7 +131,7 @@ "Puzzle 1.1: .0002 seconds, answer 1830467 ok" ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -157,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "id": "e33f9705-3b51-4314-a302-6b3445290713", "metadata": {}, "outputs": [ @@ -167,7 +167,7 @@ "Puzzle 1.2: .0001 seconds, answer 26674158 ok" ] }, - "execution_count": 10, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -194,7 +194,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "id": "10e1ab83-a6ec-4143-ad9a-eaae220adcde", "metadata": {}, "outputs": [ @@ -245,7 +245,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "id": "368cbe1c-b6b6-4a82-bef9-599ee9725899", "metadata": {}, "outputs": [], @@ -265,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 14, "id": "e662bf10-4d6a-40f1-95ce-dfc39f5b3fc2", "metadata": {}, "outputs": [ @@ -275,7 +275,7 @@ "Puzzle 2.1: .0004 seconds, answer 257 ok" ] }, - "execution_count": 15, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -297,7 +297,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 16, "id": "67ba1d53-95b7-4811-b225-2ff15d6bdc5c", "metadata": {}, "outputs": [], @@ -315,17 +315,17 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 17, "id": "d1b9ffb5-af7a-465f-a063-c31df2d0605c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 2.2: .0023 seconds, answer 328 ok" + "Puzzle 2.2: .0021 seconds, answer 328 ok" ] }, - "execution_count": 18, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -347,7 +347,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 19, "id": "78080200-0f9f-4492-9bee-c936737ee96f", "metadata": {}, "outputs": [ @@ -386,7 +386,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 21, "id": "bf6366b1-6952-47d8-8b3c-09f8d05ec093", "metadata": {}, "outputs": [], @@ -400,17 +400,17 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 22, "id": "2032c903-5f23-4c16-ba68-410b6c1750e1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 3.1: .0006 seconds, answer 156388521 ok" + "Puzzle 3.1: .0005 seconds, answer 156388521 ok" ] }, - "execution_count": 23, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -432,7 +432,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 24, "id": "4525d01a-bac0-41c2-92b8-baf0fd395e88", "metadata": {}, "outputs": [], @@ -444,7 +444,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 25, "id": "ce40f258-ca76-48c3-9965-27a6979a4243", "metadata": {}, "outputs": [ @@ -454,7 +454,7 @@ "Puzzle 3.2: .0004 seconds, answer 75920122 ok" ] }, - "execution_count": 26, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -476,7 +476,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 27, "id": "a0d903b9-018e-4861-9314-cafed59055fd", "metadata": {}, "outputs": [ @@ -515,7 +515,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 29, "id": "72d48abb-7a82-452f-b91d-838b3836a90f", "metadata": {}, "outputs": [], @@ -534,17 +534,17 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 30, "id": "6175362b-d8b4-45d1-b70c-d8575a0fe188", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 4.1: .0338 seconds, answer 2401 ok" + "Puzzle 4.1: .0331 seconds, answer 2401 ok" ] }, - "execution_count": 31, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -572,17 +572,17 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 32, "id": "ff7540fd-b5cb-4d02-810d-5c77da2bd9f4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 4.2: .0274 seconds, answer 1822 ok" + "Puzzle 4.2: .0260 seconds, answer 1822 ok" ] }, - "execution_count": 33, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -617,7 +617,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 34, "id": "b77a5a1f-a43b-4ce8-a60c-94d69a595505", "metadata": {}, "outputs": [ @@ -672,7 +672,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 35, "id": "4c85a23e-686a-4129-a14c-ff6f6a88b9ac", "metadata": {}, "outputs": [], @@ -693,7 +693,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 37, "id": "78898d37-46ff-4367-9d89-b2a107a90aa1", "metadata": {}, "outputs": [], @@ -711,7 +711,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 38, "id": "b1c87359-1d2d-4a90-8305-9d152ce5d547", "metadata": {}, "outputs": [ @@ -721,7 +721,7 @@ "Puzzle 5.1: .0007 seconds, answer 5762 ok" ] }, - "execution_count": 39, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -744,7 +744,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 40, "id": "d8718c3e-0b3b-49ce-8cca-abd82aa788d7", "metadata": {}, "outputs": [ @@ -754,7 +754,7 @@ "23" ] }, - "execution_count": 41, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -777,7 +777,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 42, "id": "7222dc1c-067f-4bb5-84e1-3c2fc72fd53a", "metadata": {}, "outputs": [], @@ -796,7 +796,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 43, "id": "dc1fbda9-2cfd-442a-afef-12c9b0d2b17f", "metadata": {}, "outputs": [ @@ -806,7 +806,7 @@ "Puzzle 5.2: .0009 seconds, answer 4130 ok" ] }, - "execution_count": 44, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -836,7 +836,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 46, "id": "6ec71cf8-c43d-457e-8e14-0e9eb99b956a", "metadata": {}, "outputs": [ @@ -878,7 +878,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 48, "id": "95f0b409-a6d6-47bc-8ce5-1c2df80f2b18", "metadata": {}, "outputs": [], @@ -896,17 +896,17 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 49, "id": "f4be3d1f-7f24-4d55-8221-df0026178e1e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 6.1: .0015 seconds, answer 5329 ok" + "Puzzle 6.1: .0017 seconds, answer 5329 ok" ] }, - "execution_count": 50, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -936,7 +936,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 51, "id": "1718fecb-aa3e-4162-9948-1c06d4ec5e8a", "metadata": {}, "outputs": [], @@ -966,17 +966,17 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 52, "id": "36196264-eb33-4fc0-95d5-06c985105ebf", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 6.2: 2.0299 seconds, answer 2162 ok" + "Puzzle 6.2: 1.9609 seconds, answer 2162 ok" ] }, - "execution_count": 53, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1006,7 +1006,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 55, "id": "c1c6cee8-122c-43c9-8c7d-ed8980ea2b76", "metadata": {}, "outputs": [ @@ -1067,7 +1067,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 58, "id": "6fa3907c-0e1a-4d4a-9fc3-f809b9325674", "metadata": {}, "outputs": [ @@ -1077,7 +1077,7 @@ "13" ] }, - "execution_count": 59, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -1096,11 +1096,19 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 60, "id": "5dfe0edf-cf29-4623-bb2c-6180f832f4d7", "metadata": {}, "outputs": [], "source": [ + "def can_be_calibrated(numbers: ints, operators=(operator.add, operator.mul)) -> bool:\n", + " \"\"\"Can the tuple of numbers be calibrated as a correct equation using '+' and '*' ?\"\"\"\n", + " target, first, *rest = numbers\n", + " results = {first} # A set of all possible results of the partial computation\n", + " for y in rest:\n", + " results = {op(x, y) for x in results if x <= target for op in operators}\n", + " return target in results\n", + "\n", "def can_be_calibrated(numbers: ints, operators=(operator.add, operator.mul)) -> bool:\n", " \"\"\"Can the tuple of numbers be calibrated as a correct equation using '+' and '*' ?\"\"\"\n", " target, first, *rest = numbers\n", @@ -1112,17 +1120,17 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 61, "id": "3085596d-f5ec-4ba8-b05a-cf70cf276a0c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 7.1: .0217 seconds, answer 1985268524462 ok" + "Puzzle 7.1: .0214 seconds, answer 1985268524462 ok" ] }, - "execution_count": 62, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -1144,17 +1152,17 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 63, "id": "5bdcb999-7f38-4814-bca5-13db88f4e214", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 7.2: 1.1304 seconds, answer 150077710195188 ok" + "Puzzle 7.2: 1.0813 seconds, answer 150077710195188 ok" ] }, - "execution_count": 64, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -1178,7 +1186,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 65, "id": "cf6361a7-e3bc-42ec-ae16-f9eec166055e", "metadata": {}, "outputs": [ @@ -1219,7 +1227,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 67, "id": "22180ce8-5d03-4aee-8c73-62f2afbddf71", "metadata": {}, "outputs": [], @@ -1240,17 +1248,17 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 68, "id": "dd173ce9-cbbb-4282-b43f-c7cff662bd90", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 8.1: .0027 seconds, answer 220 ok" + "Puzzle 8.1: .0028 seconds, answer 220 ok" ] }, - "execution_count": 69, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -1274,7 +1282,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 70, "id": "d30f8ce9-f186-46a0-a2e7-f74eceae6905", "metadata": {}, "outputs": [], @@ -1295,17 +1303,17 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 71, "id": "6bf85b57-8b8f-4196-9903-6d5fe082f404", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 8.1: .0028 seconds, answer 220 ok" + "Puzzle 8.1: .0027 seconds, answer 220 ok" ] }, - "execution_count": 72, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -1317,17 +1325,17 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 72, "id": "f232952c-5fc6-4696-a8b1-d0b54137ac02", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 8.2: .0030 seconds, answer 813 ok" + "Puzzle 8.2: .0031 seconds, answer 813 ok" ] }, - "execution_count": 73, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -1357,7 +1365,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 75, "id": "0e944f9e-5c16-440c-b12e-178058a87048", "metadata": {}, "outputs": [ @@ -1398,7 +1406,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 77, "id": "76e8454d-a2f3-4b6b-92df-182116cf46e0", "metadata": {}, "outputs": [], @@ -1430,17 +1438,17 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 78, "id": "2aa7e2b9-844e-49ed-b41b-4a4cecff86b7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 9.1: .0209 seconds, answer 6332189866718 ok" + "Puzzle 9.1: .0196 seconds, answer 6332189866718 ok" ] }, - "execution_count": 79, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -1468,7 +1476,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 80, "id": "fcf4d832-3d7d-4987-aa57-e6e0f1df16bf", "metadata": {}, "outputs": [], @@ -1510,17 +1518,17 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 81, "id": "e3036875-88d0-496e-9d2f-facd0e80a5b2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 9.2: 2.8353 seconds, answer 6353648390778 ok" + "Puzzle 9.2: 2.7301 seconds, answer 6353648390778 ok" ] }, - "execution_count": 82, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } @@ -1550,7 +1558,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 84, "id": "5804fb03-05f3-402f-b6cc-6804c5d22512", "metadata": {}, "outputs": [ @@ -1603,7 +1611,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 86, "id": "76b5379e-ee19-4607-91b8-88ec7b38023f", "metadata": {}, "outputs": [], @@ -1619,7 +1627,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 87, "id": "97cf05f7-fa56-4a90-b2d8-2cd4d9b81f95", "metadata": {}, "outputs": [ @@ -1629,7 +1637,7 @@ "Puzzle 10.1: .0049 seconds, answer 744 ok" ] }, - "execution_count": 88, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -1653,7 +1661,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 89, "id": "b763450f-a565-4936-bee4-e531c2eeebdb", "metadata": {}, "outputs": [], @@ -1670,17 +1678,17 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 90, "id": "f8a87032-6556-4fc9-9bb8-573611aee8dc", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 10.2: .0060 seconds, answer 1651 ok" + "Puzzle 10.2: .0059 seconds, answer 1651 ok" ] }, - "execution_count": 91, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -1702,7 +1710,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 92, "id": "4b35defa-a19e-46c5-bd04-3af55bea14e4", "metadata": {}, "outputs": [ @@ -1742,7 +1750,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 94, "id": "76b68cef-d8de-4145-b65c-b254fedf1671", "metadata": {}, "outputs": [ @@ -1786,7 +1794,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 96, "id": "1513df56-3d6f-42cf-8aec-1bdbeb991d90", "metadata": {}, "outputs": [], @@ -1812,17 +1820,17 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 97, "id": "eff17cd0-a2c7-4d69-bc55-c0ef97917915", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 11.1: .0673 seconds, answer 194482 ok" + "Puzzle 11.1: .0690 seconds, answer 194482 ok" ] }, - "execution_count": 98, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -1848,7 +1856,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 99, "id": "707b5a97-0296-48df-bdab-e34064cc67c2", "metadata": {}, "outputs": [], @@ -1873,17 +1881,17 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 101, "id": "efdcdbf8-e8ec-4a85-9d09-90a20e08c66a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 11.1: .0015 seconds, answer 194482 ok" + "Puzzle 11.1: .0016 seconds, answer 194482 ok" ] }, - "execution_count": 102, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -1895,17 +1903,17 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 102, "id": "657b1f13-ffcc-44c6-84f1-398fa2fcdac7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 11.2: .0608 seconds, answer 232454623677743 ok" + "Puzzle 11.2: .0606 seconds, answer 232454623677743 ok" ] }, - "execution_count": 103, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -1935,7 +1943,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 105, "id": "8161ee7e-76e3-499a-abf8-a607991c9602", "metadata": {}, "outputs": [ @@ -1974,7 +1982,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 107, "id": "79f91f38-e325-44f2-9e53-b64ce12d9d35", "metadata": {}, "outputs": [], @@ -2003,7 +2011,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 109, "id": "1fbabbfb-50c8-4197-8517-e7cee9582765", "metadata": {}, "outputs": [], @@ -2027,17 +2035,17 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 110, "id": "cdaf655b-d12c-4973-b19b-3132e5e691c6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 12.1: .0336 seconds, answer 1402544 ok" + "Puzzle 12.1: .0330 seconds, answer 1402544 ok" ] }, - "execution_count": 111, + "execution_count": 110, "metadata": {}, "output_type": "execute_result" } @@ -2077,7 +2085,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 112, "id": "38c30e15-3a33-40c2-b734-163a15af7a8a", "metadata": {}, "outputs": [], @@ -2100,17 +2108,17 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 113, "id": "72175812-dcd0-4f1b-9efa-0dceeeafa609", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 12.1: .0310 seconds, answer 1402544 ok" + "Puzzle 12.1: .0302 seconds, answer 1402544 ok" ] }, - "execution_count": 114, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } @@ -2122,17 +2130,17 @@ }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 114, "id": "9defcd35-91bc-41d4-a16f-bb7a4ede75e7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 12.2: .0434 seconds, answer 862486 ok" + "Puzzle 12.2: .0424 seconds, answer 862486 ok" ] }, - "execution_count": 115, + "execution_count": 114, "metadata": {}, "output_type": "execute_result" } @@ -2154,7 +2162,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 116, "id": "e78f45c0-c420-4661-aad2-14e122b4473b", "metadata": {}, "outputs": [ @@ -2218,7 +2226,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 118, "id": "c2c4bbc9-42cd-483d-8da2-97cf051e93fe", "metadata": {}, "outputs": [], @@ -2242,7 +2250,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 119, "id": "f5638ed4-1e59-4b9f-b1fc-427d2eb0d036", "metadata": {}, "outputs": [ @@ -2252,7 +2260,7 @@ "Puzzle 13.1: .0101 seconds, answer 29598 ok" ] }, - "execution_count": 120, + "execution_count": 119, "metadata": {}, "output_type": "execute_result" } @@ -2289,7 +2297,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 121, "id": "df8da2ae-52f9-409b-a54f-ad7d21b32e45", "metadata": {}, "outputs": [ @@ -2299,7 +2307,7 @@ "Counter({0: 168, 1: 152})" ] }, - "execution_count": 122, + "execution_count": 121, "metadata": {}, "output_type": "execute_result" } @@ -2328,7 +2336,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 123, "id": "6bbd0934-d962-4c93-940b-810651e9e568", "metadata": {}, "outputs": [], @@ -2352,7 +2360,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 125, "id": "dd38ba4c-44ba-426b-b1c8-0e10adbdd642", "metadata": {}, "outputs": [], @@ -2364,7 +2372,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 126, "id": "9f578b3e-6b6d-4eb0-9228-c98122a84747", "metadata": {}, "outputs": [ @@ -2374,7 +2382,7 @@ "Puzzle 13.2: .0004 seconds, answer 93217456941970 ok" ] }, - "execution_count": 127, + "execution_count": 126, "metadata": {}, "output_type": "execute_result" } @@ -2394,7 +2402,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 128, "id": "609ed4ce-548c-4af4-8e09-c621aca0124e", "metadata": {}, "outputs": [ @@ -2404,7 +2412,7 @@ "Puzzle 13.1: .0002 seconds, answer 29598 ok" ] }, - "execution_count": 129, + "execution_count": 128, "metadata": {}, "output_type": "execute_result" } @@ -2426,7 +2434,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 130, "id": "1a5f5875-426d-47ea-a35a-405c39ced5dd", "metadata": {}, "outputs": [ @@ -2477,7 +2485,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 132, "id": "5910f30b-3e81-46ac-a206-fabb7f4f6330", "metadata": {}, "outputs": [ @@ -2487,7 +2495,7 @@ "Puzzle 14.1: .0001 seconds, answer 216027840 ok" ] }, - "execution_count": 133, + "execution_count": 132, "metadata": {}, "output_type": "execute_result" } @@ -2529,7 +2537,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 134, "id": "664c686e-0c3d-43b8-970f-88c0bf47dbf6", "metadata": {}, "outputs": [], @@ -2561,7 +2569,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 136, "id": "87843969-cb37-4fa5-9788-6a1b71c43521", "metadata": {}, "outputs": [ @@ -2753,42 +2761,42 @@ "\n", "\n", "
\n", - " \n", + " \n", "
\n", - " \n", + " oninput=\"animb8d76c3afca3479ab94aa63ee8986acf.set_frame(parseInt(this.value));\">\n", "
\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", "
\n", - "
\n", - " \n", - " \n", - " Once\n", + " \n", - " \n", - " Loop\n", + " \n", - " \n", + " \n", "
\n", "
\n", "
\n", @@ -2798,9 +2806,9 @@ " /* Instantiate the Animation class. */\n", " /* The IDs given should match those used in the template above. */\n", " (function() {\n", - " var img_id = \"_anim_img8017f102d95940dd8bea1432c0130b83\";\n", - " var slider_id = \"_anim_slider8017f102d95940dd8bea1432c0130b83\";\n", - " var loop_select_id = \"_anim_loop_select8017f102d95940dd8bea1432c0130b83\";\n", + " var img_id = \"_anim_imgb8d76c3afca3479ab94aa63ee8986acf\";\n", + " var slider_id = \"_anim_sliderb8d76c3afca3479ab94aa63ee8986acf\";\n", + " var loop_select_id = \"_anim_loop_selectb8d76c3afca3479ab94aa63ee8986acf\";\n", " var frames = new Array(3);\n", " \n", " frames[0] = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA8AAAALQCAYAAABfdxm0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90\\\n", @@ -5572,17 +5580,17 @@ " /* set a timeout to make sure all the above elements are created before\n", " the object is initialized. */\n", " setTimeout(function() {\n", - " anim8017f102d95940dd8bea1432c0130b83 = new Animation(frames, img_id, slider_id, 200.0,\n", + " animb8d76c3afca3479ab94aa63ee8986acf = new Animation(frames, img_id, slider_id, 200.0,\n", " loop_select_id);\n", " }, 0);\n", " })()\n", "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 137, + "execution_count": 136, "metadata": {}, "output_type": "execute_result" } @@ -5603,7 +5611,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 138, "id": "9563d49c-54a3-439f-a833-f48c2a070609", "metadata": {}, "outputs": [], @@ -5620,7 +5628,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 139, "id": "ab8c7e3b-f400-4976-ad0d-5f92cbadec02", "metadata": {}, "outputs": [ @@ -5812,42 +5820,42 @@ "\n", "\n", "
\n", - " \n", + " \n", "
\n", - " \n", + " oninput=\"anim4e94c9293ab842dbbc3f168d91a7d500.set_frame(parseInt(this.value));\">\n", "
\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", "
\n", - "
\n", - " \n", - " \n", - " Once\n", + " \n", - " \n", - " Loop\n", + " \n", - " \n", + " \n", "
\n", "
\n", "
\n", @@ -5857,9 +5865,9 @@ " /* Instantiate the Animation class. */\n", " /* The IDs given should match those used in the template above. */\n", " (function() {\n", - " var img_id = \"_anim_imgf3ce28f81b944cba91930deeb91dbb03\";\n", - " var slider_id = \"_anim_sliderf3ce28f81b944cba91930deeb91dbb03\";\n", - " var loop_select_id = \"_anim_loop_selectf3ce28f81b944cba91930deeb91dbb03\";\n", + " var img_id = \"_anim_img4e94c9293ab842dbbc3f168d91a7d500\";\n", + " var slider_id = \"_anim_slider4e94c9293ab842dbbc3f168d91a7d500\";\n", + " var loop_select_id = \"_anim_loop_select4e94c9293ab842dbbc3f168d91a7d500\";\n", " var frames = new Array(1);\n", " \n", " frames[0] = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA8AAAALQCAYAAABfdxm0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90\\\n", @@ -6537,17 +6545,17 @@ " /* set a timeout to make sure all the above elements are created before\n", " the object is initialized. */\n", " setTimeout(function() {\n", - " animf3ce28f81b944cba91930deeb91dbb03 = new Animation(frames, img_id, slider_id, 200.0,\n", + " anim4e94c9293ab842dbbc3f168d91a7d500 = new Animation(frames, img_id, slider_id, 200.0,\n", " loop_select_id);\n", " }, 0);\n", " })()\n", "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 140, + "execution_count": 139, "metadata": {}, "output_type": "execute_result" } @@ -6569,17 +6577,17 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 141, "id": "75434bc8-35ae-4d8b-b747-01d773472541", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Puzzle 14.2: 1.8866 seconds, answer 6876 ok" + "Puzzle 14.2: 1.8721 seconds, answer 6876 ok" ] }, - "execution_count": 142, + "execution_count": 141, "metadata": {}, "output_type": "execute_result" } @@ -6589,6 +6597,399 @@ " most_clustered_time(robots, range(7000)))" ] }, + { + "cell_type": "markdown", + "id": "08cae29b-9b07-4e8d-b2d1-f3798f6c7cd0", + "metadata": {}, + "source": [ + "# [Day 15](https://adventofcode.com/2024/day/15): Warehouse Woes\n", + "\n", + "Today's input is in two parts: the first part is a grid map describing a warehouse with walls (`#`) and boxes (`O`) and one robot (`@`). The second part is a set of instructions for how the robot moves (arrows). I'll parse the two parts into paragraphs, then parse each paragraph:" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "id": "20be45ec-f6fc-472c-9b49-872b7334528c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "────────────────────────────────────────────────────────────────────────────────────────────────────\n", + "Puzzle input ➜ 71 strs:\n", + "────────────────────────────────────────────────────────────────────────────────────────────────────\n", + "##################################################\n", + "#..OO..O.O....O...O..O...O.......O...OO##....O.OO#\n", + "#O.#..#OOO#..O..OO...O.O.....OO..O#O#.OOO.....#..#\n", + "#.....O.......OO.#OO....#O.OO.O..OO.O.O.O..##....#\n", + "#.O....O...O#...#...OO..#..O........#O..#..O..O..#\n", + "#O....O...O.O..OO..OO..#OO.#OO.O......##..O..O...#\n", + "#..O.##..#O...O...#.#.O.O..O.#......O..#.#...O...#\n", + "#O.O.........O..O........OO....OO......O.....O#..#\n", + "...\n", + "────────────────────────────────────────────────────────────────────────────────────────────────────\n", + "Parsed representation ➜ 2 strs:\n", + "────────────────────────────────────────────────────────────────────────────────────────────────────\n", + "##################################################\n", + "#..OO..O.O....O...O..O...O.......O...OO##.... ...\n", + ">><^v><>^v<vv><<^^>>>>^^><>^<^^vvv>>><><>v^^^^<<^<^><>>>v><>>>^>><>v>v>^v>>v>>>v ...\n" + ] + } + ], + "source": [ + "warehouse_woes = parse(15, sections=paragraphs)\n", + "warehouse = Grid(parse(warehouse_woes[0]))\n", + "arrows = cat(parse(warehouse_woes[1]))" + ] + }, + { + "cell_type": "markdown", + "id": "9a4aed93-5205-4946-b04c-637f3b095e4b", + "metadata": {}, + "source": [ + "### Part 1: What is the sum of all boxes' GPS coordinates after moving?\n", + "\n", + "The robot attempts to follow each instruction arrow (for example, moving East when the instruction is `>`). The robot is strong enough to push a box or a line of boxes around; that's its job. However, if a push is blocked by a wall, then neither the robot nor any boxes will move.\n", + "\n", + "The **GPS coordinate** of a box at (*x*, *y*) is *x* + 100*y*.\n", + "\n", + "I'll implement `warehouse_movement` to make the moves on a copy of the warehouse. It works by finding all boxes that might move (and the robot) with `moveables`, then if they wouldn't push into a wall, they all move (as implemented in `make_movement`)." + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "id": "248d51c0-424e-4bbb-bbf4-37cdc18313e1", + "metadata": {}, + "outputs": [], + "source": [ + "def gps_coordinate(box: Point) -> int: return X_(box) + 100 * Y_(box)\n", + "\n", + "def warehouse_movement(warehouse: Grid, arrows: str) -> Grid:\n", + " \"\"\"Return a grid in which the robot has followed `arrows`, pushing boxes around.\"\"\"\n", + " grid = warehouse.copy()\n", + " robot_pos = the(grid.findall('@'))\n", + " for arrow in arrows:\n", + " dir = arrow_direction[arrow]\n", + " robot_and_boxes = moveables(grid, robot_pos, dir)\n", + " if make_movement(grid, robot_and_boxes, dir):\n", + " robot_pos = add2(robot_pos, dir)\n", + " return grid\n", + "\n", + "def moveables(grid, start_pos: Point, dir: Vector) -> List[Point]:\n", + " \"\"\"The positions of moveable things (the robot and maybe boxes) going in direction from the start.\"\"\"\n", + " def moveable(pos): return grid[pos] in ('@', 'O')\n", + " return list(takewhile(moveable, grid.follow_line(start_pos, dir)))\n", + "\n", + "def make_movement(grid, to_move: List[Point], dir: Vector) -> bool:\n", + " \"\"\"Try to move the objects in the `to_move` positions in direction `dir`; return True if they move.\"\"\"\n", + " if grid[add2(to_move[-1], dir)] == '#':\n", + " return False\n", + " else:\n", + " for p in reversed(to_move):\n", + " grid[add2(p, dir)] = grid[p]\n", + " grid[p] = '.'\n", + " return True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "id": "52e93218-6ac2-4cf7-aced-f4f3a3df77b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Puzzle 15.1: .0250 seconds, answer 1563092 ok" + ] + }, + "execution_count": 146, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "answer(15.1, 1563092, lambda:\n", + " sum(map(gps_coordinate, warehouse_movement(warehouse, arrows).findall('O'))))" + ] + }, + { + "cell_type": "markdown", + "id": "84dcd7fd-0f4a-4377-b61e-22bd5a0a51a9", + "metadata": {}, + "source": [ + "### Part 2: What is the sum of all boxes' final GPS coordinates on the double-wide grid?\n", + "\n", + "In Part 2, there is another warehouse that is similar to the first, but twice as wide. Each position in each row is replaced by two copies of the contents, except that the robot is replaced by `@.` and a box, `#`, is replaced by `[]`, indicating the two halves of a box. The rules for movement are the same, except that when the robot is moving North or South and is pushing a box, that box, since it is two positions wide, can push *two* boxes if they are lined up right. (Presumably, those two boxes could then push three boxes in the next row, and so on, although the problem description did not explicitly specify that). Finally, the GPS coordinates are taken for the `[` part of the box.\n", + "\n", + "So this is mostly the same as Part 1, but the criteria of what to move is different. Here are the changes I'll make:\n", + "- I'll need to make the double-wide grid, which I call `warehouse2`.\n", + "- I'll redefine `make_movement` to check for a wall in front of *any* of the boxes, not just the last box in a line, and to handle different characters for boxes.\n", + "- I'll redefine `moveables` to deal with double-wide boxes.\n", + "\n", + "The redefinition s are backwards-comnpatible, which I'll demonstrate by re-running Part 1." + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "id": "2d9afbc1-75b2-479d-81fc-fbab69d25753", + "metadata": {}, + "outputs": [], + "source": [ + "def doublewide(text: str) -> str:\n", + " \"\"\"Make the grid twice as wide.\"\"\"\n", + " return text.replace('#', '##').replace('O', '[]').replace('.', '..').replace('@', '@.')\n", + " \n", + "warehouse2 = Grid(parse(doublewide(warehouse_woes[0])))\n", + "\n", + "def make_movement(grid, to_move: Collection[Point], dir: Vector) -> bool:\n", + " \"\"\"Try to move the objects in the `to_move` positions in direction `dir`; return True if they move.\"\"\"\n", + " def go(p): return add2(p, dir)\n", + " if any(grid[go(p)] == '#' for p in to_move):\n", + " return False\n", + " else:\n", + " updates = {go(p): grid[p] for p in to_move}\n", + " grid.update({p: '.' for p in to_move})\n", + " grid.update(updates)\n", + " return True\n", + "\n", + "def moveables(grid, start_pos, dir) -> Set[Point]:\n", + " \"\"\"The positions of moveable things (robot and maybe boxes) going in direction from the start.\"\"\" \n", + " ahead = add2(start_pos, dir)\n", + " if dir in (East, West) or grid[ahead] == 'O': # Single line push\n", + " def moveable(pos): return grid[pos] in ('@', 'O', '[', ']')\n", + " return set(takewhile(moveable, grid.follow_line(start_pos, dir)))\n", + " else: # Potential non-linear push\n", + " results = {start_pos}\n", + " if grid[ahead] in ('[', ']'): results |= moveables(grid, ahead, dir) \n", + " if grid[ahead] == '[': results |= moveables(grid, add2(ahead, East), dir)\n", + " if grid[ahead] == ']': results |= moveables(grid, add2(ahead, West), dir)\n", + " return results" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "id": "7af49bbf-dd10-4221-9096-6f548dec44c0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Puzzle 15.1: .0292 seconds, answer 1563092 ok" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "answer(15.1, 1563092, lambda:\n", + " sum(map(gps_coordinate, warehouse_movement(warehouse, arrows).findall('O'))))" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "id": "99246602-a51e-41aa-a7e9-7cdbc8d449ca", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Puzzle 15.2: .0419 seconds, answer 1582688 ok" + ] + }, + "execution_count": 150, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "answer(15.2, 1582688, lambda:\n", + " sum(map(gps_coordinate, warehouse_movement(warehouse2, arrows).findall('['))))" + ] + }, + { + "cell_type": "markdown", + "id": "90cfbc5d-a701-4fc4-b46c-00fdcb78703b", + "metadata": {}, + "source": [ + "I had a frustrating time **debugging** this one; this was by far my worst performance of the year. First I had a copy/paste error in the two similar branches of `moveables2`; then I was mitakenly summing the GPS coordinates of both sides of the boxes when I should have been summing just the left sides." + ] + }, + { + "cell_type": "markdown", + "id": "9b4132fb-6365-4aed-bd11-aedf560933e1", + "metadata": {}, + "source": [ + "# [Day 16](https://adventofcode.com/2024/day/16): Reindeer Maze\n", + "\n", + "Today's input is yet another 2D grid, this one a maze that reindeer run through:" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "id": "03255fd5-95d9-4a90-b1bb-abdf6bbf1d85", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "────────────────────────────────────────────────────────────────────────────────────────────────────\n", + "Puzzle input ➜ 141 strs:\n", + "────────────────────────────────────────────────────────────────────────────────────────────────────\n", + "################################################################################################ ...\n", + "#.................#.................#.............#.......#...................#.......#......... ...\n", + "#.#.###############.#####.#####.###.#.#########.###.#.###.#.#################.#.#####.#.#.#####. ...\n", + "#.#.#.....#.........#...#.#...#.#.#.#.........#.....#.#...#.....#...#.........#.#.......#.#..... ...\n", + "###.#.###.#.#########.#.###.#.#.#.#.###.#############.#.###.###.#.###.#########.#.#####.#.###### ...\n", + "#...#...#...#...#...#.#.#...#...#.....#.#...#.........#...#...#.#.#...#.......#.#...#.....#..... ...\n", + "#.#####.#####.###.#.#.#.#.###.#######.###.#.#.###########.#####.#.#.###.#####.#.###.#.#.###.#### ...\n", + "#.......#...#...#.#.#.#...#...#...#.................................#...#...#...#...#.#.#.....#. ...\n", + "...\n" + ] + } + ], + "source": [ + "maze = Grid(parse(16))" + ] + }, + { + "cell_type": "markdown", + "id": "74e37bd1-f994-4e98-b6ed-8469ca7f3805", + "metadata": {}, + "source": [ + "### Part 1: What is the lowest score a Reindeer could possibly get?\n", + "\n", + "The race through the maze starts at the `S` position and ends at the `E`. A reindeer is allowed to take a step forward (as long as there isn't a `#` wall there) or to turn 90 degrees right or left (the reindeer is initially facing East). The **score** for a path is one point for every forward step and 1000 points for every turn.\n", + "\n", + "We're asked to find the path with the minimmum score. My [AdventUtils](AdventUtils.ipynb) notebook already contains an `A_star_search` function for finding least-cost paths, and a `GridProblem` class. By default, a `GridProblem` counts one point for a move in any direction, so we'll have to create a subclass, `MazeSearchProblem`. This is straightforward but a bit tedious; at first I had **two bugs**: I thought that the default `is_goal` and `h` methods would work, but they assume the state is just a position; we need the state to contain the facing direction as well." + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "id": "6f9aa64c-29c2-4b12-9ecf-4ed7a2efe9f9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Puzzle 16.1: .1464 seconds, answer 103512 ok" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "State = namedtuple('State', 'pos, facing')\n", + "\n", + "def reindeer_path(maze) -> Node:\n", + " \"\"\"The lowest-cost path through the maze.\"\"\"\n", + " start, end = the(maze.findall('S')), the(maze.findall('E'))\n", + " problem = MazeSearchProblem(grid=maze, initial=State(start, East), goal=end)\n", + " return A_star_search(problem)\n", + "\n", + "class MazeSearchProblem(GridProblem):\n", + " \"\"\"A GridProblem where a turn costs 1000 points, a step ahead 1.\"\"\"\n", + " \n", + " def actions(self, state):\n", + " ahead = add2(state.pos, state.facing)\n", + " return ['L', 'R'] + ([ahead] if self.grid[ahead] != '#' else [])\n", + " \n", + " def result(self, state, action) -> State:\n", + " if action in ('L', 'R'):\n", + " return State(state.pos, make_turn(state.facing, action))\n", + " else:\n", + " return State(add2(state.pos, state.facing), state.facing)\n", + " \n", + " def action_cost(self, s1, action, s2) -> int: return 1000 if action in ('L', 'R') else 1\n", + "\n", + " def is_goal(self, state) -> bool: return self.grid[state.pos] == 'E'\n", + "\n", + " def h(self, node) -> int: return taxi_distance(node.state.pos, self.goal)\n", + "\n", + "answer(16.1, 103512, lambda:\n", + " reindeer_path(maze).path_cost)" + ] + }, + { + "cell_type": "markdown", + "id": "030e5786-cd2a-405f-b4d4-460cefab7e32", + "metadata": {}, + "source": [ + "### Part 2: How many tiles are part of at least one of the best paths through the maze?\n", + "\n", + "In Part 2 we're asked to find the total number of positions that are part of *any* best path from start to end. I'll rewrite `best_first_search` to yield all the paths on the frontier that have the same cost as the best path. I thought this was straightforward, but I had **another bug** where I forgot to change the `<` to a `<=` in the third-to-last line of `all_paths_best_first_search`:" + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "id": "692abfea-f9c8-477c-8369-386cdd2a7606", + "metadata": {}, + "outputs": [], + "source": [ + "def reindeer_best_positions(maze) -> Node:\n", + " \"\"\"All the positions on any best path from start to end.\"\"\"\n", + " start, end = the(maze.findall('S')), the(maze.findall('E'))\n", + " problem = MazeSearchProblem(grid=maze, initial=State(start, East), goal=end)\n", + " paths = list(all_paths_best_first_search(problem, f=lambda n: n.path_cost + problem.h(n)))\n", + " return union({state.pos for state in path_states(path)} for path in paths)\n", + " \n", + "def all_paths_best_first_search(problem, f) -> 'Node':\n", + " \"Search nodes with minimum f(node) value first, return all paths with minimum cost.\"\n", + " node = Node(problem.initial)\n", + " frontier = PriorityQueue([node], key=f)\n", + " reached = {problem.initial: node}\n", + " while frontier:\n", + " node = frontier.pop()\n", + " if problem.is_goal(node.state):\n", + " yield node\n", + " while frontier:\n", + " path = frontier.pop()\n", + " if path.path_cost == node.path_cost and problem.is_goal(path.state):\n", + " yield path\n", + " return\n", + " for child in expand(problem, node):\n", + " s = child.state\n", + " if s not in reached or child.path_cost <= reached[s].path_cost:\n", + " reached[s] = child\n", + " frontier.add(child)" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "id": "ea9bf9f3-0e6b-4949-a641-6b3db2fd9d32", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Puzzle 16.2: .8586 seconds, answer 554 ok" + ] + }, + "execution_count": 158, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "answer(16.2, 554, lambda:\n", + " len(reindeer_best_positions(maze)))" + ] + }, { "cell_type": "markdown", "id": "c3317844-2b4a-4756-8a59-b765aa467445", @@ -6596,12 +6997,12 @@ "source": [ "# Summary\n", "\n", - "So far, I've solved all the puzzles. Most of them run in well under 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": 144, + "execution_count": 160, "id": "34813fc9-a000-4cd8-88ae-692851b3242c", "metadata": {}, "outputs": [ @@ -6612,38 +7013,44 @@ "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 2.2: .0021 seconds, answer 328 ok\n", + "Puzzle 3.1: .0005 seconds, answer 156388521 ok\n", "Puzzle 3.2: .0004 seconds, answer 75920122 ok\n", - "Puzzle 4.1: .0338 seconds, answer 2401 ok\n", - "Puzzle 4.2: .0274 seconds, answer 1822 ok\n", + "Puzzle 4.1: .0331 seconds, answer 2401 ok\n", + "Puzzle 4.2: .0260 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: .0015 seconds, answer 5329 ok\n", - "Puzzle 6.2: 2.0299 seconds, answer 2162 ok\n", - "Puzzle 7.1: .0217 seconds, answer 1985268524462 ok\n", - "Puzzle 7.2: 1.1304 seconds, answer 150077710195188 ok\n", - "Puzzle 8.1: .0028 seconds, answer 220 ok\n", - "Puzzle 8.2: .0030 seconds, answer 813 ok\n", - "Puzzle 9.1: .0209 seconds, answer 6332189866718 ok\n", - "Puzzle 9.2: 2.8353 seconds, answer 6353648390778 ok\n", + "Puzzle 6.1: .0017 seconds, answer 5329 ok\n", + "Puzzle 6.2: 1.9609 seconds, answer 2162 ok\n", + "Puzzle 7.1: .0214 seconds, answer 1985268524462 ok\n", + "Puzzle 7.2: 1.0813 seconds, answer 150077710195188 ok\n", + "Puzzle 8.1: .0027 seconds, answer 220 ok\n", + "Puzzle 8.2: .0031 seconds, answer 813 ok\n", + "Puzzle 9.1: .0196 seconds, answer 6332189866718 ok\n", + "Puzzle 9.2: 2.7301 seconds, answer 6353648390778 ok\n", "Puzzle 10.1: .0049 seconds, answer 744 ok\n", - "Puzzle 10.2: .0060 seconds, answer 1651 ok\n", - "Puzzle 11.1: .0015 seconds, answer 194482 ok\n", - "Puzzle 11.2: .0608 seconds, answer 232454623677743 ok\n", - "Puzzle 12.1: .0310 seconds, answer 1402544 ok\n", - "Puzzle 12.2: .0434 seconds, answer 862486 ok\n", + "Puzzle 10.2: .0059 seconds, answer 1651 ok\n", + "Puzzle 11.1: .0016 seconds, answer 194482 ok\n", + "Puzzle 11.2: .0606 seconds, answer 232454623677743 ok\n", + "Puzzle 12.1: .0302 seconds, answer 1402544 ok\n", + "Puzzle 12.2: .0424 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.8866 seconds, answer 6876 ok\n", + "Puzzle 14.2: 1.8721 seconds, answer 6876 ok\n", + "Puzzle 15.1: .0292 seconds, answer 1563092 ok\n", + "Puzzle 15.2: .0419 seconds, answer 1582688 ok\n", + "Puzzle 16.1: .1464 seconds, answer 103512 ok\n", + "Puzzle 16.2: .8586 seconds, answer 554 ok\n", "\n", - "Total time 8.1471 seconds, Mean time 0.2910 seconds\n" + "Correct: 32/32\n", + "\n", + "Time in seconds: 0.0054 median, 0.2806 mean, 8.9796 total.\n" ] } ], "source": [ - "report(answers)" + "summary(answers)" ] } ], diff --git a/ipynb/AdventUtils.ipynb b/ipynb/AdventUtils.ipynb index 541107d..70ef884 100644 --- a/ipynb/AdventUtils.ipynb +++ b/ipynb/AdventUtils.ipynb @@ -22,7 +22,7 @@ "from collections import Counter, defaultdict, namedtuple, deque, abc\n", "from dataclasses import dataclass, field\n", "from itertools import permutations, combinations, cycle, chain, islice\n", - "from itertools import count as count_from, product as cross_product\n", + "from itertools import count as count_from, product as cross_product, takewhile\n", "from typing import *\n", "from statistics import mean, median\n", "from math import ceil, floor, factorial, gcd, log, log2, log10, sqrt, inf, atan2\n", @@ -168,7 +168,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 78, "metadata": {}, "outputs": [], "source": [ @@ -200,17 +200,13 @@ " f' WRONG; expected answer is {self.solution}')\n", " return f'Puzzle {self.puzzle:4.1f}: {secs} seconds, answer {self.got:<15}{comment}'\n", "\n", - "def report(answers):\n", + "def summary(answers):\n", + " \"\"\"Print a report that summarizes the answers.\"\"\"\n", " for d in sorted(answers):\n", " print(answers[d])\n", - " secs = sum(answers[d].secs for d in answers)\n", - " print(f'\\nTotal time {secs:.4f} seconds, Mean time {secs/len(answers):.4f} seconds')\n", - "\n", - "def test_answer():\n", - " print(answer(0.1, unknown))\n", - " print(answer(0.2, 2**39, lambda: 2**39))\n", - " print(answer(0.3, 2**39, lambda: 2**39+1))\n", - " print(answer(10.4, unknown, lambda: 2 + 2))" + " times = [answers[d].secs for d in answers]\n", + " print(f'\\nCorrect: {quantify(answers[d].ok for d in answers)}/{len(answers)}')\n", + " print(f'\\nTime in seconds: {median(times):.4f} median, {mean(times):.4f} mean, {sum(times):.4f} total.')" ] }, { @@ -581,6 +577,8 @@ " \"\"\"Print a representation of the grid.\"\"\"\n", " for row in self.to_rows(xrange, yrange):\n", " print(*row, sep=sep)\n", + "\n", + " def __str__(self): return cat(self.to_rows())\n", " \n", " def plot(self, markers={'#': 's', '.': ','}, figsize=(14, 14), **kwds):\n", " \"\"\"Plot a representation of the grid.\"\"\"\n",