From 2674f6e723c1cd5804401946fd5c93587a370f5c Mon Sep 17 00:00:00 2001 From: FrederikBaerentsen Date: Wed, 18 Dec 2024 07:09:36 +0100 Subject: [PATCH] Solved 2024/18 P1 --- 2024/16/16.md | 170 +++++++++++++++++++++++++++++++ 2024/16/solution.py | 87 ++++++++++++++++ 2024/18/18.md | 144 ++++++++++++++++++++++++++ 2024/18/solution.py | 76 ++++++++++++++ __pycache__/fred.cpython-311.pyc | Bin 30163 -> 35536 bytes fred.py | 125 +++++++++++++++++++++-- 6 files changed, 596 insertions(+), 6 deletions(-) create mode 100644 2024/16/16.md create mode 100644 2024/16/solution.py create mode 100644 2024/18/18.md create mode 100644 2024/18/solution.py diff --git a/2024/16/16.md b/2024/16/16.md new file mode 100644 index 0000000..353327a --- /dev/null +++ b/2024/16/16.md @@ -0,0 +1,170 @@ +## \-\-- Day 16: Reindeer Maze \-\-- + +It\'s time again for the [Reindeer Olympics](/2015/day/14)! This year, +the big event is the *Reindeer Maze*, where the Reindeer compete for the +*[lowest +score]{title="I would say it's like Reindeer Golf, but knowing Reindeer, it's almost certainly nothing like Reindeer Golf."}*. + +You and The Historians arrive to search for the Chief right as the event +is about to start. It wouldn\'t hurt to watch a little, right? + +The Reindeer start on the Start Tile (marked `S`) facing *East* and need +to reach the End Tile (marked `E`). They can move forward one tile at a +time (increasing their score by `1` point), but never into a wall (`#`). +They can also rotate clockwise or counterclockwise 90 degrees at a time +(increasing their score by `1000` points). + +To figure out the best place to sit, you start by grabbing a map (your +puzzle input) from a nearby kiosk. For example: + + ############### + #.......#....E# + #.#.###.#.###.# + #.....#.#...#.# + #.###.#####.#.# + #.#.#.......#.# + #.#.#####.###.# + #...........#.# + ###.#.#####.#.# + #...#.....#.#.# + #.#.#.###.#.#.# + #.....#...#.#.# + #.###.#.#.#.#.# + #S..#.....#...# + ############### + +There are many paths through this maze, but taking any of the best paths +would incur a score of only `7036`. This can be achieved by taking a +total of `36` steps forward and turning 90 degrees a total of `7` times: + + ############### + #.......#....E# + #.#.###.#.###^# + #.....#.#...#^# + #.###.#####.#^# + #.#.#.......#^# + #.#.#####.###^# + #..>>>>>>>>v#^# + ###^#.#####v#^# + #>>^#.....#v#^# + #^#.#.###.#v#^# + #^....#...#v#^# + #^###.#.#.#v#^# + #S..#.....#>>^# + ############### + +Here\'s a second example: + + ################# + #...#...#...#..E# + #.#.#.#.#.#.#.#.# + #.#.#.#...#...#.# + #.#.#.#.###.#.#.# + #...#.#.#.....#.# + #.#.#.#.#.#####.# + #.#...#.#.#.....# + #.#.#####.#.###.# + #.#.#.......#...# + #.#.###.#####.### + #.#.#...#.....#.# + #.#.#.#####.###.# + #.#.#.........#.# + #.#.#.#########.# + #S#.............# + ################# + +In this maze, the best paths cost `11048` points; following one such +path would look like this: + + ################# + #...#...#...#..E# + #.#.#.#.#.#.#.#^# + #.#.#.#...#...#^# + #.#.#.#.###.#.#^# + #>>v#.#.#.....#^# + #^#v#.#.#.#####^# + #^#v..#.#.#>>>>^# + #^#v#####.#^###.# + #^#v#..>>>>^#...# + #^#v###^#####.### + #^#v#>>^#.....#.# + #^#v#^#####.###.# + #^#v#^........#.# + #^#v#^#########.# + #S#>>^..........# + ################# + +Note that the path shown above includes one 90 degree turn as the very +first move, rotating the Reindeer from facing East to facing North. + +Analyze your map carefully. *What is the lowest score a Reindeer could +possibly get?* + +Your puzzle answer was `108504`. + +The first half of this puzzle is complete! It provides one gold star: \* + +## \-\-- Part Two \-\-- {#part2} + +Now that you know what the best paths look like, you can figure out the +best spot to sit. + +Every non-wall tile (`S`, `.`, or `E`) is equipped with places to sit +along the edges of the tile. While determining which of these tiles +would be the best spot to sit depends on a whole bunch of factors (how +comfortable the seats are, how far away the bathrooms are, whether +there\'s a pillar blocking your view, etc.), the most important factor +is *whether the tile is on one of the best paths through the maze*. If +you sit somewhere else, you\'d miss all the action! + +So, you\'ll need to determine which tiles are part of *any* best path +through the maze, including the `S` and `E` tiles. + +In the first example, there are `45` tiles (marked `O`) that are part of +at least one of the various best paths through the maze: + + ############### + #.......#....O# + #.#.###.#.###O# + #.....#.#...#O# + #.###.#####.#O# + #.#.#.......#O# + #.#.#####.###O# + #..OOOOOOOOO#O# + ###O#O#####O#O# + #OOO#O....#O#O# + #O#O#O###.#O#O# + #OOOOO#...#O#O# + #O###.#.#.#O#O# + #O..#.....#OOO# + ############### + +In the second example, there are `64` tiles that are part of at least +one of the best paths: + + ################# + #...#...#...#..O# + #.#.#.#.#.#.#.#O# + #.#.#.#...#...#O# + #.#.#.#.###.#.#O# + #OOO#.#.#.....#O# + #O#O#.#.#.#####O# + #O#O..#.#.#OOOOO# + #O#O#####.#O###O# + #O#O#..OOOOO#OOO# + #O#O###O#####O### + #O#O#OOO#..OOO#.# + #O#O#O#####O###.# + #O#O#OOOOOOO..#.# + #O#O#O#########.# + #O#OOO..........# + ################# + +Analyze your map further. *How many tiles are part of at least one of +the best paths through the maze?* + +Answer: + +Although it hasn\'t changed, you can still [get your puzzle +input](16/input). + diff --git a/2024/16/solution.py b/2024/16/solution.py new file mode 100644 index 0000000..6e766c5 --- /dev/null +++ b/2024/16/solution.py @@ -0,0 +1,87 @@ +#!/bin/python3 +import sys,time,re +from pprint import pprint +sys.path.insert(0, '../../') +from fred import list2int,get_re,nprint,lprint,loadFile,dijkstra,toGrid,dfs,bfs,findInGrid,get_value_in_direction,addTuples,subTuples,create_graph_from_grid,manhattan_distance +start_time = time.time() + +input_f = 'input' + +######################################### +# # +# Part 1 # +# # +######################################### + +def part1(): + grid = toGrid(input_f) + + start = findInGrid(grid,'S') + end = findInGrid(grid,'E') + + print(start,end) + + def a_star(graph, start, end, heuristic): + + import heapq + + # Priority queue stores (f_score, current_node, current_direction) + priority_queue = [(0, start, None)] + g_scores = {node: float('inf') for node in graph} # Cost from start to each node + g_scores[start] = 0 + + f_scores = {node: float('inf') for node in graph} + f_scores[start] = heuristic(start, end) + + previous_nodes = {node: None for node in graph} + + while priority_queue: + current_f_score, current_node, previous_direction = heapq.heappop(priority_queue) + + if current_node == end: + break + + for neighbor, weight in graph[current_node]: + current_direction = ( + neighbor[0] - current_node[0], # Row direction (-1, 0, 1) + neighbor[1] - current_node[1] # Col direction (-1, 0, 1) + ) + + turn_cost = 1000 if previous_direction and current_direction != previous_direction else 0 + tentative_g_score = g_scores[current_node] + weight + turn_cost + + if tentative_g_score < g_scores[neighbor]: + g_scores[neighbor] = tentative_g_score + f_scores[neighbor] = tentative_g_score + heuristic(neighbor, end) + previous_nodes[neighbor] = current_node + heapq.heappush(priority_queue, (f_scores[neighbor], neighbor, current_direction)) + + path = [] + while end is not None: + path.append(end) + end = previous_nodes[end] + path.reverse() + + return path, g_scores[path[-1]] + + + graph = create_graph_from_grid(grid,start,end,'#') + + path, score = a_star(graph,start,end,manhattan_distance) + #print(path) + return score #Instead of implementing edge cases, just try with turning (adding 1000) to the result, if that's wrong, just don't add it. I got it right by not turning right away. + +start_time = time.time() +print('Part 1:',part1(), '\t\t', round((time.time() - start_time)*1000), 'ms') + + +######################################### +# # +# Part 2 # +# # +######################################### +def part2(): + return + +start_time = time.time() +print('Part 2:',part2(), '\t\t', round((time.time() - start_time)*1000), 'ms') \ No newline at end of file diff --git a/2024/18/18.md b/2024/18/18.md new file mode 100644 index 0000000..ec66fde --- /dev/null +++ b/2024/18/18.md @@ -0,0 +1,144 @@ +## \-\-- Day 18: RAM Run \-\-- + +You and The Historians look a lot more pixelated than you remember. +You\'re [inside a computer](/2017/day/2) at the North Pole! + +Just as you\'re about to check out your surroundings, a program runs up +to you. \"This region of memory isn\'t safe! The User misunderstood what +a [pushdown +automaton](https://en.wikipedia.org/wiki/Pushdown_automaton) +is and their algorithm is pushing whole *bytes* down on top of us! +Run!\" + +The algorithm is fast - it\'s going to cause a byte to fall into your +memory space once every +[nanosecond](https://www.youtube.com/watch?v=9eyFDBPk4Yw)! +Fortunately, you\'re *faster*, and by quickly scanning the algorithm, +you create a *list of which bytes will fall* (your puzzle input) in the +order they\'ll land in your memory space. + +Your memory space is a two-dimensional grid with coordinates that range +from `0` to `70` both horizontally and vertically. However, for the sake +of example, suppose you\'re on a smaller grid with coordinates that +range from `0` to `6` and the following list of incoming byte positions: + + 5,4 + 4,2 + 4,5 + 3,0 + 2,1 + 6,3 + 2,4 + 1,5 + 0,6 + 3,3 + 2,6 + 5,1 + 1,2 + 5,5 + 2,5 + 6,5 + 1,4 + 0,4 + 6,4 + 1,1 + 6,1 + 1,0 + 0,5 + 1,6 + 2,0 + +Each byte position is given as an `X,Y` coordinate, where `X` is the +distance from the left edge of your memory space and `Y` is the distance +from the top edge of your memory space. + +You and The Historians are currently in the top left corner of the +memory space (at `0,0`) and need to reach the exit in the bottom right +corner (at `70,70` in your memory space, but at `6,6` in this example). +You\'ll need to simulate the falling bytes to plan out where it will be +safe to run; for now, simulate just the first few bytes falling into +your memory space. + +As bytes fall into your memory space, they make that coordinate +*corrupted*. Corrupted memory coordinates cannot be entered by you or +The Historians, so you\'ll need to plan your route carefully. You also +cannot leave the boundaries of the memory space; your only hope is to +reach the exit. + +In the above example, if you were to draw the memory space after the +first `12` bytes have fallen (using `.` for safe and `#` for corrupted), +it would look like this: + + ...#... + ..#..#. + ....#.. + ...#..# + ..#..#. + .#..#.. + #.#.... + +You can take steps up, down, left, or right. After just 12 bytes have +corrupted locations in your memory space, the shortest path from the top +left corner to the exit would take `22` steps. Here (marked with `O`) is +one such path: + + OO.#OOO + .O#OO#O + .OOO#OO + ...#OO# + ..#OO#. + .#.O#.. + #.#OOOO + +Simulate the first kilobyte (`1024` bytes) falling onto your memory +space. Afterward, *what is the minimum number of steps needed to reach +the exit?* + +Your puzzle answer was `294`. + +The first half of this puzzle is complete! It provides one gold star: \* + +## \-\-- Part Two \-\-- {#part2} + +The Historians aren\'t as used to moving around in this pixelated +universe as you are. You\'re afraid they\'re not going to be fast enough +to make it to the exit before the path is completely blocked. + +To determine how fast everyone needs to go, you need to determine *the +first byte that will cut off the path to the exit*. + +In the above example, after the byte at `1,1` falls, there is still a +path to the exit: + + O..#OOO + O##OO#O + O#OO#OO + OOO#OO# + ###OO## + .##O### + #.#OOOO + +However, after adding the very next byte (at `6,1`), there is no longer +a path to the exit: + + ...#... + .##..## + .#..#.. + ...#..# + ###..## + .##.### + #.#.... + +So, in this example, the coordinates of the first byte that prevents the +exit from being reachable are `6,1`. + +Simulate more of the bytes that are about to corrupt your memory space. +*What are the coordinates of the first byte that will prevent the exit +from being reachable from your starting position?* (Provide the answer +as two integers separated by a comma with no other characters.) + +Answer: + +Although it hasn\'t changed, you can still [get your puzzle +input](18/input). + diff --git a/2024/18/solution.py b/2024/18/solution.py new file mode 100644 index 0000000..75bfcea --- /dev/null +++ b/2024/18/solution.py @@ -0,0 +1,76 @@ +#!/bin/python3 +import sys,time,re +from pprint import pprint +sys.path.insert(0, '../../') +from fred import list2int,get_re,nprint,lprint,loadFile,bfs,get_value_in_direction,addTuples,grid_valid +start_time = time.time() + +input_f = 'input' + +######################################### +# # +# Part 1 # +# # +######################################### +def part1(): + instructions = [] + grid = [] + w = 70 + h = 70 + end = (h,w) + start = (0,0) + with open(input_f) as file: + for line in file: + l = list2int(line.rstrip().split(',')) + instructions.append((l[0],l[1])) + #print(instructions) + + grid = [[ '.' for x in range(0,w+1)] for y in range(0,h+1)] + + for i in range(1024): + x = instructions[i] + grid[x[0]][x[1]] = '#' + + + + def is_goal(node): + #print(node) + + return True if node == end else False + + def get_neighbors(node): + directions = ['up','down','left','right'] + offsets = { + 'up': (-1, 0), + 'down': (1, 0), + 'left': (0, -1), + 'right': (0, 1), + } + neighbors = [] + + # Loop through all the directions + for d in directions: + tmp = addTuples(offsets[d],node) + if get_value_in_direction(grid,node,d) != '#' and grid_valid(tmp[0],tmp[1],grid): + neighbors.append((tmp[0],tmp[1])) + # Return the list of valid neighbors + return neighbors + + goal_nodes, path = bfs((0,0),is_goal,get_neighbors) + print(goal_nodes) + return len(path[goal_nodes[0]])-1 + +start_time = time.time() +print('Part 1:',part1(), '\t\t', round((time.time() - start_time)*1000), 'ms') + + +######################################### +# # +# Part 2 # +# # +######################################### +def part2(): + return + +start_time = time.time() +print('Part 2:',part2(), '\t\t', round((time.time() - start_time)*1000), 'ms') \ No newline at end of file diff --git a/__pycache__/fred.cpython-311.pyc b/__pycache__/fred.cpython-311.pyc index 88f68b7e10f5a7bef54001e1b0daedb579303cb9..0f304cb1787d1ae4f20928d495484f1da6947308 100644 GIT binary patch delta 8280 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 delta 4291 zcmb7H32c3yuy?O7=doz3OnDqOT($JA+~7C<|#rq(^A z(?M-Y3bn)QP@EwvP1$4abWx|`rY^-DGE+CSQ^&MZp`Aw4fLEg9fF4KFm2{vpfX)EE z@pQbB3G*_cokg=iQ#Q>8%%M|hj?(X;9$?88s`B<&I&*104Cc`SzcTC3(yG8<{xy*_3&nOz6;E!C16UVOm!ttZK{)T}4UR>9X`&fn+uWJAwni zXV>k)KqM0A25|-0i-BTJgj@s{f*TDG9FsS4B08(EmLbY#dt7!RPL zBCmLuSdmqWEoNaGW#uqXmpnS|J7%W`xrK^6z9K89q5|8K5PSft3;+_0`DS%XAkL;h z*BX8(YoGm7piDa1n#|A6mu8|Q-J&JpU_u_y!xF2851R_0N&pYVI0BTeMmCkzLwAJ7 zJl(|4Pi@cS)!8SK1Gz0^!E~I7lUNPHa|m?+j|);0-6Mi#!Ppm&X$FEHp(L4_|6M|I z_~n9NrH=?fKB8NKu|!n!$-3m9$|eX#fDuCnSSEkI&`;jxI|^saMKL}z8{;DA&}CK8 zKwJsV!mYKzaE>Dsj?x8*I9N3gdWZP^!WG1yoLBT~DYF6RkHecuR33 z*uKAbmP`20Ov6EqMdmYfCRmC6CO5fQg05EVgB=2)2~hR+Nz6@s5gGn0&aR zJ_F;+st|CC`Yf!B->j&p>VSr70e~&@FxpOKYdjF8U1EcF!`Ls7=?cGJRmV3>I$=d8 z8>z-0%+BGxrOT3izK=-3TIgplAhhFzF)o1hB&v$n-p8>{1QG6aNWFxBtPr;KNQ;H$ zBZaGlu$93_gl%L`J2om>vl7)5#uOO3%l}#x10!FYTrItBP9B=R(!B$7C}e9H+MPj?*CR zoREn9#B}gwe#c)}okE=($$8gl$iZ`%hvE@)tWTNq20v9@`+v^)6+cqFFvtD4{)Za) zw5j6>)WE4zWY8yCC1YFiLM@FqE%8{1X^0%$#D}LYe4%ouV~69Bd(ySEhsy>-Q=ew) zSPq!^G{dS4HFjbW#W^kc0^3}SkT92j~&mp=~#}i z_*U~1vll?kdw=#h>Dl>vx?y7d3MigW<}W6JPac!HB5N(92MH9Ke>dc1i{mT>OG;N? zAOa`fZkX7_s~ZO)ca4L@lDyvNl9mc%7>pXmNKDZ!y#X!Q11Eqj9?)PrM2#bJFEEXk zXK3hFs103AVS~_E%gg7tfpB!z!TE(!$+PMc@NdtH$N98k581{)UVvpYe_?6IW*A@* zfY!(AlZxHAAHn(J$n3+8EeKl?wgEg@R&DXPvHuRhV1yrEc+x4#pg1nek`0S0?avND z+X{cy(WivqC2=}5HMC4d3*lH19u)B(IUnEzjWPN}MFw&F?t*dr@QQLU_6I9kTE$8* zpm46>haDS3;~>b!E&&hek$y232ccikHN15t>ldWBtcZxBJAIBPc8hU+LMGk3S=pvC z4RtsXmLq?vk>&@n;}F7Y2!|1b@A0~#+hU;*9P_F!(XH@q(=AbDOBZi!t0b9xQ(J-M z>%g@ud7$m*672VttKXN);c6j=qanOugy3eVfz{$PHAiDqdE_j`GfB>nysAGZ%ZmZ!czNDj|5Dn68Oo1Dws`y-NKDI5Y z7GN6d<-0mw%@jFer{PVTp#}!nPk`Y{GNnd!v2v$fSV`Ha@1+pZs z>8>Of`1s{x?t-uc!RnWg>N*d z%M=#DXM3o5#j0*cZ$`%r>KzW7P z##JB%5;t|LMCbhyE+F+k set: + """Find the location of a character in a grid. + + + """ + + for r,row in enumerate(grid): + for c, char in enumerate(row): + if char == x: + return (r,c) + def loadFile(input_f): """ Loads a file and returns its lines as a list. @@ -179,7 +190,6 @@ def dprint(graph: dict): except Exception as e: raise RuntimeError(f"An error occurred while printing the dictionary: {e}") - def lprint(x: str, log: bool): """ Prints a string if logging is enabled. @@ -271,7 +281,10 @@ def nprint(grid, cur: set = None, sign: str = None, positions:list = None): for idx, i in enumerate(grid): for jdx, j in enumerate(i): if jdx == 0: - prepend = str(idx)+' ' + if idx < 10: + prepend = ' '+str(idx)+' ' + else: + prepend = ''+str(idx)+' ' else: prepend = '' @@ -286,7 +299,7 @@ def nprint(grid, cur: set = None, sign: str = None, positions:list = None): else: if positions is not None: if (idx,jdx) in positions: - print(prepend+colored(grid[idx][jdx],'red'),end=' ') + print(prepend+colored(grid[idx][jdx],'green'),end=' ') else: print(prepend+grid[idx][jdx], end=' ') else: @@ -296,8 +309,26 @@ def nprint(grid, cur: set = None, sign: str = None, positions:list = None): for r in range(len(grid[0])): if r == 0: - print(' ',end='') - print(r,end=' ') + print(' 0',end='') + + else: + if r%2 == 0: + print(r,end='') + else: + print(' ',end='') + print() + for r in range(len(grid[0])): + if r == 0: + print(' ',end='') + + else: + if r%2 != 0: + print(r,end='') + else: + if r != 9: + print(' ',end='') + else: + print('',end='') print() def list2int(x): @@ -647,6 +678,9 @@ def dfs(grid:list, pos:set) -> list: dfs(pos[0], pos[1]) return list(visited) +def manhattan_distance(node, goal): + return abs(node[0] - goal[0]) + abs(node[1] - goal[1]) + # Should probably be added to the regular dfs. def flood_fill(cells, pos): """ @@ -677,4 +711,83 @@ def flood_fill(cells, pos): dfs(neighbor) dfs(pos) - return list(visited) \ No newline at end of file + return list(visited) + +def create_graph_from_grid(grid, start, end,wall): + """ + Converts a grid into a graph representation for use in pathfinding algorithms. + + Parameters: + - grid: A 2D list representing the grid, where '#' is a wall, and '.' is a path. + - start: The coordinates of the start node (row, col). + - end: The coordinates of the end node (row, col). + - wall: The string of the wall node. + + Returns: + - graph: A dictionary where each key is a node (row, col), and the value is a list of tuples. + Each tuple represents (neighbor, weight). + """ + rows, cols = len(grid), len(grid[0]) + graph = {} + + # Helper to get neighbors + def neighbors(r, c): + for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]: # Up, Down, Left, Right + nr, nc = r + dr, c + dc + if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] != '#': + yield (nr, nc) + + for r in range(rows): + for c in range(cols): + if grid[r][c] != '#': # Only process valid cells + graph[(r, c)] = [(neighbor, 1) for neighbor in neighbors(r, c)] # Weight is 1 + + return graph + +def a_star(graph, start, end, heuristic): + """ + A* Algorithm to find the shortest path between two nodes in a graph. + + Parameters: + - graph: A dictionary where each key is a node, and the value is a list of tuples. + Each tuple represents (neighbor, distance). + - start: The starting node (row, col). + - end: The destination node (row, col). + - heuristic: A function that estimates the cost from a node to the end. + + Returns: + - path: A list of nodes that represent the shortest path from start to end. + - total_cost: The total cost of the shortest path. + """ + priority_queue = [(0, start)] # (f_score, current_node) + g_scores = {node: float('inf') for node in graph} # Cost from start to each node + g_scores[start] = 0 + + f_scores = {node: float('inf') for node in graph} # Estimated total cost (g + h) + f_scores[start] = heuristic(start, end) + + previous_nodes = {node: None for node in graph} + + while priority_queue: + current_f_score, current_node = heapq.heappop(priority_queue) + + if current_node == end: + break + + for neighbor, weight in graph[current_node]: + tentative_g_score = g_scores[current_node] + weight + + if tentative_g_score < g_scores[neighbor]: + g_scores[neighbor] = tentative_g_score + f_scores[neighbor] = tentative_g_score + heuristic(neighbor, end) + previous_nodes[neighbor] = current_node + heapq.heappush(priority_queue, (f_scores[neighbor], neighbor)) + + # Reconstruct the path + path = [] + while end is not None: + path.append(end) + end = previous_nodes[end] + path.reverse() + + return path, g_scores[path[-1]] \ No newline at end of file