summaryrefslogtreecommitdiff
path: root/2024/aoc2024-d15.py
diff options
context:
space:
mode:
Diffstat (limited to '2024/aoc2024-d15.py')
-rw-r--r--2024/aoc2024-d15.py157
1 files changed, 157 insertions, 0 deletions
diff --git a/2024/aoc2024-d15.py b/2024/aoc2024-d15.py
new file mode 100644
index 0000000..a6bb5a0
--- /dev/null
+++ b/2024/aoc2024-d15.py
@@ -0,0 +1,157 @@
+#advent of code 2024
+#day 15
+#could probably reduce the length of code even more
+#if I were to index the part2 box
+#instead of hardcoding the left and right part of the box
+#but w/e, I don't feel like doing it
+#the code is ~110 lines of code shorter after cleaning up
+#explanation:
+#given: picture of the grid (fg) and robots movement list (movement)
+#create 2 grids: grid1 and grid2 for part 1 and part 2 respectively
+#for part 2 the grid is made wider x2: the grid2 is created with
+#resizing dictionary to know what to insert where
+#there are 3 functions: GPS, movebox, stackbox
+#GPS calculates the result required by puzzle after simulating
+#the movement of the robot
+#the box variable is a list of characters that are considered a box
+#for each part of a puzzle: O for part 1, [ and ] for part 2
+#the thing is that by making it into a list, I don't need
+#separate functions for each part: the logic checks if
+#the current tile is equal to the last or the first element of this list
+#to determine whether or not it's a box
+#for list of length 1, element with index 0 and -1 are the same
+#next it iterates over each step of the robot's movement in movement list
+#before the robot moves, it checks what is in it's way
+#if it's a floor ("."), then it takes a step
+#if it's a wall ("#"), it stands in the same spot
+#if it's a box, it runs the movebox or stackbox function
+#to determine if it's viable or not
+#stackbox is a function that is run only for part2 if the robot
+#wants to push boxes up or down. if it's part 1 or it moves left or right
+#it uses movebox function
+#movebox function scans the grid in the provided direction
+#(accordingly with robots desired movement) as long as it meets
+#a wall tile or floor tile. if it's a wall: can't move
+#if it's a floor, then for each tile in the path, from last tile to the first
+#move each tile in given direction
+#stackbox function queues each tile with a box
+#and checks if something is above or below (depending on the robots direction)
+#if there's another box, it queues up that tile and it's counterpart
+#(left or right) to check it again recursively
+#if at any moment there's a wall in the path, the robot can't move
+#if the final tiles don't have only floor tiles above or below them,
+#then each box tile is moved one step in correct direction,
+#leaving behind a free space
+#this comment is as long as solutions for days 1-13
+#but I just fucking know that I'll look this up one day and will
+#not remember anything
+grid1 = {};
+grid2 = {};
+dirs = {};
+dirs["^"] = (0,-1);
+dirs["v"] = (0,1);
+dirs["<"] = (-1,0);
+dirs[">"] = (1,0);
+resizing = {};
+resizing["#"] = "##";
+resizing["O"] = "[]";
+resizing["."] = "..";
+resizing["@"] = "@.";
+
+f = open("15.in","r");
+fg, movement = f.read().split("\n\n");
+movement = movement.replace("\n","");
+for y,l in enumerate(fg.split("\n")):
+ for x,c in enumerate(l.strip()):
+ if c == "@":
+ robot1 = (x,y);
+ robot2 = (2*x,y);
+ c = ".";
+ grid1[(x,y)] = c;
+ for offset in range(2):
+ grid2[(2*x+offset,y)] = resizing[c][offset];
+f.close();
+
+def movebox(BoxPos,direction,grid,box,p2):
+ valid = True;
+ bx, by = BoxPos; #coordinates of 1st box to push
+ dirx,diry = direction;
+ distance = 1; #python ranges are exclusive on outside
+ NextTile = grid.get((bx+distance*dirx,by+distance*diry),"#");
+ while NextTile == box[0] or NextTile == box[-1]:
+ distance += 1;
+ NextTile = grid.get((bx+distance*dirx,by+distance*diry),"#");
+ LastTile = grid.get((bx+distance*dirx,by+distance*diry),"#");
+ if LastTile == "#": #if wall, can't move
+ valid = False;
+ else: #replace each box in the way step by step in reversed order
+ for dist in range(distance):
+ NewPosition = (bx+distance*dirx -dist*dirx,by+distance*diry -dist*diry);
+ PrevPosition = (bx+distance*dirx -dist*dirx -dirx,by + distance*diry - dist*diry - diry);
+ grid[NewPosition] = grid[PrevPosition];
+ grid[PrevPosition] = ".";
+ return valid;
+
+def stackbox(BoxPos,direction,grid,box,p2):
+ valid = True;
+ bx,by = BoxPos;
+ dirx,diry = direction;
+ if grid[BoxPos] == box[0]: BoxPos2 = (bx+1,by,box[-1]);
+ else: BoxPos2 = (bx-1,by,box[0]);
+ queue = [(bx,by,grid[BoxPos]),BoxPos2]; #each part of the box is a separate entry in queue
+ BoxesToMove = queue.copy();
+ while queue:
+ PosToCheck = [];
+ for queuedpos in queue:
+ BX,BY,dummy = queuedpos; #dummy not needed for queue loop, needed later
+ NextTile = grid[(BX,BY+diry)];
+ if NextTile == box[0]:
+ PosToCheck.append((BX,BY+diry,NextTile));
+ PosToCheck.append((BX+1,BY+diry,grid[(BX+1,BY+diry)]));
+ elif NextTile == box[-1]:
+ PosToCheck.append((BX,BY+diry,NextTile));
+ PosToCheck.append((BX-1,BY+diry,grid[(BX-1,BY+diry)]));
+ elif NextTile == "#":
+ valid = False;
+ break;
+ if valid:
+ queue = PosToCheck.copy();
+ BoxesToMove.extend(PosToCheck);
+ else:
+ BoxesToMove = [];
+ queue = [];
+ BoxesToMove = sorted(BoxesToMove, key = lambda k: k[1],reverse=diry>0);
+ for BoxToMove in BoxesToMove:
+ boxx,boxy,prevtile = BoxToMove;
+ grid[(boxx,boxy+diry)] = prevtile;
+ grid[(boxx,boxy)] = ".";
+ return valid;
+
+def GPS(grid,robot,p2):
+ box = ["O"];
+ if p2: box = ["[","]"];
+ for d in movement:
+ dx,dy = dirs[d];
+ rx,ry = robot;
+ NextPos = (rx+dx,ry+dy);
+ tile = grid.get(NextPos,"#");
+ if tile == "#": #if wall, dont move
+ ok = False;
+ elif tile == box[0] or tile == box[-1]: #if boxes in the way, move boxes first
+ if p2 and dy != 0:
+ ok = stackbox(NextPos,dirs[d],grid,box,p2);
+ else:
+ ok = movebox(NextPos,dirs[d],grid,box,p2);
+ else: #if nothing in the way, then move
+ ok = True;
+ robot = (rx+ok*dx,ry+ok*dy);
+ score = 0;
+ for g in grid:
+ if grid[g] == box[0]: score += 100*g[1] + g[0];
+ return score;
+
+part1 = GPS(grid1,robot1,False);
+part2 = GPS(grid2,robot2,True);
+print("part 1 =", part1);
+print("part 2 =", part2);
+