From 008df35271c206f593f457430c85a218a21319a7 Mon Sep 17 00:00:00 2001 From: kzkedzierska <31040515+kzkedzierska@users.noreply.github.com> Date: Mon, 4 Dec 2023 02:19:58 +0000 Subject: [PATCH] :art: improved the code (with suggestions from Github Copilot; added typing and docs --- 2023/Day03.ipynb | 396 +++++++++++++++++++++++++---------------------- 1 file changed, 207 insertions(+), 189 deletions(-) diff --git a/2023/Day03.ipynb b/2023/Day03.ipynb index c55c3d0..0052f7c 100644 --- a/2023/Day03.ipynb +++ b/2023/Day03.ipynb @@ -53,81 +53,95 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, + "outputs": [], + "source": [ + "example_input = \"\"\"467..114..\n", + "...*......\n", + "..35..633.\n", + "......#...\n", + "617*......\n", + ".....+.58.\n", + "..592.....\n", + "......755.\n", + "...$.*....\n", + ".664.598..\"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "({(3, 1): '*',\n", - " (6, 3): '#',\n", - " (3, 4): '*',\n", - " (5, 5): '+',\n", - " (3, 8): '$',\n", - " (5, 8): '*'},\n", - " {(2, 0): 467,\n", - " (7, 0): 114,\n", - " (3, 2): 35,\n", - " (8, 2): 633,\n", - " (2, 4): 617,\n", - " (8, 5): 58,\n", - " (4, 6): 592,\n", - " (8, 7): 755,\n", - " (3, 9): 664,\n", - " (7, 9): 598})" + "({(3, 1), (3, 4), (3, 8), (5, 5), (5, 8), (6, 3)},\n", + " {(0, 0): 467,\n", + " (5, 0): 114,\n", + " (2, 2): 35,\n", + " (6, 2): 633,\n", + " (0, 4): 617,\n", + " (7, 5): 58,\n", + " (2, 6): 592,\n", + " (6, 7): 755,\n", + " (1, 9): 664,\n", + " (5, 9): 598})" ] }, - "execution_count": 1, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "example_input = \"\"\"467..114..\n", - "...*......\n", - "..35..633.\n", - "......#...\n", - "617*......\n", - ".....+.58.\n", - "..592.....\n", - "......755.\n", - "...$.*....\n", - ".664.598..\"\"\"\n", - "\n", - "def process_input(input, example = False):\n", - "\n", - " symbol_dict = dict()\n", - " numbers_dict = dict()\n", + "from typing import Tuple, Set, Dict\n", + "def process_input(input_file: str, \n", + " example: bool = False) -> Tuple[Set[Tuple[int, int]], \n", + " Dict[Tuple[int, int], int]]:\n", + " \"\"\"\n", + " Process the input file and return symbols and numbers dictionary.\n", + "\n", + " Parameters:\n", + " - input_file (str): Path to the input file or the input data itself.\n", + " - example (bool): Flag indicating whether the input is an example or a file path.\n", + "\n", + " Returns:\n", + " - symbols (set): Set of symbols.\n", + " - numbers_dict (dict): Dictionary of numbers with their coordinates as keys.\n", + "\n", + " \"\"\"\n", + " symbols = set()\n", + " numbers_dict = {}\n", " y = 0\n", + "\n", " if not example:\n", - " input = open(input).read()\n", + " with open(input_file) as f:\n", + " input_data = f.read()\n", + " else:\n", + " input_data = input_file\n", "\n", - " for line in input.splitlines():\n", + " for line in input_data.splitlines():\n", + " line += \".\" # Add sentinel to process last number\n", " num = \"\"\n", " for x, c in enumerate(line):\n", - " # if c in a number add to num\n", - " if c in '0123456789':\n", + " if c.isdigit():\n", " num += c\n", " else:\n", - " # if num is not empty add to numbers_dict\n", - " if num != \"\":\n", - " numbers_dict[(x-1, y)] = int(num)\n", + " if num:\n", + " numbers_dict[(x-len(num), y)] = int(num)\n", " num = \"\"\n", - " # if c is not a dot add to symbol_dict\n", " if c != '.':\n", - " # I don't really need symbol_dict, a set would be enough\n", - " # but in case I might need it later, I'll keep it\n", - " symbol_dict[(x, y)] = c\n", - " if num != \"\":\n", - " numbers_dict[(x, y)] = int(num)\n", + " symbols.add((x, y))\n", " y += 1\n", "\n", - " return symbol_dict, numbers_dict\n", + " return symbols, numbers_dict\n", "\n", "process_input(example_input, True)" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -136,31 +150,34 @@ "4361" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def scan_grid(symbol_dict, numbers_dict):\n", + "def scan_grid(symbols: set, numbers_dict: dict) -> int:\n", + " \"\"\"\n", + " Scan the grid for parts numbers. Those are defined as numbers that are\n", + " adjacent to a symbol. The sum of all parts numbers is the solution.\n", + " \n", + " Parameters:\n", + " symbols: Set of coordinates of symbols.\n", + " numbers_dict: Dictionary of coordinates of numbers and their value.\n", + " \n", + " Returns:\n", + " Sum of all parts numbers.\n", + " \"\"\"\n", " parts_sum = 0\n", - " # for each number in numbers_dict, check if in x+-1 and y+-1 there is a symbol\n", " for (x, y), num in numbers_dict.items():\n", - " # get the number of digits of num\n", " num_digits = len(str(num))\n", - " # check if there is a symbol in (x_min-1 to x_max+1) and y+-1\n", - " x_min = x - num_digits\n", - " x_max = x + 1\n", - " # create all possible combinations of x1, y1 \n", - " # where x is within x_min and x_max and y is y-1 or y+1\n", - " xs = range(x_min, x_max+1)\n", - " ys = [y-1, y+1]\n", - " # take only the xs and ys >= 0\n", - " xs = [x1 for x1 in xs if x1 >= 0]\n", - " ys = [y1 for y1 in ys if y1 >= 0]\n", + " x_min = x - 1\n", + " x_max = x + num_digits\n", + " xs = [x1 for x1 in range(x_min, x_max+1) if x1 >= 0]\n", + " ys = [y1 for y1 in [y-1, y+1] if y1 >= 0]\n", " pos = [(x1, y1) for x1 in xs for y1 in ys]\n", " pos += [(x1, y) for x1 in [x_min, x_max] if x1 >= 0]\n", - " if any([symbol_dict.get(p) for p in pos]):\n", + " if any(p in symbols for p in pos):\n", " parts_sum += num\n", "\n", " return parts_sum\n", @@ -177,7 +194,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -186,35 +203,37 @@ "text": [ " | 0 | 1 | 2 | 3 |\n", " --------------------\n", - " 0 | . | . | . | . |\n", - " 1 | . | . | . | 1 |\n", - " 2 | 9 | 8 | 9 | * |\n", - " 3 | . | _ | - | - |\n" + "0 | . | . | . | . |\n", + "1 | . | . | . | 1 |\n", + "2 | 9 | 8 | 9 | * |\n", + "3 | . | _ | - | - |\n" ] } ], "source": [ - "def print_matrix(input_string):\n", + "def print_matrix(input_string: str) -> None:\n", + " \"\"\"\n", + " Prints a matrix representation of the input string.\n", + "\n", + " Args:\n", + " input_string (str): The input string representing the matrix.\n", + "\n", + " Returns:\n", + " None\n", + " \"\"\"\n", " # Split the input string into rows\n", " rows = input_string.split(\"\\n\")\n", " # get the max length of the rows\n", - " max_len = max([len(row) for row in rows])\n", + " max_len = max(len(row) for row in rows)\n", " \n", " # Create a matrix of the max length\n", - " matrix = [[' ' for i in range(max_len)] for j in range(len(rows))]\n", + " matrix = [[char for char in row.ljust(max_len)] for row in rows]\n", "\n", - " # Populate the matrix with characters from the input string\n", - " for i, row in enumerate(rows):\n", - " for j, char in enumerate(row):\n", - " matrix[i][j] = char\n", " # Print the matrix with row and column numbers\n", - " print(f\" | {' | '.join([str(i) if i < 10 else ' ' for i in range(max_len)])} |\")\n", + " print(f\" | {'| '.join(str(i).ljust(2) for i in range(max_len))}|\")\n", " print(f\" {'-'*(max_len*4 + 4)}\")\n", " for i, row in enumerate(matrix):\n", - " if i < 10:\n", - " print(f\" {i} | {' | '.join(row)} |\")\n", - " else:\n", - " print(f\"{i} | {' | '.join(row)} |\")\n", + " print(f\"{str(i).ljust(2)} | {' | '.join(row)} |\")\n", "\n", "# Input string\n", "input_string = \"\"\"....\n", @@ -228,7 +247,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -237,9 +256,9 @@ "text": [ " | 0 | 1 | 2 | 3 | 4 | 5 |\n", " ----------------------------\n", - " 0 | 1 | 1 | 1 | $ | 1 | 2 |\n", - " 1 | + | . | . | . | . | . |\n", - " 2 | . | . | 1 | 3 | . | % |\n" + "0 | 1 | 1 | 1 | $ | 1 | 2 |\n", + "1 | + | . | . | . | . | . |\n", + "2 | . | . | 1 | 3 | . | % |\n" ] } ], @@ -255,7 +274,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -264,9 +283,9 @@ "text": [ " | 0 | 1 | 2 | 3 | 4 | 5 |\n", " ----------------------------\n", - " 0 | 1 | 1 | 1 | $ | 1 | 2 |\n", - " 1 | + | . | . | . | 8 | . |\n", - " 2 | . | . | 1 | 3 | . | % |\n" + "0 | 1 | 1 | 1 | $ | 1 | 2 |\n", + "1 | + | . | . | . | 8 | . |\n", + "2 | . | . | 1 | 3 | . | % |\n" ] } ], @@ -282,7 +301,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -291,9 +310,9 @@ "text": [ " | 0 | 1 | 2 | 3 | 4 | 5 |\n", " ----------------------------\n", - " 0 | . | . | $ | $ | . | . |\n", - " 1 | + | . | . | . | 8 | . |\n", - " 2 | . | . | 1 | 3 | . | % |\n" + "0 | . | . | $ | $ | . | . |\n", + "1 | + | . | . | . | 8 | . |\n", + "2 | . | . | 1 | 3 | . | % |\n" ] } ], @@ -309,7 +328,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -318,9 +337,9 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | 1 | . | 3 |\n", - " 1 | . | 5 | . |\n", - " 2 | 7 | . | 9 |\n" + "0 | 1 | . | 3 |\n", + "1 | . | 5 | . |\n", + "2 | 7 | . | 9 |\n" ] } ], @@ -336,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -345,9 +364,9 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | 1 | * | 3 |\n", - " 1 | * | 5 | * |\n", - " 2 | 7 | * | 9 |\n" + "0 | 1 | * | 3 |\n", + "1 | * | 5 | * |\n", + "2 | 7 | * | 9 |\n" ] } ], @@ -363,7 +382,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -372,7 +391,7 @@ "text": [ " | 0 | 1 | 2 | 3 |\n", " --------------------\n", - " 0 | . | $ | 1 | 2 |\n" + "0 | . | $ | 1 | 2 |\n" ] } ], @@ -384,7 +403,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -393,7 +412,7 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | * | * | * |\n" + "0 | * | * | * |\n" ] } ], @@ -405,7 +424,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -414,8 +433,8 @@ "text": [ " | 0 | 1 | 2 | 3 | 4 |\n", " ------------------------\n", - " 0 | 3 | 3 | 3 | . | 3 |\n", - " 1 | . | . | . | * | . |\n" + "0 | 3 | 3 | 3 | . | 3 |\n", + "1 | . | . | . | * | . |\n" ] } ], @@ -428,7 +447,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -437,9 +456,9 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | 1 | . | 3 |\n", - " 1 | . | 5 | * |\n", - " 2 | . | . | 9 |\n" + "0 | 1 | . | 3 |\n", + "1 | . | 5 | * |\n", + "2 | . | . | 9 |\n" ] } ], @@ -453,7 +472,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -462,8 +481,8 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | * | . | * |\n", - " 1 | 1 | 2 | 3 |\n" + "0 | * | . | * |\n", + "1 | 1 | 2 | 3 |\n" ] } ], @@ -476,7 +495,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -485,7 +504,7 @@ "539637" ] }, - "execution_count": 14, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -527,45 +546,50 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ - "def process_input_for_gears(input, example = False):\n", - "\n", + "def process_input_for_gears(input_file: str, \n", + " example: bool = False) -> Tuple[Set[Tuple[int, int]],\n", + " Dict[int, str],\n", + " Dict[Tuple[int, int], int]]:\n", + " \"\"\"\n", + " Process the input file and extract gears, numbers_idx, and numbers_dict.\n", + "\n", + " Parameters:\n", + " input_file (str): The path to the input file.\n", + " example (bool): Flag indicating whether the input is an example or not.\n", + "\n", + " Returns:\n", + " tuple: A tuple containing gears (set), numbers_idx (dict), and numbers_dict (dict).\n", + " \"\"\"\n", " gears = set()\n", " numbers_idx = dict()\n", " numbers_dict = dict()\n", " y = 0\n", " idx = 0\n", " if not example:\n", - " input = open(input).read()\n", + " with open(input_file) as f:\n", + " input_data = f.read()\n", + " else:\n", + " input_data = input_file\n", "\n", - " for line in input.splitlines():\n", + " for line in input_data.splitlines():\n", + " line += \".\" # Add sentinel to process last number\n", " num = \"\"\n", " for x, c in enumerate(line):\n", - " # if c in a number add to num\n", - " if c in '0123456789':\n", + " if c.isdigit():\n", " num += c\n", " else:\n", - " # if num is not empty add to numbers_dict\n", - " if num != \"\":\n", + " if num:\n", " numbers_idx[idx] = num\n", - " # for each position of the number add the idx\n", " for i in range(len(num)):\n", - " numbers_dict[(x-i-1, y)] = idx\n", + " numbers_dict[(x - i - 1, y)] = idx\n", " num = \"\"\n", " idx += 1\n", - " # if c is not a dot add to symbol_dict\n", " if c == '*':\n", " gears.add((x, y))\n", - " if num != \"\":\n", - " numbers_idx[idx] = num\n", - " # for each position of the number add the idx\n", - " for i in range(len(num)):\n", - " numbers_dict[(x-i, y)] = idx\n", - " num = \"\"\n", - " idx += 1\n", " y += 1\n", "\n", " return gears, numbers_idx, numbers_dict\n" @@ -573,49 +597,43 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "467835" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "def scan_grid_for_gears(gears, numbers_idx, numbers_dict):\n", + "def scan_grid_for_gears(gears: set, \n", + " numbers_idx: dict, \n", + " numbers_dict: dict) -> int:\n", + " \"\"\"\n", + " Scans the grid for gears and calculates the sum of the products of the corresponding numbers.\n", + "\n", + " Parameters:\n", + " gears (set): List of coordinates representing the positions of gears.\n", + " numbers_idx (dict): Dictionary mapping gear indices to numbers.\n", + " numbers_dict (dict): Dictionary mapping positions to gear indices.\n", + "\n", + " Returns:\n", + " gears_sum (int): Sum of the products of the corresponding numbers.\n", + "\n", + " \"\"\"\n", " gears_sum = 0\n", - " # for each '*' in symbol_dict, check if in x+-1 and y+-1 there are exactly two numbers\n", " for (x, y) in gears:\n", - " # check if there are exactly two numbers in (x-1 to x+1) and (y-1 to y+1)\n", - " xs = [x-1, x, x+1]\n", - " ys = [y-1, y, y+1]\n", - " # take only the xs and ys >= 0\n", - " xs = [x1 for x1 in xs if x1 >= 0]\n", - " ys = [y1 for y1 in ys if y1 >= 0]\n", + " xs = [x1 for x1 in [x-1, x, x+1] if x1 >= 0]\n", + " ys = [y1 for y1 in [y-1, y, y+1] if y1 >= 0]\n", " pos = [(x1, y1) for x1 in xs for y1 in ys if (x1, y1) != (x, y)]\n", - " # get the idx of the numbers in the positions\n", - " idxs = [numbers_dict.get(p) for p in pos if numbers_dict.get(p) is not None]\n", - " idxs = list(set(idxs))\n", + " idxs = {numbers_dict.get(p) for p in pos if numbers_dict.get(p) is not None}\n", " if len(idxs) == 2:\n", - " # get the numbers\n", - " nums = [numbers_idx.get(i) for i in idxs]\n", - " # multiply the numbers\n", - " gears_sum += int(nums[0]) * int(nums[1])\n", + " nums = list(map(int, [numbers_idx.get(i) for i in idxs]))\n", + " gears_sum += nums[0] * nums[1]\n", "\n", " return gears_sum\n", "\n", - "scan_grid_for_gears(*process_input_for_gears(example_input, True))" + "assert(scan_grid_for_gears(*process_input_for_gears(example_input, True)) == 467835)" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -624,9 +642,9 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | 1 | . | 3 |\n", - " 1 | . | 5 | * |\n", - " 2 | . | . | 9 |\n" + "0 | 1 | . | 3 |\n", + "1 | . | 5 | * |\n", + "2 | . | . | 9 |\n" ] } ], @@ -640,7 +658,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -649,8 +667,8 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | * | . | * |\n", - " 1 | 1 | 2 | 3 |\n" + "0 | * | . | * |\n", + "1 | 1 | 2 | 3 |\n" ] } ], @@ -663,7 +681,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -672,8 +690,8 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | . | * | . |\n", - " 1 | 1 | . | 3 |\n" + "0 | . | * | . |\n", + "1 | 1 | . | 3 |\n" ] } ], @@ -686,7 +704,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -695,7 +713,7 @@ "text": [ " | 0 | 1 | 2 |\n", " ----------------\n", - " 0 | * | * | * |\n" + "0 | * | * | * |\n" ] } ], @@ -707,7 +725,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -716,9 +734,9 @@ "text": [ " | 0 | 1 | 2 | 3 | 4 | 5 |\n", " ----------------------------\n", - " 0 | 1 | 1 | 1 | * | 1 | 2 |\n", - " 1 | * | . | . | . | 8 | . |\n", - " 2 | . | . | 1 | 3 | . | * |\n" + "0 | 1 | 1 | 1 | * | 1 | 2 |\n", + "1 | * | . | . | . | 8 | . |\n", + "2 | . | . | 1 | 3 | . | * |\n" ] } ], @@ -734,7 +752,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -743,9 +761,9 @@ "text": [ " | 0 | 1 | 2 | 3 | 4 | 5 |\n", " ----------------------------\n", - " 0 | 1 | 1 | 1 | * | 1 | 2 |\n", - " 1 | * | . | . | . | 8 | . |\n", - " 2 | . | . | 1 | 3 | * | * |\n" + "0 | 1 | 1 | 1 | * | 1 | 2 |\n", + "1 | * | . | . | . | 8 | . |\n", + "2 | . | . | 1 | 3 | * | * |\n" ] } ], @@ -761,7 +779,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -770,9 +788,9 @@ "text": [ " | 0 | 1 | 2 | 3 | 4 | 5 |\n", " ----------------------------\n", - " 0 | 1 | 1 | 1 | * | 1 | 2 |\n", - " 1 | * | . | * | . | 8 | . |\n", - " 2 | . | . | 1 | 3 | * | * |\n" + "0 | 1 | 1 | 1 | * | 1 | 2 |\n", + "1 | * | . | * | . | 8 | . |\n", + "2 | . | . | 1 | 3 | * | * |\n" ] } ], @@ -788,7 +806,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -797,7 +815,7 @@ "82818007" ] }, - "execution_count": 24, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" }