Add files via upload
This commit is contained in:
parent
10ee4b4900
commit
ecefbabb55
875
ipynb/Advent-2024.ipynb
Normal file
875
ipynb/Advent-2024.ipynb
Normal file
@ -0,0 +1,875 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "4f0ef1f2-0c73-4ef5-b430-48fe4ce450c0",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"<div style=\"text-align: right\" align=\"right\"><i>Peter Norvig, December 2024</i></div>\n",
|
||||||
|
"\n",
|
||||||
|
"# Advent of Code 2024\n",
|
||||||
|
"\n",
|
||||||
|
"I enjoy doing the [**Advent of Code**](https://adventofcode.com/) (AoC) programming puzzles, so here we go for 2024! Our old friend [@GaryJGrady](https://x.com/garyjgrady) is here to provide illustrations:\n",
|
||||||
|
"\n",
|
||||||
|
"<a href=\"https://x.com/garyjgrady\"><img src=\"https://pbs.twimg.com/media/Gdp709FW8AAq2_m?format=jpg&name=medium\" width=400 alt=\"GaryJGrady cartoon\"></a>\n",
|
||||||
|
"\n",
|
||||||
|
"I traditionally start by loading up my [**AdventUtils.ipynb**](AdventUtils.ipynb) notebook (same as last time except for the `current_year`):"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"id": "ed82ed5b-a42d-468b-8f6e-288d3c2de20b",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"%run AdventUtils.ipynb\n",
|
||||||
|
"current_year = 2024"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "dfecffd7-6955-45ba-9dc2-1ec805baba85",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Each day's solution consists of three parts, making use of my `parse` and `answer` utilities:\n",
|
||||||
|
"- **Reading the day's input**. E.g. `pairs = parse(1, ints)`. \n",
|
||||||
|
"- **Solving Part One**. Find the solution and record it with, e.g., `answer(1.1, 4, lambda: 2 + 2)`.\n",
|
||||||
|
"- **Solving Part Two**. Find the solution and record it with, e.g., `answer(1.2, 9, lambda: 3 * 3)`.\n",
|
||||||
|
"\n",
|
||||||
|
"The function `parse` assumes that the input is a sequence of records (default one per line), each of which should be parsed in some way (default just left as a string, but the argument `ints` says to treat each record as a tuple of integers). The function `answer` records the correct answer (for regression testing), and records the run time (that's why a `lambda:` is used).\n",
|
||||||
|
"\n",
|
||||||
|
"To fully understand each day's puzzle, and to follow along the drama involving Santa, the elves, the Chief Historian, and all the rest, you need to read the descriptions on the [**AoC**](https://adventofcode.com/) site, as linked in the header for each of my day's solutions, e.g. [**Day 1**](https://adventofcode.com/2023/day/1) below. Since you can't read Part 2 until you solve Part 1, I'll partially describe Part 2 in this notebook. But I can't copy the content of AoC here, nor show my input files; you need to go to the site for that.\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
" "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "4c6120a1-3129-44ff-935c-30c1d81ae028",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# [Day 1](https://adventofcode.com/2024/day/1) Historian Hysteria\n",
|
||||||
|
"\n",
|
||||||
|
"According to the narrative, North Pole Historians created two lists of **location IDs**. We can parse them as a sequence of pairs of integers, and then use the transpose function, `T` to get two lists of ID numbers:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 2,
|
||||||
|
"id": "22e5d621-a152-4712-866f-f8b962b5dd14",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"Puzzle input ➜ 1000 strs:\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"38665 13337\n",
|
||||||
|
"84587 21418\n",
|
||||||
|
"93374 50722\n",
|
||||||
|
"68298 57474\n",
|
||||||
|
"54771 18244\n",
|
||||||
|
"49242 83955\n",
|
||||||
|
"66490 44116\n",
|
||||||
|
"65908 51323\n",
|
||||||
|
"...\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"Parsed representation ➜ 1000 tuples:\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"(38665, 13337)\n",
|
||||||
|
"(84587, 21418)\n",
|
||||||
|
"(93374, 50722)\n",
|
||||||
|
"(68298, 57474)\n",
|
||||||
|
"(54771, 18244)\n",
|
||||||
|
"(49242, 83955)\n",
|
||||||
|
"(66490, 44116)\n",
|
||||||
|
"(65908, 51323)\n",
|
||||||
|
"...\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"left, right = location_ids = T(parse(1, ints))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "63cf2940-e251-49e4-8bc9-f1bcd599f8f4",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"<img src=\"https://pbs.twimg.com/media/GdvPVOpXcAEZ34_?format=jpg&name=medium\" width=400>\n",
|
||||||
|
"\n",
|
||||||
|
"### Part 1: What is the total distance between your lists?\n",
|
||||||
|
"\n",
|
||||||
|
"The **distance** between two numbers is the absolute value of their difference, and the **total distance** between two lists is the sum of the distances between respective pairs, where \"respective\" means to sort each list and then take the distance between the first element of each list, plus the distance between the second element of each list, and so on. (I use the transpose utility function, `T`, to turn the input sequence of 1000 pairs into two lists, each of 1000 integers.)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"id": "2dbfa3ae-3d47-4711-8821-7d1b2564bdc8",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 1.1: .0002 seconds, answer 1830467 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 3,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"def total_distance(left: Ints, right: Ints) -> int:\n",
|
||||||
|
" \"\"\"Total distance between respective list elements, after sorting.\"\"\"\n",
|
||||||
|
" return sum(abs(a - b) for a, b in zip(sorted(left), sorted(right)))\n",
|
||||||
|
"\n",
|
||||||
|
"answer(1.1, 1830467, lambda:\n",
|
||||||
|
" total_distance(left, right))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "88e26234-f1d2-4a62-86b0-2ad9251215eb",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 2: What is their similarity score?\n",
|
||||||
|
"\n",
|
||||||
|
"The **similarity score** is the sum of each element of the left list times the number of times that value appears in the right list."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 4,
|
||||||
|
"id": "e33f9705-3b51-4314-a302-6b3445290713",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 1.2: .0002 seconds, answer 26674158 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 4,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"def similarity_score(left: Ints, right: Ints) -> int:\n",
|
||||||
|
" \"\"\"The sum of each x in `left` times the number of times x appears in `right`.\"\"\"\n",
|
||||||
|
" counts = Counter(right)\n",
|
||||||
|
" return sum(x * counts[x] for x in left)\n",
|
||||||
|
"\n",
|
||||||
|
"answer(1.2, 26674158, lambda:\n",
|
||||||
|
" similarity_score(left, right))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "b9fa4fe0-4194-47d7-b815-b571af98caee",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# [Day 2](https://adventofcode.com/2024/day/2): Red-Nosed Reports\n",
|
||||||
|
"\n",
|
||||||
|
"Today's input is a sequence of **reports**, each of which is a sequence of integers:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 5,
|
||||||
|
"id": "10e1ab83-a6ec-4143-ad9a-eaae220adcde",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"Puzzle input ➜ 1000 strs:\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"74 76 78 79 76\n",
|
||||||
|
"38 40 43 44 44\n",
|
||||||
|
"1 2 4 6 8 9 13\n",
|
||||||
|
"65 68 70 72 75 76 81\n",
|
||||||
|
"89 91 92 95 93 94\n",
|
||||||
|
"15 17 16 18 19 17\n",
|
||||||
|
"46 47 45 48 51 52 52\n",
|
||||||
|
"77 78 79 82 79 83\n",
|
||||||
|
"...\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"Parsed representation ➜ 1000 tuples:\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"(74, 76, 78, 79, 76)\n",
|
||||||
|
"(38, 40, 43, 44, 44)\n",
|
||||||
|
"(1, 2, 4, 6, 8, 9, 13)\n",
|
||||||
|
"(65, 68, 70, 72, 75, 76, 81)\n",
|
||||||
|
"(89, 91, 92, 95, 93, 94)\n",
|
||||||
|
"(15, 17, 16, 18, 19, 17)\n",
|
||||||
|
"(46, 47, 45, 48, 51, 52, 52)\n",
|
||||||
|
"(77, 78, 79, 82, 79, 83)\n",
|
||||||
|
"...\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"reports = parse(2, ints)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "5dfd72c2-06c6-4c71-ae37-0c2c84074091",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 1: How many reports are safe?\n",
|
||||||
|
"\n",
|
||||||
|
"A report is **safe** if it is monotonically strictly increasing or strictly decreasing, and if no difference between adjacent numbers is greater than 3 in absolute value."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 6,
|
||||||
|
"id": "368cbe1c-b6b6-4a82-bef9-599ee9725899",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def safe(report: Ints) -> bool:\n",
|
||||||
|
" \"\"\"A report is safe if all differences are either in {1, 2, 3} or in {-1, -2, -3}.\"\"\"\n",
|
||||||
|
" deltas = diffs(report)\n",
|
||||||
|
" return deltas.issubset({1, 2, 3}) or deltas.issubset({-1, -2, -3})\n",
|
||||||
|
" \n",
|
||||||
|
"def diffs(report: Ints) -> set:\n",
|
||||||
|
" \"\"\"The set of differences between adjacent numbers in the report.\"\"\"\n",
|
||||||
|
" return {report[i] - report[i - 1] for i in range(1, len(report))}\n",
|
||||||
|
"\n",
|
||||||
|
"assert diffs((7, 6, 4, 2, 1)) == {-1, -2}\n",
|
||||||
|
"assert safe((7, 6, 4, 2, 1)) == True"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 7,
|
||||||
|
"id": "e662bf10-4d6a-40f1-95ce-dfc39f5b3fc2",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 2.1: .0013 seconds, answer 257 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 7,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"answer(2.1, 257, lambda:\n",
|
||||||
|
" quantify(reports, safe))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "ee48bf63-8a67-407b-9a73-df097811eabc",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 2: How many reports are safe using the Problem Dampener?\n",
|
||||||
|
"\n",
|
||||||
|
"The **problem dampener** says that a report is safe if you can drop one element and get a safe report."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 8,
|
||||||
|
"id": "67ba1d53-95b7-4811-b225-2ff15d6bdc5c",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def safe_with_dampener(report: Ints) -> bool:\n",
|
||||||
|
" \"\"\"Is there any way to drop one element of `report` to get a safe report?\"\"\"\n",
|
||||||
|
" return any(map(safe, drop_one(report)))\n",
|
||||||
|
"\n",
|
||||||
|
"def drop_one(report) -> Iterable:\n",
|
||||||
|
" \"\"\"All ways of dropping one element of the input report.\"\"\"\n",
|
||||||
|
" return (report[:i] + report[i + 1:] for i in range(len(report)))\n",
|
||||||
|
"\n",
|
||||||
|
"assert set(drop_one('1234')) == {'234', '134', '124', '123'}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 9,
|
||||||
|
"id": "d1b9ffb5-af7a-465f-a063-c31df2d0605c",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 2.2: .0076 seconds, answer 328 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 9,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"answer(2.2, 328, lambda:\n",
|
||||||
|
" quantify(reports, safe_with_dampener))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "54d6a0c2-a8ed-404d-abc0-72aa28a49f5d",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# [Day 3](https://adventofcode.com/2024/day/3): Mull It Over\n",
|
||||||
|
"\n",
|
||||||
|
"Today's input is a computer program with some corrupted characters. The program has multiple lines, but lines don't matter, so I will concatenate them into one big string:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 10,
|
||||||
|
"id": "78080200-0f9f-4492-9bee-c936737ee96f",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"Puzzle input ➜ 6 strs:\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"where(536,162)~'what()what()how(220,399){ mul(5,253);mul(757,101)$where()@why()who()&when()from( ...\n",
|
||||||
|
"}?~who()select()-mul(316,505)&%*how()mul(363,589)>?%-:)where()~{{mul(38,452)select()%>[{]%>%mul( ...\n",
|
||||||
|
"?>where(911,272)'mul(894,309)~+%@#}@#why()mul(330,296)what()mul(707,884)mul;&}<{>where()$why()]m ...\n",
|
||||||
|
"> (when()[where()/#!/usr/bin/perl,@;mul(794,217)select():'])select()mul(801,192)why()&]why()/:]* ...\n",
|
||||||
|
",+who():mul(327,845)/ >@[>@}}mul(86,371)!~&&~how(79,334)mul(637,103)why()mul(358,845)-#~?why(243 ...\n",
|
||||||
|
"where()#{*,!?:$mul(204,279)what()!{ what()mul(117,94)!select()>:mul(665,432)#don't()!!<!? mul(50 ...\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"program = cat(parse(3))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "5327e631-502f-4687-83d5-84a4e4012ccf",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 1: What do you get if you add up all of the results of the multiplications?\n",
|
||||||
|
"\n",
|
||||||
|
"For Part 1, just look for instructions of the form \"mul(*digits*,*digits*)\", using a regular expression and `re.findall`. Perform each of these multiplications and add them up, and ignore all other characters/instructions:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 11,
|
||||||
|
"id": "bf6366b1-6952-47d8-8b3c-09f8d05ec093",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def execute(program: str) -> int:\n",
|
||||||
|
" \"\"\"The sum of the results of the multiply instructions.\"\"\"\n",
|
||||||
|
" return sum(prod(ints(m)) for m in multiplications(program))\n",
|
||||||
|
"\n",
|
||||||
|
"def multiplications(program: str) -> List[str]:\n",
|
||||||
|
" \"\"\"A list of all the multiplication instructions in the program.\"\"\"\n",
|
||||||
|
" return re.findall(r'mul\\(\\d+,\\d+\\)', program)\n",
|
||||||
|
"\n",
|
||||||
|
"test = \"xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))\"\n",
|
||||||
|
"assert execute(test) == 161\n",
|
||||||
|
"assert multiplications(test) == ['mul(2,4)', 'mul(5,5)', 'mul(11,8)', 'mul(8,5)']"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 12,
|
||||||
|
"id": "2032c903-5f23-4c16-ba68-410b6c1750e1",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 3.1: .0015 seconds, answer 156388521 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 12,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"answer(3.1, 156388521, lambda: \n",
|
||||||
|
" execute(program))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "622d7010-145e-422a-a592-d4b446afcc0f",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 2: What do you get if you add up all of the results of just the enabled multiplications?\n",
|
||||||
|
"\n",
|
||||||
|
"For Part 2, the instruction \"don't()\" says to disable (ignore) following multiply instructions until a \"do()\" instruction enables them again. I will define the function `enabled`, which returns the part of the program that is enabled, by susbstituting a space for the \"don't()...do()\" sequence."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 13,
|
||||||
|
"id": "4525d01a-bac0-41c2-92b8-baf0fd395e88",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def enabled(program: str) -> str:\n",
|
||||||
|
" \"\"\"Just the part of the program that is enabled; remove \"don't()...do()\" text.\"\"\"\n",
|
||||||
|
" return re.sub(r\"don't\\(\\).*?(do\\(\\)|$)\", \" \", program)\n",
|
||||||
|
"\n",
|
||||||
|
"test2 = \"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))\"\n",
|
||||||
|
"assert enabled(test2) == 'xmul(2,4)&mul[3,7]!^ ?mul(8,5))'\n",
|
||||||
|
"assert execute(enabled(test2)) == 2 * 4 + 8 * 5 == 48\n",
|
||||||
|
"assert multiplications(enabled(test2)) == ['mul(2,4)', 'mul(8,5)']"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 14,
|
||||||
|
"id": "ce40f258-ca76-48c3-9965-27a6979a4243",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 3.2: .0009 seconds, answer 75920122 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 14,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"answer(3.2, 75920122, lambda:\n",
|
||||||
|
" execute(enabled(program)))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "e1448343-6488-45ad-b03d-d7928feb75cd",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# [Day 4](https://adventofcode.com/2024/day/4): Ceres Search\n",
|
||||||
|
"\n",
|
||||||
|
"Today's puzzle is a 2D word-search puzzle:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 15,
|
||||||
|
"id": "a0d903b9-018e-4861-9314-cafed59055fd",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"Puzzle input ➜ 140 strs:\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"MASAMXMSSXXMAMXXMXMASXMASXMMSMSMMMAXMASASMMSSMSXAXMASMMSMMMSSMSASMSSSSMSMSMXXMXMAXAMXMSMSSXSAMXM ...\n",
|
||||||
|
"MASMMXMASAXASMSMMMSAMXSMSAMXAAAAAXAMXASXAMAAAAMMSMMMMMASXAAAAMMAMAMMASAAAAXMXMSSSSSSMMSAMAXAXXSM ...\n",
|
||||||
|
"MMXAXMMMSXMAMAAXAAXAAAXXSMMSMSMSMXAXMXSMMMMSSMXAMXAAXMAMMMMSSMMAMAMMAMMMMMXSAAXAAMMAXXSAMXMSMAXM ...\n",
|
||||||
|
"SXSAMASASMSXMSMSMSSMMMMMMXAMXMMXMASMMMMAXXAAAMMMSSSSSMASXXAAXASMSXXMXSXSXSASMSMMSMSAMMMAMXAAMASX ...\n",
|
||||||
|
"AAAXXXMASASXMXMAXXMMASAASMXSASASXAAAAMSSMMMSXMAAMMMMMXAXMMMMSAMXAMASAMXSAMASXXAXAAMAMXSAMXSXSMMA ...\n",
|
||||||
|
"MSMMXXMMMAMAMMMMMMXSAXXAMMMMXSAXMMXXAMXAAMMXMASXMAAASMMXAAMXAXAMMMAMAMAMAMXMASXMMXMAAXMAXMAMXMSA ...\n",
|
||||||
|
"MXAXAMXXMMMMSAMAASMMMSMMASASAMAMAXMSXMSMMXAMXAXMMSSXSASXSSSMAMSMXMXSAMSSSMAMXMXAMAXXMMSAXAXMMXMA ...\n",
|
||||||
|
"ASXMMXSAMXAASXXMXSAAAXASAMMMASMSSSMAAMMXMMSSMASAMAMMMAMMAXMAXMASXMAXMSAAASASAMXSSMXSAAXSSMXAAXXA ...\n",
|
||||||
|
"...\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"xmas_grid = Grid(parse(4))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "16d56872-be2c-4e9d-8821-a0fe9f66970b",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 1: How many times does XMAS appear?\n",
|
||||||
|
"\n",
|
||||||
|
"We just have to find how many times the word \"XMAS\" appears in the grid, horizontally, vertically, or diagonally, forwards or backwards. The variable `directions8` contains those eight directions (as (delta-x, delta-y) pairs). So examine each square of the grid and if it contains \"X\", see in how many of the directions it spells \"XMAS\". (Note that locations in the grid are denoted by `(x, y)` coordinates, as are directions (e.g., `(1, 0)` is the `East` direction. The functions `add` and `mul` do addition and scalar multiplication on these vectors.)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 28,
|
||||||
|
"id": "72d48abb-7a82-452f-b91d-838b3836a90f",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def word_search(grid: Grid, word='XMAS') -> int:\n",
|
||||||
|
" \"\"\"How many times does the given word appear in the grid?\"\"\"\n",
|
||||||
|
" return quantify(grid_can_spell(grid, start, dir, word) \n",
|
||||||
|
" for start in grid \n",
|
||||||
|
" if grid[start] == word[0]\n",
|
||||||
|
" for dir in directions8)\n",
|
||||||
|
"\n",
|
||||||
|
"def grid_can_spell(grid, start, dir, word):\n",
|
||||||
|
" \"\"\"Does `word` appear in grid starting at `start` and going in direction `dir`?\"\"\"\n",
|
||||||
|
" return all(grid[add(start, mul(dir, i))] == word[i] for i in range(len(word)))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 29,
|
||||||
|
"id": "6175362b-d8b4-45d1-b70c-d8575a0fe188",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 4.1: .1282 seconds, answer 2401 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 29,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"answer(4.1, 2401, lambda:\n",
|
||||||
|
" word_search(xmas_grid))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "eabe90c4-b668-4d9e-a345-b09f4b8ee42b",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 1: How many times does an X-MAS appear?\n",
|
||||||
|
"\n",
|
||||||
|
"Upon further review, the goal is not to find \"XMAS\" byt rather X-\"MAS\"; that is, two \"MAS\" words in an X pattern. The pattern can be any of these four:\n",
|
||||||
|
"\n",
|
||||||
|
" M.S S.M M.M S.S\n",
|
||||||
|
" .A. .A. .A. .A.\n",
|
||||||
|
" M.S S.M S.S M.M\n",
|
||||||
|
"\n",
|
||||||
|
"I decided to find these by looking for each instance of the middle letter (\"A\") in the grid, and then, for each pair of diagonal directions, see if the target word (\"MAS\") can be spelled in both directions:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 18,
|
||||||
|
"id": "ff7540fd-b5cb-4d02-810d-5c77da2bd9f4",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 4.2: .0785 seconds, answer 1822 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 18,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"diagonal_pairs = ([SE, NE], [SW, NW], [SE, SW], [NE, NW])\n",
|
||||||
|
"\n",
|
||||||
|
"def x_search(grid: Grid, word='MAS') -> int:\n",
|
||||||
|
" \"\"\"How many times does an X-MAS appear in the grid?\"\"\"\n",
|
||||||
|
" return quantify((grid_can_spell(grid, sub(mid_pos, dir1), dir1, word) and\n",
|
||||||
|
" grid_can_spell(grid, sub(mid_pos, dir2), dir2, word))\n",
|
||||||
|
" for mid_pos in grid if grid[mid_pos] == word[1]\n",
|
||||||
|
" for dir1, dir2 in diagonal_pairs)\n",
|
||||||
|
"\n",
|
||||||
|
"answer(4.2, 1822, lambda:\n",
|
||||||
|
" x_search(xmas_grid))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "0249ce80-e649-44b3-8c02-613fc7652110",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# [Day 5](https://adventofcode.com/2024/day/5): Print Queue\n",
|
||||||
|
"\n",
|
||||||
|
"Today's puzzle involves a **sleigh launch safety manual** that needs to be updated. The day's input is in two parts: the first a set of **rules** such as \"47|53\", which means that page 47 must be printed before page 53; and the second a list of **updates** of the form \"75,47,61,53,29\", meaning that those pages are to be printed in that order.\n",
|
||||||
|
"\n",
|
||||||
|
"I mostly like my `parse` function, but I admit it is not ideal when an input file has two parts like this. I'll parse the two parts into paragraphs, and then call `parse` again on each paragraph:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 19,
|
||||||
|
"id": "b77a5a1f-a43b-4ce8-a60c-94d69a595505",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"Puzzle input ➜ 1366 strs:\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"48|39\n",
|
||||||
|
"39|84\n",
|
||||||
|
"39|23\n",
|
||||||
|
"95|51\n",
|
||||||
|
"95|76\n",
|
||||||
|
"95|61\n",
|
||||||
|
"14|52\n",
|
||||||
|
"14|49\n",
|
||||||
|
"...\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"Parsed representation ➜ 2 strs:\n",
|
||||||
|
"────────────────────────────────────────────────────────────────────────────────────────────────────\n",
|
||||||
|
"48|39\n",
|
||||||
|
"39|84\n",
|
||||||
|
"39|23\n",
|
||||||
|
"95|51\n",
|
||||||
|
"95|76\n",
|
||||||
|
"95|61\n",
|
||||||
|
"14|52\n",
|
||||||
|
"14|49\n",
|
||||||
|
"14|39\n",
|
||||||
|
"14|53\n",
|
||||||
|
"85|19\n",
|
||||||
|
"85|25\n",
|
||||||
|
"85|61\n",
|
||||||
|
"85|35\n",
|
||||||
|
"85|58\n",
|
||||||
|
"74|86\n",
|
||||||
|
" ...\n",
|
||||||
|
"61,58,51,32,12,14,71\n",
|
||||||
|
"58,25,54,14,12,94,32,76,39\n",
|
||||||
|
"35,53,26,77,14,71,25,76,85,55,51,49,95\n",
|
||||||
|
"32,91,76, ...\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"manual = parse(5, sections=paragraphs)\n",
|
||||||
|
"rules = set(parse(manual[0], ints))\n",
|
||||||
|
"updates = parse(manual[1], ints)\n",
|
||||||
|
"\n",
|
||||||
|
"assert (48, 39) in rules # `rules` is a set of (earlier, later) page number pairs\n",
|
||||||
|
"assert updates[0] == (61,58,51,32,12,14,71) # `updates` is a sequence of page number tuples"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "d6b6d374-cbe9-4b84-a1dd-d9df927c7182",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 1: What do you get if you add up the middle page number from the correctly-ordered updates?\n",
|
||||||
|
"\n",
|
||||||
|
"I'll define `is_correct` to determine if an update is in the correct order, and `sum_of_correct_middles` to add up the middle numbers of the correct updates:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 20,
|
||||||
|
"id": "78898d37-46ff-4367-9d89-b2a107a90aa1",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def sum_of_correct_middles(rules: Set[Ints], updates: Tuple[Ints]) -> int:\n",
|
||||||
|
" \"\"\"The sum of the middle elements of each update that is correct.\"\"\"\n",
|
||||||
|
" return sum(middle(update) for update in updates if is_correct(update, rules))\n",
|
||||||
|
"\n",
|
||||||
|
"def is_correct(update: Ints, rules: Set[Ints]) -> bool:\n",
|
||||||
|
" \"\"\"An update is correct if no pair of pages violates a rule in the rules set.\"\"\"\n",
|
||||||
|
" return not any((second, first) in rules for (first, second) in combinations(update, 2))\n",
|
||||||
|
"\n",
|
||||||
|
"def middle(sequence) -> object: return sequence[len(sequence) // 2]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 21,
|
||||||
|
"id": "b1c87359-1d2d-4a90-8305-9d152ce5d547",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 5.1: .0010 seconds, answer 5762 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 21,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"answer(5.1, 5762, lambda:\n",
|
||||||
|
" sum_of_correct_middles(rules, updates))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "80da4fd9-b11e-4dbb-8d22-2071d1a89827",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Part 2: What do you get if you add up the middle page numbers after correctly re-ordering the incorrect updates?\n",
|
||||||
|
"\n",
|
||||||
|
"In Part 2 we have to find the incorrect updates, re-order them into a correct order, and again sum the middle page numbers.\n",
|
||||||
|
"Since I have already defined `is_correct`, i could just generate all permutations of each update and find one that `is_correct`. That would work great if the longest update is only 5 pages long, as it is in the example input. But what is the longest update in the actual input?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 22,
|
||||||
|
"id": "d8718c3e-0b3b-49ce-8cca-abd82aa788d7",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"23"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 22,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"max(map(len, updates))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "4449200f-dd19-48f1-94b2-7304daa9fa00",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"That's not great. With 23 numbers there are 23! permutations, which is over 25 sextillion. So instead, here's my strategy:\n",
|
||||||
|
"\n",
|
||||||
|
"- `sum_of_corrected_middles` will find the incorrect rules, perform a correction on each, and sum the middle numbers.\n",
|
||||||
|
"- `correction` will sort an update, obeying the rules. It used to be that Python's `sort` method allowed a `cmp` keyword to compare two values; there is vestigial support for this with the `functools.cmp_to_key` function. I will **sort** each update so that page *m* comes before page *n* if (*m*, *n*) is in the rules.\n",
|
||||||
|
"- Sorting will be a sextillion times faster than enumerating permutations."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 23,
|
||||||
|
"id": "7222dc1c-067f-4bb5-84e1-3c2fc72fd53a",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def sum_of_corrected_middles(rules, updates) -> int:\n",
|
||||||
|
" \"\"\"The sum of the middle elements of each update that is correct.\"\"\"\n",
|
||||||
|
" incorrect = [update for update in updates if not is_correct(update, rules)]\n",
|
||||||
|
" corrected = [correction(update, rules) for update in incorrect]\n",
|
||||||
|
" return sum(map(middle, corrected))\n",
|
||||||
|
" \n",
|
||||||
|
"def correction(update: Ints, rules) -> Ints:\n",
|
||||||
|
" \"\"\"Reorder the update to make it correct.\"\"\"\n",
|
||||||
|
" def rule_lookup(m, n): return +1 if (m, n) in rules else -1 \n",
|
||||||
|
" return sorted(update, key=functools.cmp_to_key(rule_lookup))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 24,
|
||||||
|
"id": "dc1fbda9-2cfd-442a-afef-12c9b0d2b17f",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"Puzzle 5.2: .0016 seconds, answer 4130 ok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 24,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"answer(5.2, 4130, lambda:\n",
|
||||||
|
" sum_of_corrected_middles(rules, updates))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "53b1ccbc-01ae-43d0-a75f-3f9389fdd3c9",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"I have to say, I'm pleased that this day I got both parts right the first time (and in fact, the same for the previous days). I was worried I might have my `+1` and `-1` backwards in `cmp_to_key`, but so far, everything has gone very smoothly. (However, even if I started solving right at midnight (which I don't), I don't think I would show up on the leaderboard; I have been good at getting a correct answer the first time, but I'm still way slower than the skilled contest programmers."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "c3317844-2b4a-4756-8a59-b765aa467445",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Summary\n",
|
||||||
|
"\n",
|
||||||
|
"So far, I've solved all the puzzles, each with a run time of less than a tenth of a second."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 25,
|
||||||
|
"id": "34813fc9-a000-4cd8-88ae-692851b3242c",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Puzzle 1.1: .0002 seconds, answer 1830467 ok\n",
|
||||||
|
"Puzzle 1.2: .0002 seconds, answer 26674158 ok\n",
|
||||||
|
"Puzzle 2.1: .0013 seconds, answer 257 ok\n",
|
||||||
|
"Puzzle 2.2: .0076 seconds, answer 328 ok\n",
|
||||||
|
"Puzzle 3.1: .0015 seconds, answer 156388521 ok\n",
|
||||||
|
"Puzzle 3.2: .0009 seconds, answer 75920122 ok\n",
|
||||||
|
"Puzzle 4.1: .0990 seconds, answer 2401 ok\n",
|
||||||
|
"Puzzle 4.2: .0785 seconds, answer 1822 ok\n",
|
||||||
|
"Puzzle 5.1: .0010 seconds, answer 5762 ok\n",
|
||||||
|
"Puzzle 5.2: .0016 seconds, answer 4130 ok\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"for d in sorted(answers):\n",
|
||||||
|
" print(answers[d])"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3 (ipykernel)",
|
||||||
|
"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.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user