diff --git a/2024/10/10.md b/2024/10/10.md new file mode 100644 index 0000000..3c2e6aa --- /dev/null +++ b/2024/10/10.md @@ -0,0 +1,106 @@ +## \-\-- Day 10: Hoof It \-\-- + +You all arrive at a [Lava Production Facility](/2023/day/15) on a +floating island in the sky. As the others begin to search the massive +industrial complex, you feel a small nose boop your leg and look down to +discover a reindeer wearing a hard +hat. + +The reindeer is holding a book titled \"Lava Island Hiking Guide\". +However, when you open the book, you discover that most of it seems to +have been scorched by lava! As you\'re about to ask how you can help, +the reindeer brings you a blank [topographic +map](https://en.wikipedia.org/wiki/Topographic_map) of +the surrounding area (your puzzle input) and looks up at you excitedly. + +Perhaps you can help fill in the missing hiking trails? + +The topographic map indicates the *height* at each position using a +scale from `0` (lowest) to `9` (highest). For example: + + 0123 + 1234 + 8765 + 9876 + +Based on un-scorched scraps of the book, you determine that a good +hiking trail is *as long as possible* and has an *even, gradual, uphill +slope*. For all practical purposes, this means that a *hiking trail* is +any path that starts at height `0`, ends at height `9`, and always +increases by a height of exactly 1 at each step. Hiking trails never +include diagonal steps - only up, down, left, or right (from the +perspective of the map). + +You look up from the map and notice that the reindeer has helpfully +begun to construct a small pile of pencils, markers, rulers, compasses, +stickers, and other equipment you might need to update the map with +hiking trails. + +A *trailhead* is any position that starts one or more hiking trails - +here, these positions will always have height `0`. Assembling more +fragments of pages, you establish that a trailhead\'s *score* is the +number of `9`-height positions reachable from that trailhead via a +hiking trail. In the above example, the single trailhead in the top left +corner has a score of `1` because it can reach a single `9` (the one in +the bottom left). + +This trailhead has a score of `2`: + + ...0... + ...1... + ...2... + 6543456 + 7.....7 + 8.....8 + 9.....9 + +(The positions marked `.` are impassable tiles to simplify these +examples; they do not appear on your actual topographic map.) + +This trailhead has a score of `4` because every `9` is reachable via a +hiking trail except the one immediately to the left of the trailhead: + + ..90..9 + ...1.98 + ...2..7 + 6543456 + 765.987 + 876.... + 987.... + +This topographic map contains *two* trailheads; the trailhead at the top +has a score of `1`, while the trailhead at the bottom has a score of +`2`: + + 10..9.. + 2...8.. + 3...7.. + 4567654 + ...8..3 + ...9..2 + .....01 + +Here\'s a larger example: + + 89010123 + 78121874 + 87430965 + 96549874 + 45678903 + 32019012 + 01329801 + 10456732 + +This larger example has 9 trailheads. Considering the trailheads in +reading order, they have scores of `5`, `6`, `5`, `3`, `1`, `3`, `5`, +`3`, and `5`. Adding these scores together, the sum of the scores of all +trailheads is `36`. + +The reindeer gleefully carries over a protractor and adds it to the +pile. *What is the sum of the scores of all trailheads on your +topographic map?* + +To begin, [get your puzzle input](10/input). + +Answer: + diff --git a/2024/10/solution.py b/2024/10/solution.py new file mode 100644 index 0000000..61e99a5 --- /dev/null +++ b/2024/10/solution.py @@ -0,0 +1,31 @@ +#!/bin/python3 +import sys,time,re +from pprint import pprint +sys.path.insert(0, '../../') +from fred import list2int,get_re,nprint,lprint,loadFile,toGrid,TSP,dijkstra +start_time = time.time() + +input_f = 'test' + +part = 1 +######################################### +# # +# Part 1 # +# # +######################################### + +if part == 1: + grid = toGrid(input_f,True) + nprint(grid) + + print(dijkstra(grid,0,9)) + +######################################### +# # +# Part 2 # +# # +######################################### +if part == 2: + exit() + +print("--- %s seconds ---" % (time.time() - start_time)) \ No newline at end of file diff --git a/2024/10/solution_old.py b/2024/10/solution_old.py new file mode 100644 index 0000000..d584859 --- /dev/null +++ b/2024/10/solution_old.py @@ -0,0 +1,162 @@ +#!/bin/python3 +import sys,time,re +from pprint import pprint +sys.path.insert(0, '../../') +from fred import list2int,get_re,nprint,lprint,loadFile,toGrid, get_value_in_direction,addTuples +start_time = time.time() + +input_f = 'test2' + +part = 1 +######################################### +# # +# Part 1 # +# # +######################################### + +if part == 1: + + + def get_value_in_direction(grid, position, direction=None, length=1, type: str = None): + """ + Get the value(s) in a specified direction from a given position in a grid. + If no direction is provided, returns the value at the current position. + + Args: + grid (list of list of int/float/str): The 2D grid. + position (set): A set containing x (row index) and y (column index) as integers. + direction (str, optional): The direction to check. Defaults to None. + length (int, optional): The number of steps to check in the given direction. Default is 1. + type (str, optional): The type of result to return ('list' or 'str'). Defaults to None. + + Returns: + list or str: A list or string of values in the specified direction, or a single value. + + Raises: + ValueError: If direction is invalid or position is not a set of two integers. + TypeError: If grid is not a list of lists. + """ + if not all(isinstance(row, list) for row in grid): + raise TypeError("Grid must be a list of lists.") + + # Ensure position is a set of two integers + if len(position) != 2 or not all(isinstance(coord, int) for coord in position): + raise ValueError("Position must be a set containing two integers (x, y).") + + x, y = position + offsets = { + 'up': (-1, 0), + 'down': (1, 0), + 'left': (0, -1), + 'right': (0, 1), + 'up-left': (-1, -1), + 'up-right': (-1, 1), + 'down-left': (1, -1), + 'down-right': (1, 1) + } + + # If no direction is given, return the value at the current position + if direction is None: + if 0 <= x < len(grid) and 0 <= y < len(grid[x]): + return grid[x][y] + else: + return None + + # Validate direction + if direction not in offsets: + raise ValueError(f"Invalid direction: {direction}. Choose from {list(offsets.keys())}") + + dx, dy = offsets[direction] + new_x, new_y = x + dx, y + dy + + values = [] + + if length == 1: + # Check for out-of-bounds + if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[new_x]): + return grid[new_x][new_y] + else: + return None + else: + for step in range(length): + new_x, new_y = x + step * dx, y + step * dy + # Check for out-of-bounds + if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[new_x]): + values.append(grid[new_x][new_y]) + else: + return [] # Return empty list if any position is out of bounds + if type == 'list': + return values + elif type == 'str': + return ''.join(values) + else: + return values + + grid = toGrid(input_f,True) + + def extract_tuples(nested_list): + result = [] + for item in nested_list: + if isinstance(item, tuple): # Check if the item is a tuple + result.append(item) + elif isinstance(item, list): # If it's a list, recurse into it + result.extend(extract_tuples(item)) + return result + + def find_path(grid,pos:set,cur): + path = 0 + if cur == 9: + return 1 + + cur+=1 + + + if get_value_in_direction(grid,pos,'up') == cur: + path += find_path(grid,addTuples(pos,(-1,0)),cur) + + if get_value_in_direction(grid,pos,'down') == cur: + path += find_path(grid,addTuples(pos,(1,0)),cur) + + if get_value_in_direction(grid,pos,'left') == cur: + path += find_path(grid,addTuples(pos,(0,-1)),cur) + + if get_value_in_direction(grid,pos,'right') == cur: + path += find_path(grid,addTuples(pos,(0,1)),cur) + + + return path + + result = 0 + + for r, row in enumerate(grid): + for c, col in enumerate(row): + #get_value_in_direction(grid, position, direction=None, length=1, type: str = None): + if grid[r][c] == 0: + if get_value_in_direction(grid,(r,c),'up') == 1: + #print(find_path(grid,(r-1,c),1)) + result += find_path(grid,(r-1,c),1) + + if get_value_in_direction(grid,(r,c),'down') == 1: + result += find_path(grid,(r+1,c),1) + #print(find_path(grid,(r+1,c),1,0)) + + if get_value_in_direction(grid,(r,c),'left') == 1: + result += find_path(grid,(r,c-1),1) + #print(find_path(grid,(r,c-1),1,0)) + + if get_value_in_direction(grid,(r,c),'right') == 1: + result += find_path(grid,(r,c+1),1) + #print(find_path(grid,(r,c+1),1,0)) + + print(result) + + +######################################### +# # +# Part 2 # +# # +######################################### +if part == 2: + exit() + +print("--- %s seconds ---" % (time.time() - start_time)) diff --git a/__pycache__/fred.cpython-311.pyc b/__pycache__/fred.cpython-311.pyc index 97aa3fc..3e4a8d8 100644 Binary files a/__pycache__/fred.cpython-311.pyc and b/__pycache__/fred.cpython-311.pyc differ diff --git a/fred.py b/fred.py index ff9ab81..f291ac6 100644 --- a/fred.py +++ b/fred.py @@ -66,14 +66,12 @@ def toGrid(input, parser=None): FileNotFoundError: If the file cannot be found (if input is a file path). ValueError: If the parser function is invalid. """ - if parser is not None and not callable(parser): - raise ValueError("The parser must be a callable function.") - + grid = [] try: with open(input) as file: for line in file: - grid.append(list2int(line.rstrip()) if parser else list(line.rstrip())) # Use parser or default processing + grid.append(list2int(list(line.rstrip())) if parser else list(line.rstrip())) # Use parser or default processing except FileNotFoundError: raise FileNotFoundError(f"The file '{input}' was not found.") except IOError as e: