Created
November 29, 2024 18:28
-
-
Save selimrbd/4313c8ad3e3afb7fe6f5f5a739d50911 to your computer and use it in GitHub Desktop.
My Dialog
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"cells": [{"cell_type": "markdown", "metadata": {}, "source": "[LINK](https://adventofcode.com/2023/day/2)"}, {"cell_type": "markdown", "metadata": {}, "source": "# Problem statement \n\nYou're launched high into the atmosphere! The apex of your trajectory just barely reaches the surface of a large island floating in the sky. You gently land in a fluffy pile of leaves. It's quite cold, but you don't see much snow. An Elf runs over to greet you.\n\nThe Elf explains that you've arrived at Snow Island and apologizes for the lack of snow. He'll be happy to explain the situation, but it's a bit of a walk, so you have some time. They don't get many visitors up here; would you like to play a game in the meantime?\n\nAs you walk, the Elf shows you a small bag and some cubes which are either red, green, or blue. Each time you play this game, he will hide a secret number of cubes of each color in the bag, and your goal is to figure out information about the number of cubes.\n\nTo get information, once a bag has been loaded with cubes, the Elf will reach into the bag, grab a handful of random cubes, show them to you, and then put them back in the bag. He'll do this a few times per game.\n\nYou play several games and record the information from each game (your puzzle input). Each game is listed with its ID number (like the 11 in Game 11: ...) followed by a semicolon-separated list of subsets of cubes that were revealed from the bag (like 3 red, 5 green, 4 blue).\n\nFor example, the record of a few games might look like this:\n\nGame 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green\nGame 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue\nGame 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red\nGame 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red\nGame 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green\nIn game 1, three sets of cubes are revealed from the bag (and then put back again). The first set is 3 blue cubes and 4 red cubes; the second set is 1 red cube, 2 green cubes, and 6 blue cubes; the third set is only 2 green cubes.\n\nThe Elf would first like to know which games would have been possible if the bag contained only 12 red cubes, 13 green cubes, and 14 blue cubes?\n\nIn the example above, games 1, 2, and 5 would have been possible if the bag had been loaded with that configuration. However, game 3 would have been impossible because at one point the Elf showed you 20 red cubes at once; similarly, game 4 would also have been impossible because the Elf showed you 15 blue cubes at once. If you add up the IDs of the games that would have been possible, you get 8.\n\nDetermine which games would have been possible if the bag had been loaded with only 12 red cubes, 13 green cubes, and 14 blue cubes. What is the sum of the IDs of those games?"}, {"cell_type": "code", "metadata": {}, "source": "from aocd import get_data\ndata = get_data(day=2, year=2023)\ngames = data.split(\"\\n\")", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Let's look at the data first:"}, {"cell_type": "code", "metadata": {}, "source": "games[:3]", "outputs": [{"output_type": "execute_result", "data": {"text/plain": ["['Game 1: 9 red, 2 green, 13 blue; 10 blue, 2 green, 13 red; 8 blue, 3 red, 6 green; 5 green, 2 red, 1 blue',\n", " 'Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue; 7 red, 7 green, 2 blue',\n", " 'Game 3: 6 red, 4 green, 7 blue; 7 blue, 9 red, 3 green; 2 red, 4 green; 6 red, 2 blue; 7 blue, 9 red, 5 green']"]}, "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "# Part 1"}, {"cell_type": "markdown", "metadata": {}, "source": "Let's remind ourselves of the objective:\n\n\"Determine which games would have been possible if the bag had been loaded with only 12 red cubes, 13 green cubes, and 14 blue cubes. What is the sum of the IDs of those games?\""}, {"cell_type": "markdown", "metadata": {}, "source": "First question: **What makes a game \"impossible\" ?**"}, {"cell_type": "markdown", "metadata": {}, "source": "Well, if one of the reveal has a greater number of cubes in one of the colors than the expected number, that game is impossible for the given set of cubes (12 red, 13 green, 14 blue)"}, {"cell_type": "markdown", "metadata": {}, "source": "For example:\n\nThe first reveal of game 2 yields: 2 green, 2 blue, 16 red.\n\n\"16 red\" is impossible, given we expect to have only 12 red cubes in the bag --> Game 2 is therefore impossible"}, {"cell_type": "markdown", "metadata": {}, "source": "Here's my first idea to solve the challenge (as pseudo code):\n\n```\nfor each game:\n format all reveals in a standardized way\n for each reveal:\n check that the number of RED cubes is LESS THAN OR EQUAL TO 12\n check that the number of GREEN cubes is LESS THAN OR EQUAL TO 13\n check that the number of BLUE cubes is LESS THAN OR EQUAL TO 14\n\n if one of these checks fails, the game is impossible --> return FALSE\n otherwise: move on to the next reveal \n \n if all the reveals are possible, the game is possible --> return TRUE\n```"}, {"cell_type": "markdown", "metadata": {}, "source": "Let's think of **the ideal format each reveal should be in** to compare each one with our number of expected cubes (12 red, 13 green, 14 blue)"}, {"cell_type": "markdown", "metadata": {}, "source": "My first idea is to format each reveal as a tuple of integers: (nb-red, nb-green, nb-blue)"}, {"cell_type": "markdown", "metadata": {}, "source": "If each reveal is formatted as such \n\n```\nreveal = '9 red, 2 green, 13 blue'\n\nformatted reveal = (9, 2, 13) #(red, green, blue)\n```\n\nThen we could easily compare the reveal with the expected number of cubes: (12, 13, 14) by using \"zip\" in python:"}, {"cell_type": "code", "metadata": {}, "source": "def is_reveal_possible(reveal: tuple[int, int, int], expected_cubes: tuple[int, int, int] = (12, 13, 14)) -> bool:\n return all([(nb_drawn <= max_expected) for (nb_drawn, max_expected) in zip(reveal, expected_cubes)])\n\n## example\nreveal = (1, 5, 3)\nprint(reveal)\nprint(f\"is the reveal possible ? {is_reveal_possible(reveal)}\") # we expect this to be True\n\nreveal = (16, 2, 2)\nprint(reveal)\nprint(f\"is the reveal possible ? {is_reveal_possible(reveal)}\") # we expect this to be False", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["(1, 5, 3)\n", "is the reveal possible ? True\n", "(16, 2, 2)\n", "is the reveal possible ? False\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Shoot, when looking back at the data I realize that **all reveals don't necessarily contain at least 1 cube of each color !**\n\nEx: we could have a reveal with only reds and blues.\n\nHow should we represent this in our data ?"}, {"cell_type": "markdown", "metadata": {}, "source": "IDEA: We could put a None instead of an integer if no cube of that color is drawn:\n\nexample:\n\nif the reveal is : 16 reds, 3 blue\n\nThe tuple representing it would be:\n\n```\n(16, None, 3)\n```"}, {"cell_type": "markdown", "metadata": {}, "source": "Hmm.. I need to modify `is_reveal_possible` to account for potential None values, otherwise the code will crash\n\nLet's test that it crashes first:"}, {"cell_type": "code", "metadata": {}, "source": "reveal = (16, None, 3)\nprint(reveal)\nprint(f\"is the reveal possible ? {is_reveal_possible(reveal)}\") # we expect this to be False", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["(16, None, 3)\n", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)\n", "Cell \u001b[0;32mIn[1], line 3\u001b[0m\n", "\u001b[1;32m 1\u001b[0m reveal \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m16\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m3\u001b[39m)\n", "\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(reveal)\n", "\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mis the reveal possible ? \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mis_reveal_possible\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreveal\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;66;03m# we expect this to be False\u001b[39;00m\n", "\n", "Cell \u001b[0;32mIn[1], line 2\u001b[0m, in \u001b[0;36mis_reveal_possible\u001b[0;34m(reveal, expected_cubes)\u001b[0m\n", "\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mis_reveal_possible\u001b[39m(reveal: \u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mint\u001b[39m, \u001b[38;5;28mint\u001b[39m, \u001b[38;5;28mint\u001b[39m], expected_cubes: \u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mint\u001b[39m, \u001b[38;5;28mint\u001b[39m, \u001b[38;5;28mint\u001b[39m] \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m12\u001b[39m, \u001b[38;5;241m13\u001b[39m, \u001b[38;5;241m14\u001b[39m)) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mbool\u001b[39m:\n", "\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mall\u001b[39m(\u001b[43m[\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnb_drawn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m<\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mmax_expected\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43mnb_drawn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_expected\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mzip\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mreveal\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexpected_cubes\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m)\n", "\n", "Cell \u001b[0;32mIn[1], line 2\u001b[0m, in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n", "\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mis_reveal_possible\u001b[39m(reveal: \u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mint\u001b[39m, \u001b[38;5;28mint\u001b[39m, \u001b[38;5;28mint\u001b[39m], expected_cubes: \u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mint\u001b[39m, \u001b[38;5;28mint\u001b[39m, \u001b[38;5;28mint\u001b[39m] \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m12\u001b[39m, \u001b[38;5;241m13\u001b[39m, \u001b[38;5;241m14\u001b[39m)) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mbool\u001b[39m:\n", "\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mall\u001b[39m([(\u001b[43mnb_drawn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m<\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mmax_expected\u001b[49m) \u001b[38;5;28;01mfor\u001b[39;00m (nb_drawn, max_expected) \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(reveal, expected_cubes)])\n", "\n", "\u001b[0;31mTypeError\u001b[0m: '<=' not supported between instances of 'NoneType' and 'int'\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "A small modification in the `is_reveal_possible` function will solve the bug:"}, {"cell_type": "code", "metadata": {}, "source": "def is_reveal_possible(reveal: tuple[int, int, int], expected_cubes: tuple[int, int, int] = (12, 13, 14)) -> bool:\n return all([(nb_drawn <= max_expected) for (nb_drawn, max_expected) in zip(reveal, expected_cubes) if nb_drawn is not None])\n\n## let's test that it doesn't crash anymore\nreveal = (16, None, 3)\nprint(reveal)\nprint(f\"is the reveal possible ? {is_reveal_possible(reveal)}\") # we expect this to be False", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["(16, None, 3)\n", "is the reveal possible ? False\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Ok ! \n\nWhat next ?\n\nLet's write a function that checks if a game is possible: `is_game_possible`\n\nHow should we format each game data ?\n\nHow about a dictionary:"}, {"cell_type": "code", "metadata": {}, "source": "line = 'Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue; 7 red, 7 green, 2 blue'\n\noutput = {\n 2: [\n (16, 2, 2), #2 green, 2 blue, 16 red\n (14, None, None), #14 red\n (13, 13, 2), #13 red, 13 green, 2 blue\n (7, 7, 2), #7 red, 7 green, 2 blue\n ]\n}", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Each dictionary **key** represents the game number\n\nand the **value** represents a list of reveals"}, {"cell_type": "markdown", "metadata": {}, "source": "Let's write the function `is_game_possible`:"}, {"cell_type": "code", "metadata": {}, "source": "def is_game_possible(reveals: list[tuple[int, int, int]], expected_cubes: tuple[int, int, int] = (12,13,14)):\n for reveal in reveals:\n if not is_reveal_possible(reveal, expected_cubes):\n return False \n return True", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Explanation:\n\nWe go through each reveal. If one of the reveal is impossible --> the game is impossible (we return False)\n\nIf all reveals are possible, then the game is possible: we exit the for loop and return True\n\nLet's test on 2 examples"}, {"cell_type": "code", "metadata": {}, "source": "## this game should be possible\ngame_reveals = [(10, 2, 2), (12, None, 14)]\nprint(f\"game reveals: {game_reveals}\")\nprint(f\"is the game possible ? {is_game_possible(game_reveals)}\")\n\n\n## this game should NOT be possible\ngame_reveals = [(16, 2, 2), (12, None, 14)]\nprint(f\"game reveals: {game_reveals}\")\nprint(f\"is the game possible ? {is_game_possible(game_reveals)}\")", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["game reveals: [(10, 2, 2), (12, None, 14)]\n", "is the game possible ? True\n", "game reveals: [(16, 2, 2), (12, None, 14)]\n", "is the game possible ? False\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Great ! we now have a way to check:\n- if a reveal is possible \n- if a game is possible \n\nWhat next ?\n\nRemember the answer to the question should be the sum of all the game IDs.\n\nLet's write the function that calculates this:"}, {"cell_type": "code", "metadata": {}, "source": "def solve_day2_part1(games: dict[int, list[tuple[int, int, int]]], expected_cubes: list[int, int, int] = (12, 13, 14)) -> int:\n \"\"\"\n games: a dict where the key is the game number and the value is a list of all reveals \n \"\"\"\n output = 0\n for game_number, reveals in games.items():\n if is_game_possible(reveals, expected_cubes):\n output += game_number\n return output", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Let's try it on some examples:"}, {"cell_type": "code", "metadata": {}, "source": "## let's try it\n\nexpected_cubes = (12, 13, 14)\n\n## all games are possible --> answer should be 6\ngames = {\n 1: [(11,None,11), (1, 1, 10)], \n 2: [(9,12,None), (1, 2, 9)], \n 3: [(7,None,None), (8, 1, 4)],\n}\nexpected_answer = 6\nanswer = solve_day2_part1(games, expected_cubes)\nprint(f\"games: {games}\")\nprint(f\"expected answer: {expected_answer}\")\nprint(f\"answer: {answer}\")\n\nprint('-----------')\n\n## only game 3 is possible --> answer should be 3\ngames = {\n 1: [(16,None,11), (1, 1, 10)], \n 2: [(13,12,None), (1, 2, 9)], \n 3: [(7,None,None), (8, 1, 4)],\n}\nexpected_answer = 3\nanswer = solve_day2_part1(games, expected_cubes)\nprint(f\"games: {games}\")\nprint(f\"expected answer: {expected_answer}\")\nprint(f\"answer: {answer}\")", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["games: {1: [(11, None, 11), (1, 1, 10)], 2: [(9, 12, None), (1, 2, 9)], 3: [(7, None, None), (8, 1, 4)]}\n", "expected answer: 6\n", "answer: 6\n", "-----------\n", "games: {1: [(16, None, 11), (1, 1, 10)], 2: [(13, 12, None), (1, 2, 9)], 3: [(7, None, None), (8, 1, 4)]}\n", "expected answer: 3\n", "answer: 3\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "What is left to do ?\n\nThe only thing missing now is **parsing the input into the expected format for all our functions to work**.\n\nLet's first write a function to parse a reveal to the expected format: `parse_reveal`\n\nA reveal line looks like this:\n\n\"2 green, 2 blue, 16 red\"\n\nWe can split the line on the comma \",\"\n\nWhat should we be careful about ? Let's see:\n- We need to make sure the number for each color **goes in the right index of the tuple**.\n- We can't match the cube color by position: they can be given in any order.\n- We have to account for the possibility of no cubes being drawn for a particular color.\n\nHere's my first try:"}, {"cell_type": "code", "metadata": {}, "source": "def parse_reveal(line: str) -> tuple[int, int, int]:\n \n output = (None, None, None)\n color_position = {\"red\": 0, \"green\": 1, \"blue\": 2}\n cubes = line.split(',')\n for cube in cubes:\n number, color = cube.split(' ')\n number = int(number) # transform as an integer\n position = color_position[color]\n output[position] = number\n return output\n\nreveal = \" 5 green, 2 red, 1 blue\"\nprint(f'reveal: {reveal}')\nprint(f'parsed reveal: {parse_reveal(reveal)}')", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["reveal: 5 green, 2 red, 1 blue\n", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)\n", "Cell \u001b[0;32mIn[1], line 15\u001b[0m\n", "\u001b[1;32m 13\u001b[0m reveal \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m 5 green, 2 red, 1 blue\u001b[39m\u001b[38;5;124m\"\u001b[39m\n", "\u001b[1;32m 14\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mreveal: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mreveal\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[0;32m---> 15\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mparsed reveal: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mparse_reveal\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreveal\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", "\n", "Cell \u001b[0;32mIn[1], line 7\u001b[0m, in \u001b[0;36mparse_reveal\u001b[0;34m(line)\u001b[0m\n", "\u001b[1;32m 5\u001b[0m cubes \u001b[38;5;241m=\u001b[39m line\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m,\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m cube \u001b[38;5;129;01min\u001b[39;00m cubes:\n", "\u001b[0;32m----> 7\u001b[0m number, color \u001b[38;5;241m=\u001b[39m cube\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[1;32m 8\u001b[0m number \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(number) \u001b[38;5;66;03m# transform as an integer\u001b[39;00m\n", "\u001b[1;32m 9\u001b[0m position \u001b[38;5;241m=\u001b[39m color_position[color]\n", "\n", "\u001b[0;31mValueError\u001b[0m: too many values to unpack (expected 2)\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Looks like my code has a bug, let's add print statements to see what's going on:"}, {"cell_type": "code", "metadata": {}, "source": "def parse_reveal(line: str) -> tuple[int, int, int]:\n \n output = (None, None, None)\n color_position = {\"red\": 0, \"green\": 1, \"blue\": 2}\n cubes = line.split(',')\n print(f'cubes: {cubes}')\n for cube in cubes:\n print(f\"cube split: {cube.split(' ')}\")\n number, color = cube.split(' ')\n number = int(number) # transform as an integer\n position = color_position[color]\n output[position] = number\n return output\n\nreveal = \" 5 green, 2 red, 1 blue\"\nprint(f'reveal: {reveal}')\nprint(f'parsed reveal: {parse_reveal(reveal)}')", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["reveal: 5 green, 2 red, 1 blue\n", "cubes: [' 5 green', ' 2 red', ' 1 blue']\n", "cube split: ['', '5', 'green']\n", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)\n", "Cell \u001b[0;32mIn[1], line 17\u001b[0m\n", "\u001b[1;32m 15\u001b[0m reveal \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m 5 green, 2 red, 1 blue\u001b[39m\u001b[38;5;124m\"\u001b[39m\n", "\u001b[1;32m 16\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mreveal: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mreveal\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[0;32m---> 17\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mparsed reveal: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mparse_reveal\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreveal\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", "\n", "Cell \u001b[0;32mIn[1], line 9\u001b[0m, in \u001b[0;36mparse_reveal\u001b[0;34m(line)\u001b[0m\n", "\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m cube \u001b[38;5;129;01min\u001b[39;00m cubes:\n", "\u001b[1;32m 8\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcube split: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mcube\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m'\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;32m----> 9\u001b[0m number, color \u001b[38;5;241m=\u001b[39m cube\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[1;32m 10\u001b[0m number \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(number) \u001b[38;5;66;03m# transform as an integer\u001b[39;00m\n", "\u001b[1;32m 11\u001b[0m position \u001b[38;5;241m=\u001b[39m color_position[color]\n", "\n", "\u001b[0;31mValueError\u001b[0m: too many values to unpack (expected 2)\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Oh I see, the **trailing spaces** are messing the parsing, we should strip them before getting the number and color:"}, {"cell_type": "code", "metadata": {}, "source": "def parse_reveal(line: str) -> tuple[int, int, int]:\n \n output = (None, None, None)\n color_position = {\"red\": 0, \"green\": 1, \"blue\": 2}\n cubes = line.split(',')\n for cube in cubes:\n number, color = cube.strip().split(' ')\n number = int(number) # transform as an integer\n print(f\"number: '{number}'\")\n print(f\"color: '{color}'\")\n position = color_position[color]\n output[position] = number\n return output\n\nreveal = \" 5 green, 2 red, 1 blue\"\nprint(f'reveal: {reveal}')\nprint(f'parsed reveal: {parse_reveal(reveal)}')", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["reveal: 5 green, 2 red, 1 blue\n", "number: '5'\n", "color: 'green'\n", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)\n", "Cell \u001b[0;32mIn[1], line 17\u001b[0m\n", "\u001b[1;32m 15\u001b[0m reveal \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m 5 green, 2 red, 1 blue\u001b[39m\u001b[38;5;124m\"\u001b[39m\n", "\u001b[1;32m 16\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mreveal: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mreveal\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[0;32m---> 17\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mparsed reveal: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mparse_reveal\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreveal\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", "\n", "Cell \u001b[0;32mIn[1], line 12\u001b[0m, in \u001b[0;36mparse_reveal\u001b[0;34m(line)\u001b[0m\n", "\u001b[1;32m 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcolor: \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mcolor\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[1;32m 11\u001b[0m position \u001b[38;5;241m=\u001b[39m color_position[color]\n", "\u001b[0;32m---> 12\u001b[0m \u001b[43moutput\u001b[49m\u001b[43m[\u001b[49m\u001b[43mposition\u001b[49m\u001b[43m]\u001b[49m \u001b[38;5;241m=\u001b[39m number\n", "\u001b[1;32m 13\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m output\n", "\n", "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Looks like we resolved the trailing spaces problem, but there's another one ...\n\nRight, I forgot that **tuples in python are immutable**, meaning that we can't modify them after they were created.\n\nTo solve this, let's first create a list, then cast that list as a tuple just before returning the function result:"}, {"cell_type": "code", "metadata": {}, "source": "def parse_reveal(line: str) -> tuple[int, int, int]:\n \n output = [None, None, None]\n color_position = {\"red\": 0, \"green\": 1, \"blue\": 2}\n cubes = line.split(',')\n for cube in cubes:\n number, color = cube.strip().split(' ')\n number = int(number) # transform as an integer\n position = color_position[color]\n output[position] = number\n \n output = tuple(output)\n return output\n\nreveal = \" 5 green, 2 red, 1 blue\"\nprint(f'reveal: {reveal}')\nprint(f'parsed reveal: {parse_reveal(reveal)}')\n\nreveal = \"2 red\"\nprint(f'reveal: {reveal}')\nprint(f'parsed reveal: {parse_reveal(reveal)}')", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["reveal: 5 green, 2 red, 1 blue\n", "parsed reveal: (2, 5, 1)\n", "reveal: 2 red\n", "parsed reveal: (2, None, None)\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "Looks like it works ! \n\nOk, just one final step is missing:\n\na function to parse the entire game line: `parse_game`"}, {"cell_type": "markdown", "metadata": {}, "source": "A game line looks like: \"Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue;\"\n\nTo get the game number, we isolate what's left of the \":\" and remove the \"Game\" string, something like\n\n```\ngame_line = \"Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue;\"\ngame_nb_raw, reveals_raw = game_line.split(':')\ngame_nb = game_nb_raw.strip().split(' ')[1]\ngame_nb = int(game_nb)\n```\n\nOr in one line:\n\n```\ngame_line = \"Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue;\"\ngame_nb = int(game_line.split(':').strip().split(' ')[1])\n```\n\nAnd to parse each reveal, we already have a function ! \n\nLet's combine everything in one function:"}, {"cell_type": "code", "metadata": {}, "source": "def parse_game(line: str) -> dict[int, list[tuple[int, int, int]]]:\n game_nb_raw, reveals_raw = game_line.split(':')\n game_nb = int(game_nb_raw.strip().split(' ')[1])\n reveals = reveals_raw.strip().split(';')\n reveals_formatted = [parse_reveal(reveal) for reveal in reveals]\n return {game_nb: reveals_formatted}", "outputs": [], "images": [], "execution_count": null}, {"cell_type": "code", "metadata": {}, "source": "## let's test the function\n\ngame_line = \"Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue;\"\nprint(f'game line: {game_line}')\nprint(f'parsed game line: {parse_game(game_line)}')", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["game line: Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue;\n", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)\n", "Cell \u001b[0;32mIn[1], line 5\u001b[0m\n", "\u001b[1;32m 3\u001b[0m game_line \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGame 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue;\u001b[39m\u001b[38;5;124m\"\u001b[39m\n", "\u001b[1;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mgame line: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mgame_line\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[0;32m----> 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mparsed game line: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mparse_game\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgame_line\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", "\n", "Cell \u001b[0;32mIn[1], line 5\u001b[0m, in \u001b[0;36mparse_game\u001b[0;34m(line)\u001b[0m\n", "\u001b[1;32m 3\u001b[0m game_nb \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(game_nb_raw\u001b[38;5;241m.\u001b[39mstrip()\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;241m1\u001b[39m])\n", "\u001b[1;32m 4\u001b[0m reveals \u001b[38;5;241m=\u001b[39m reveals_raw\u001b[38;5;241m.\u001b[39mstrip()\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m;\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[0;32m----> 5\u001b[0m reveals_formatted \u001b[38;5;241m=\u001b[39m \u001b[43m[\u001b[49m\u001b[43mparse_reveal\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreveal\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mreveal\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mreveals\u001b[49m\u001b[43m]\u001b[49m\n", "\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {game_nb: reveals_formatted}\n", "\n", "Cell \u001b[0;32mIn[1], line 5\u001b[0m, in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n", "\u001b[1;32m 3\u001b[0m game_nb \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(game_nb_raw\u001b[38;5;241m.\u001b[39mstrip()\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;241m1\u001b[39m])\n", "\u001b[1;32m 4\u001b[0m reveals \u001b[38;5;241m=\u001b[39m reveals_raw\u001b[38;5;241m.\u001b[39mstrip()\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m;\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[0;32m----> 5\u001b[0m reveals_formatted \u001b[38;5;241m=\u001b[39m [\u001b[43mparse_reveal\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreveal\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m reveal \u001b[38;5;129;01min\u001b[39;00m reveals]\n", "\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {game_nb: reveals_formatted}\n", "\n", "Cell \u001b[0;32mIn[1], line 7\u001b[0m, in \u001b[0;36mparse_reveal\u001b[0;34m(line)\u001b[0m\n", "\u001b[1;32m 5\u001b[0m cubes \u001b[38;5;241m=\u001b[39m line\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m,\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m cube \u001b[38;5;129;01min\u001b[39;00m cubes:\n", "\u001b[0;32m----> 7\u001b[0m number, color \u001b[38;5;241m=\u001b[39m cube\u001b[38;5;241m.\u001b[39mstrip()\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m'\u001b[39m)\n", "\u001b[1;32m 8\u001b[0m number \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(number) \u001b[38;5;66;03m# transform as an integer\u001b[39;00m\n", "\u001b[1;32m 9\u001b[0m position \u001b[38;5;241m=\u001b[39m color_position[color]\n", "\n", "\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 2, got 1)\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "source": "**AI Prompt**\n\nOh, looks like the trailing \";\" is messing things up\n\nAny idea on how I could remove **trailing punctuation** in a string in python ?", "metadata": {}}, {"cell_type": "markdown", "source": "**AI Response**\n\nYes! You can use Python's `rstrip()` method with a string of characters you want to remove. In this case, we want to remove both semicolons and whitespace. Here's how:\n\n```python\ntext = \"some text;\"\ncleaned = text.rstrip(';').strip()\n```\n\nWould you like to try modifying your code to use this approach?", "is_reply": true, "metadata": {}}, {"cell_type": "markdown", "metadata": {}, "source": "Nice let's do that:"}, {"cell_type": "code", "metadata": {}, "source": "def parse_game(line: str) -> dict[int, list[tuple[int, int, int]]]:\n game_nb_raw, reveals_raw = line.split(':')\n game_nb = int(game_nb_raw.strip().split(' ')[1])\n reveals = reveals_raw.rstrip(';').strip().split(';')\n reveals_formatted = [parse_reveal(reveal) for reveal in reveals]\n return {game_nb: reveals_formatted}\n\ngame_line = \"Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue;\"\nprint(f'game line: {game_line}')\nprint(f'parsed game line: {parse_game(game_line)}')", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["game line: Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue;\n", "parsed game line: {2: [(16, 2, 2), (14, None, None), (13, 13, 2)]}\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "great ! we can now finally build a function that parses all game lines:"}, {"cell_type": "markdown", "source": "**AI Prompt**\n\nIf I have a list of dicts, how can I combine them all into a single dict ?", "metadata": {}}, {"cell_type": "markdown", "source": "**AI Response**\n\nIn Python, you can combine multiple dictionaries using a few methods. For dictionary merging, I'd recommend either:\n\n1. Using the `|` operator (Python 3.9+):\n```python\ncombined = dict1 | dict2 | dict3\n```\n\n2. Or using `update()` in a loop:\n```python\ncombined = {}\nfor d in list_of_dicts:\n combined.update(d)\n```\n\nWhich method would you like to try?", "is_reply": true, "metadata": {}}, {"cell_type": "code", "metadata": {}, "source": "def parse_all_games(lines: list[str]) -> dict[int, tuple[int, int, int]]:\n games_list = [parse_game(line) for line in lines]\n games = {}\n for game in games_list:\n games.update(game)\n return games \n\n\n## test \nlines = data.split(\"\\n\")[:3]\nprint(f'lines:')\nfor line in lines:\n print(line)\nprint(f'parsed lines: {parse_all_games(lines)}')", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["lines:\n", "Game 1: 9 red, 2 green, 13 blue; 10 blue, 2 green, 13 red; 8 blue, 3 red, 6 green; 5 green, 2 red, 1 blue\n", "Game 2: 2 green, 2 blue, 16 red; 14 red; 13 red, 13 green, 2 blue; 7 red, 7 green, 2 blue\n", "Game 3: 6 red, 4 green, 7 blue; 7 blue, 9 red, 3 green; 2 red, 4 green; 6 red, 2 blue; 7 blue, 9 red, 5 green\n", "parsed lines: {1: [(9, 2, 13), (13, 2, 10), (3, 6, 8), (2, 5, 1)], 2: [(16, 2, 2), (14, None, None), (13, 13, 2), (7, 7, 2)], 3: [(6, 4, 7), (9, 3, 7), (2, 4, None), (6, None, 2), (9, 5, 7)]}\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "We now have all the ingredients to answer the question:"}, {"cell_type": "code", "metadata": {}, "source": "from aocd import get_data\n\ndata = get_data(day=2, year=2023)\ngames = data.split(\"\\n\")\n\ngames_parsed = parse_all_games(games)\nexpected_cubes = (12, 13, 14)\nanswer = solve_day2_part1(games_parsed, expected_cubes)\n\nprint('final answer:')\nprint(answer)", "outputs": [{"output_type": "stream", "name": "stdout", "text": ["final answer:\n", "2283\n"], "metadata": {}, "execution_count": null}], "images": [], "execution_count": null}, {"cell_type": "markdown", "metadata": {}, "source": "I submitted the answer ... And it's correct ! \n\nRemember: your data will be different than mine, so it's expected that your answer won't be the same :)"}, {"cell_type": "markdown", "metadata": {}, "source": "## Conclusion"}, {"cell_type": "markdown", "metadata": {}, "source": "We solved this puzzle by [\"Working Backwards\"](https://gist.github.com/jph00/d60301884c56fe063101a7cc6193b3af), one of the key heuristic strategies of George Polya.\n\nWe started \"from the top\", by asking ourselves \"what makes a game impossible ?\"\n\nAnd slowly built all the functions necessary to answer the question.\n\nThis is how I intuitively tackled the puzzle, but it certainly **isn't the only way of doing it**.\n\nIf you started \"from the bottom\" (by parsing the game lines), that perfectly fine too ! \n\nIt's very common that different people tackle challenges in different ways.\n\nMy advice: when you solve a problem, look at how others have done it and compare your approaches.\n\nYou'll learn a lot by \"peaking into the brain\" of your peers, and probably discover new python features, or a new problem-solving mindset, that might help you in the future."}], "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