Skip to content

Instantly share code, notes, and snippets.

@selimrbd
Created November 30, 2024 06:19
Show Gist options
  • Select an option

  • Save selimrbd/fb4e871617858798b7927f9cd005250c to your computer and use it in GitHub Desktop.

Select an option

Save selimrbd/fb4e871617858798b7927f9cd005250c to your computer and use it in GitHub Desktop.
My Dialog
Display the source blob
Display the rendered blob
Raw
{"cells": [{"cell_type": "markdown", "metadata": {}, "source": "[LINK](https://adventofcode.com/2023/day/3)"}, {"cell_type": "markdown", "metadata": {}, "source": "# Part 1"}, {"cell_type": "code", "metadata": {}, "source": "from aocd import get_data\nengine = get_data(day=3, year=2023)\nengine_lines = data.split(\"\\n\")\n\nprint(engine_lines[:3])", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["['...733.......289..262.....520..................161.462..........450.........................183.............................................', '....*....................*.............707.352....*............/.....................801...@...............333..196........484.635......287.', '....42.........131....913..............*......&..........634..................440..&...............83.....@...........404$..=....*..423.*...']\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "What are all the possible symbols ?\n\nLet's build `is_symbol` to determine if a character is a symbol.\n\nFrom the definition, any character that isn't a digit or a period is a symbol."}, {"cell_type": "markdown", "source": "**AI Prompt**\n\nIn python how would I check if a character is not a digit ? can I do so without using a regex", "metadata": {}}, {"cell_type": "markdown", "source": "**AI Response**\n\nYes! Python has a built-in string method called `isdigit()` that checks if a character is a digit. \n\nTo check if a character is NOT a digit, you can use:\n```python\nnot character.isdigit()\n```\n\nWould you like to try writing the full `is_symbol` function yourself using this method? Remember we need to check for both non-digits AND non-periods.", "is_reply": true, "metadata": {}}, {"cell_type": "code", "metadata": {}, "source": "def is_symbol(character: str) -> bool:\n is_digit = character.isdigit()\n is_period = character == '.'\n return not is_digit and not is_period", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "def find_all_symbols(engine_lines: list[str]):\n symbols = list()\n for line in engine_lines:\n for character in line:\n if is_symbol(character):\n symbols.append(character)\n return set(symbols)", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "symbols = find_all_symbols(engine_lines)\nsymbols", "outputs": [{"output_type": "execute_result", "data": {"text/plain": ["{'#', '$', '%', '&', '*', '+', '-', '/', '=', '@'}"]}, "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "import re \n\ndef extract_numbers(engine_lines: list[str]) -> list[list[int]]:\n numbers = list()\n for line in engine_lines:\n numbers.append(re.findall('\\d+', line))\n return numbers", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "inp = engine_lines[:2]\nprint('input:')\nprint(inp)\nprint('extracted numbers:')\nprint(extract_numbers(inp))", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["input:\n", "['...733.......289..262.....520..................161.462..........450.........................183.............................................', '....*....................*.............707.352....*............/.....................801...@...............333..196........484.635......287.']\n", "extracted numbers:\n", "[['733', '289', '262', '520', '161', '462', '450', '183'], ['707', '352', '801', '333', '196', '484', '635', '287']]\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "source": "**AI Prompt**\n\nI want to match all digits on a line but also get the beginning and ending position", "metadata": {}}, {"cell_type": "markdown", "source": "**AI Response**\n\nThere are a couple of ways to do this without regex. One simple approach would be to iterate through the line and track positions. Would you like to try implementing this yourself? \n\nHere's a way to think about it:\n1. Keep track of the current number and its start position\n2. When you find a digit, either start a new number or continue the current one\n3. When you find a non-digit, save the completed number and its positions\n\nWould you like to try implementing this, or would you like me to show you how?", "is_reply": true, "metadata": {}}, {"cell_type": "markdown", "source": "**AI Prompt**\n\nIsn't there an equivalent to \"findall\" that also gives the positions ?", "metadata": {}}, {"cell_type": "markdown", "source": "**AI Response**\n\nYes! You can use `re.finditer()` which returns match objects that include the positions. Each match object has `.start()` and `.end()` methods to get the positions, and `.group()` to get the actual text that matched.\n\nWould you like to see an example of how to use it?", "is_reply": true, "metadata": {}}, {"cell_type": "code", "metadata": {}, "source": "ex = '...345..34.'\nfor element in re.finditer('\\d+', ex):\n print(element)\n print(element.group())\n print(element.start())\n print(element.end())", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["<re.Match object; span=(3, 6), match='345'>\n", "345\n", "3\n", "6\n", "<re.Match object; span=(8, 10), match='34'>\n", "34\n", "8\n", "10\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "I want to adapt extract_numbers, so that I also get the start and end position of the digit.\n\nWith this information, I can get the range of positions to check for a symbol (next function we will create)"}, {"cell_type": "code", "metadata": {}, "source": "def extract_numbers(line: str) -> list[list[int]]:\n numbers = list()\n for n in re.finditer('\\d+', line):\n numbers.append((int(n.group()), n.start(), n.end()))\n return numbers", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "inp = engine_lines[2]\nprint('input:')\nprint(inp)\nprint('extracted numbers:')\nprint(extract_numbers(inp))", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["input:\n", "....42.........131....913..............*......&..........634..................440..&...............83.....@...........404$..=....*..423.*...\n", "extracted numbers:\n", "[(42, 4, 6), (131, 15, 18), (913, 22, 25), (634, 57, 60), (440, 78, 81), (83, 99, 101), (404, 118, 121), (423, 132, 135)]\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Now let's build `get_index_adjacent_horizontal` that retrieves the indexes to check for a symbol on the **same** line of the digit"}, {"cell_type": "code", "metadata": {}, "source": "def get_index_adjacent_horizontal(start: int, end: int, line_length: int):\n indexes = list()\n if start > 0:\n indexes.append(start-1)\n if end < line_length:\n indexes.append(end)\n return indexes", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "print(get_index_adjacent_horizontal(3, 5, 10))\nprint(get_index_adjacent_horizontal(3, 5, 5))\nprint(get_index_adjacent_horizontal(0, 5, 10))", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["[2, 5]\n", "[2]\n", "[5]\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "How about the lines above and below the line ? You have to account for:\n- the full range of the digit\n- potential diagonal matches"}, {"cell_type": "code", "metadata": {}, "source": "def get_index_adjacent_vertical(start: int, end: int, line_length: int):\n i1 = start - 1 if start > 0 else start \n i2 = end + 1 if end < line_length else end\n return list(range(i1, i2))", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "print(get_index_adjacent_vertical(3, 5, 10))\nprint(get_index_adjacent_vertical(0, 5, 10))\nprint(get_index_adjacent_vertical(3, 5, 5))", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["[2, 3, 4, 5]\n", "[0, 1, 2, 3, 4, 5]\n", "[2, 3, 4]\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "def is_part_number(engine_lines, line_nb, start, end):\n line_length = len(engine_lines[0])\n index_h = get_index_adjacent_horizontal(start, end, line_length)\n index_v = get_index_adjacent_vertical(start, end, line_length)\n\n characters = [engine_lines[line_nb][i] for i in index_h]\n if line_nb > 0:\n characters += [engine_lines[line_nb-1][i] for i in index_v]\n if line_nb < len(engine_lines) - 1:\n characters += [engine_lines[line_nb+1][i] for i in index_v]\n \n return any([c in symbols for c in characters])", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "inp = engine_lines[:3]\nprint('input:')\nprint(inp)\nprint(extract_numbers(inp[0]))\nprint(f'733: {is_part_number(inp, 0, 3, 6)}')\nprint(f'289: {is_part_number(inp, 0, 13, 16)}')", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["input:\n", "['...733.......289..262.....520..................161.462..........450.........................183.............................................', '....*....................*.............707.352....*............/.....................801...@...............333..196........484.635......287.', '....42.........131....913..............*......&..........634..................440..&...............83.....@...........404$..=....*..423.*...']\n", "[(733, 3, 6), (289, 13, 16), (262, 18, 21), (520, 26, 29), (161, 47, 50), (462, 51, 54), (450, 64, 67), (183, 92, 95)]\n", "733: True\n", "289: False\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "def solve_day3_part1(engine_lines: list[str]):\n total = 0\n for i, line in enumerate(engine_lines):\n numbers = extract_numbers(line)\n for number in numbers:\n nb, start, end = number\n if is_part_number(engine_lines, i, start, end):\n total += nb\n return total", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "solve_day3_part1(engine_lines)", "outputs": [{"output_type": "execute_result", "data": {"text/plain": ["525181"]}, "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "# Part 2"}, {"cell_type": "code", "metadata": {}, "source": "ex = \"...*..*..\"\nfor e in re.finditer('\\*', ex):\n print(e)", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["<re.Match object; span=(3, 4), match='*'>\n", "<re.Match object; span=(6, 7), match='*'>\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "def extract_gears(line: str) -> tuple[int, int]:\n gears = list()\n for e in re.finditer('\\*', line):\n gears.append(e.start())\n return gears", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "ex = \"...*..*..\"\nextract_gears(ex)", "outputs": [{"output_type": "execute_result", "data": {"text/plain": ["[3, 6]"]}, "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "def is_number_adjacent_h(start, end, gear_index):\n return (end == gear_index) or (start == gear_index + 1)\n\ndef is_number_adjacent_v(start, end, gear_index):\n return ((end - 1) >= gear_index - 1) and (start <= gear_index + 1)", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "def extract_adjacent_numbers(engine_lines, gear_line, gear_index):\n \n adjacent = list()\n numbers = extract_numbers(engine_lines[gear_line])\n \n adjacent = [nb for (nb, start, end) in numbers if is_number_adjacent_h(start, end, gear_index)]\n\n if gear_line > 0:\n numbers = extract_numbers(engine_lines[gear_line - 1])\n adjacent += [nb for (nb, start, end) in numbers if is_number_adjacent_v(start, end, gear_index)]\n \n if gear_line < len(engine_lines) - 1:\n numbers = extract_numbers(engine_lines[gear_line + 1])\n adjacent += [nb for (nb, start, end) in numbers if is_number_adjacent_v(start, end, gear_index)]\n \n return adjacent", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "inp = engine_lines[:3]\nprint('input:')\nprint(inp)\nprint(extract_adjacent_numbers(engine_lines, 1, 4))", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["input:\n", "['...733.......289..262.....520..................161.462..........450.........................183.............................................', '....*....................*.............707.352....*............/.....................801...@...............333..196........484.635......287.', '....42.........131....913..............*......&..........634..................440..&...............83.....@...........404$..=....*..423.*...']\n", "[733, 42]\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "def solve_day3_part2(engine_lines) -> int:\n\n total = 0\n for i, line in enumerate(engine_lines):\n gears = extract_gears(line)\n for gear_index in gears:\n numbers = extract_adjacent_numbers(engine_lines, i, gear_index)\n if len(numbers) == 2:\n total += numbers[0] * numbers[1]\n return total", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "answer = solve_day3_part2(engine_lines)\nprint(answer)", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["84289137\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}], "metadata": {"kernelspec": {"display_name": "python3", "language": "python", "name": "python3"}}, "nbformat": 4, "nbformat_minor": 4}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment