Solved 2024/15 P1

This commit is contained in:
FrederikBaerentsen 2024-12-15 10:18:34 +01:00
parent e5d1db9508
commit 960f4f4a06
4 changed files with 855 additions and 6 deletions

478
2024/15/15.md Normal file
View File

@ -0,0 +1,478 @@
## \-\-- Day 15: Warehouse Woes \-\--
You appear back inside your own mini submarine! Each Historian drives
their mini submarine in a different direction; maybe the Chief has his
own submarine down here somewhere as well?
You look up to see a vast school of [lanternfish](/2021/day/6) swimming
past you. On closer inspection, they seem quite anxious, so you drive
your mini submarine over to see if you can help.
Because lanternfish populations grow rapidly, they need a lot of food,
and that food needs to be stored somewhere. That\'s why these
lanternfish have built elaborate warehouse complexes operated by robots!
These lanternfish seem so anxious because they have lost control of the
robot that operates one of their most important warehouses! It is
currently running
amok,
pushing around boxes in the warehouse with no regard for lanternfish
logistics *or* lanternfish inventory management strategies.
Right now, none of the lanternfish are brave enough to swim up to an
unpredictable robot so they could shut it off. However, if you could
anticipate the robot\'s movements, maybe they could find a safe option.
The lanternfish already have a map of the warehouse and a list of
movements the robot will *attempt* to make (your puzzle input). The
problem is that the movements will sometimes fail as boxes are shifted
around, making the actual movements of the robot difficult to predict.
For example:
##########
#..O..O.O#
#......O.#
#.OO..O.O#
#..O@..O.#
#O#..O...#
#O..O..O.#
#.OO.O.OO#
#....O...#
##########
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
As the robot (`@`) attempts to move, if there are any boxes (`O`) in the
way, the robot will also attempt to push those boxes. However, if this
action would cause the robot or a box to move into a wall (`#`), nothing
moves instead, including the robot. The initial positions of these are
shown on the map at the top of the document the lanternfish gave you.
The rest of the document describes the *moves* (`^` for up, `v` for
down, `<` for left, `>` for right) that the robot will attempt to make,
in order. (The moves form a single giant sequence; they are broken into
multiple lines just to make copy-pasting easier. Newlines within the
move sequence should be ignored.)
Here is a smaller example to get started:
########
#..O.O.#
##@.O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
<^^>>>vv<v>>v<<
Were the robot to attempt the given sequence of moves, it would push
around the boxes as follows:
Initial state:
########
#..O.O.#
##@.O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
Move <:
########
#..O.O.#
##@.O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
Move ^:
########
#.@O.O.#
##..O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
Move ^:
########
#.@O.O.#
##..O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
Move >:
########
#..@OO.#
##..O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
Move >:
########
#...@OO#
##..O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
Move >:
########
#...@OO#
##..O..#
#...O..#
#.#.O..#
#...O..#
#......#
########
Move v:
########
#....OO#
##..@..#
#...O..#
#.#.O..#
#...O..#
#...O..#
########
Move v:
########
#....OO#
##..@..#
#...O..#
#.#.O..#
#...O..#
#...O..#
########
Move <:
########
#....OO#
##.@...#
#...O..#
#.#.O..#
#...O..#
#...O..#
########
Move v:
########
#....OO#
##.....#
#..@O..#
#.#.O..#
#...O..#
#...O..#
########
Move >:
########
#....OO#
##.....#
#...@O.#
#.#.O..#
#...O..#
#...O..#
########
Move >:
########
#....OO#
##.....#
#....@O#
#.#.O..#
#...O..#
#...O..#
########
Move v:
########
#....OO#
##.....#
#.....O#
#.#.O@.#
#...O..#
#...O..#
########
Move <:
########
#....OO#
##.....#
#.....O#
#.#O@..#
#...O..#
#...O..#
########
Move <:
########
#....OO#
##.....#
#.....O#
#.#O@..#
#...O..#
#...O..#
########
The larger example has many more moves; after the robot has finished
those moves, the warehouse would look like this:
##########
#.O.O.OOO#
#........#
#OO......#
#OO@.....#
#O#.....O#
#O.....OO#
#O.....OO#
#OO....OO#
##########
The lanternfish use their own custom Goods Positioning System (GPS for
short) to track the locations of the boxes. The *GPS coordinate* of a
box is equal to 100 times its distance from the top edge of the map plus
its distance from the left edge of the map. (This process does not stop
at wall tiles; measure all the way to the edges of the map.)
So, the box shown below has a distance of `1` from the top edge of the
map and `4` from the left edge of the map, resulting in a GPS coordinate
of `100 * 1 + 4 = 104`.
#######
#...O..
#......
The lanternfish would like to know the *sum of all boxes\' GPS
coordinates* after the robot finishes moving. In the larger example, the
sum of all boxes\' GPS coordinates is `10092`. In the smaller example,
the sum is `2028`.
Predict the motion of the robot and boxes in the warehouse. After the
robot is finished moving, *what is the sum of all boxes\' GPS
coordinates?*
Your puzzle answer was `1568399`.
The first half of this puzzle is complete! It provides one gold star: \*
## \-\-- Part Two \-\-- {#part2}
The lanternfish use your information to find a safe moment to swim in
and turn off the malfunctioning robot! Just as they start preparing a
festival in your honor, reports start coming in that a *second*
warehouse\'s robot is *also* malfunctioning.
This warehouse\'s layout is surprisingly similar to the one you just
helped. There is one key difference: everything except the robot is
*twice as wide*! The robot\'s list of movements doesn\'t change.
To get the wider warehouse\'s map, start with your original map and, for
each tile, make the following changes:
- If the tile is `#`, the new map contains `##` instead.
- If the tile is `O`, the new map contains `[]` instead.
- If the tile is `.`, the new map contains `..` instead.
- If the tile is `@`, the new map contains `@.` instead.
This will produce a new warehouse map which is twice as wide and with
wide boxes that are represented by `[]`. (The robot does not change
size.)
The larger example from before would now look like this:
####################
##....[]....[]..[]##
##............[]..##
##..[][]....[]..[]##
##....[]@.....[]..##
##[]##....[]......##
##[]....[]....[]..##
##..[][]..[]..[][]##
##........[]......##
####################
Because boxes are now twice as wide but the robot is still the same size
and speed, boxes can be aligned such that they directly push two other
boxes at once. For example, consider this situation:
#######
#...#.#
#.....#
#..OO@#
#..O..#
#.....#
#######
<vv<<^^<<^^
After appropriately resizing this map, the robot would push around these
boxes as follows:
Initial state:
##############
##......##..##
##..........##
##....[][]@.##
##....[]....##
##..........##
##############
Move <:
##############
##......##..##
##..........##
##...[][]@..##
##....[]....##
##..........##
##############
Move v:
##############
##......##..##
##..........##
##...[][]...##
##....[].@..##
##..........##
##############
Move v:
##############
##......##..##
##..........##
##...[][]...##
##....[]....##
##.......@..##
##############
Move <:
##############
##......##..##
##..........##
##...[][]...##
##....[]....##
##......@...##
##############
Move <:
##############
##......##..##
##..........##
##...[][]...##
##....[]....##
##.....@....##
##############
Move ^:
##############
##......##..##
##...[][]...##
##....[]....##
##.....@....##
##..........##
##############
Move ^:
##############
##......##..##
##...[][]...##
##....[]....##
##.....@....##
##..........##
##############
Move <:
##############
##......##..##
##...[][]...##
##....[]....##
##....@.....##
##..........##
##############
Move <:
##############
##......##..##
##...[][]...##
##....[]....##
##...@......##
##..........##
##############
Move ^:
##############
##......##..##
##...[][]...##
##...@[]....##
##..........##
##..........##
##############
Move ^:
##############
##...[].##..##
##...@.[]...##
##....[]....##
##..........##
##..........##
##############
This warehouse also uses GPS to locate the boxes. For these larger
boxes, distances are measured from the edge of the map to the closest
edge of the box in question. So, the box shown below has a distance of
`1` from the top edge of the map and `5` from the left edge of the map,
resulting in a GPS coordinate of `100 * 1 + 5 = 105`.
##########
##...[]...
##........
In the scaled-up version of the larger example from above, after the
robot has finished all of its moves, the warehouse would look like this:
####################
##[].......[].[][]##
##[]...........[].##
##[]........[][][]##
##[]......[]....[]##
##..##......[]....##
##..[]............##
##..@......[].[][]##
##......[][]..[]..##
####################
The sum of these boxes\' GPS coordinates is `9021`.
Predict the motion of the robot and boxes in this new, scaled-up
warehouse. *What is the sum of all boxes\' final GPS coordinates?*
Answer:
Although it hasn\'t changed, you can still [get your puzzle
input](15/input).

355
2024/15/solution.py Normal file
View File

@ -0,0 +1,355 @@
#!/bin/python3
import sys,time,re
from pprint import pprint
sys.path.insert(0, '../../')
from fred import list2int,get_re,lprint, grid_valid,get_value_in_direction,addTuples,subTuples
from termcolor import colored
start_time = time.time()
input_f = 'test3'
def nprint(grid, cur: set = None, sign: str = None, positions:list = None):
"""
Prints a grid, highlighting the current position if specified.
Args:
grid (list): A 2D grid to print.
cur (set, optional): A set containing the (row, col) indices of the current position.
Defaults to None.
sign (str, optional): The sign to highlight the current position with. Defaults to None.
positions (list, optional): A list of sets containing the (row, col) indices of positions to color.
Defaults to None.
Returns:
None
Raises:
TypeError: If cur is not a set or sign is not a string.
"""
if cur is not None and not isinstance(cur, tuple):
raise TypeError("Cur must be a tuple with (row, column) indices.")
if sign is not None and not isinstance(sign, str):
raise TypeError("Sign must be a string.")
if positions is not None and not isinstance(positions, list):
raise TypeError("Positions must be a list.")
prepend = 0
for idx, i in enumerate(grid):
for jdx, j in enumerate(i):
if jdx == 0:
prepend = str(idx)+' '
else:
prepend = ''
if (idx, jdx) == cur:
if sign is not None:
if len(sign) > 1:
print(prepend+sign[0] + grid[idx][jdx] + sign[1], end='') # Print with sign
else:
print(prepend+colored(sign,'green',attrs=["underline","bold"]), end=' ') # Print sign
else:
print(prepend+colored(grid[idx][jdx],'green',attrs=["underline","bold"]), end=' ')
else:
if positions is not None:
if (idx,jdx) in positions:
print(prepend+colored(grid[idx][jdx],'red'),end=' ')
else:
print(prepend+grid[idx][jdx], end=' ')
else:
if grid[idx][jdx] == '.':
print(prepend+colored(grid[idx][jdx],'blue',attrs=["concealed"]),end=' ')
elif grid[idx][jdx] == 'O':
print(prepend+colored(grid[idx][jdx],'red'),end=' ')
elif grid[idx][jdx] == '#':
print(prepend+colored(grid[idx][jdx],'white'),end=' ')
else:
print(prepend+grid[idx][jdx], end=' ') # Regular grid element
print()
for r in range(len(grid[0])):
if r == 0:
print(' ',end='')
print(r,end=' ')
print()
def loadFile(input_f):
grid = []
instructions = []
with open(input_f) as file:
for line in file:
# load map part
if line.startswith("#"):
grid.append(list(line.rstrip()))
# load instructions
elif line.startswith(('v','<','>','^')):
instructions += list(line.rstrip())
return grid,instructions
#########################################
# #
# Part 1 #
# #
#########################################
def part1():
grid, instructions = loadFile(input_f)
start = ()
for r,row in enumerate(grid):
for c, col in enumerate(row):
if grid[r][c] == '@':
start = (r,c)
# translate arrows to something get_value_in_direction can use
directions = {
'^':('up',(-1, 0)),
'v':('down',(1, 0)),
'>':('right',(0, 1)),
'<':('left',(0, -1)),
}
pos = start
#print('Initial state')
#nprint(grid,pos)
#print()
#input()
for idx, inst in enumerate(instructions):
#print('Move',inst,'(',len(instructions)-idx,')')
dir = directions[inst][0]
next = get_value_in_direction(grid,pos,dir)
# If wall, don't do anything
if next == '#':
#nprint(grid,pos)
#input()
continue
# If free space, move there
if next == '.':
grid[pos[0]][pos[1]] = '.'
pos = addTuples(pos,directions[inst][1])
grid[pos[0]][pos[1]] = '@'
# If box, move the box and the stack of boxes.
if next == 'O':
#print('@',pos)
prev = pos
next_chars = ['@']
next_poss = [pos]
skip = False
while True:
nextPos = addTuples(pos,directions[inst][1])
nextChar = get_value_in_direction(grid,nextPos)
#print(nextPos,nextChar)
if nextChar == 'O':
next_chars.append(nextChar)
next_poss.append(nextPos)
pos = nextPos
if nextChar == '.':
next_chars.append(nextChar)
next_poss.append(nextPos)
break
if nextChar == '#':
skip = True
pos = prev
break
#input()
if not skip:
for ndx,n in enumerate(next_poss):
if ndx == 0:
grid[n[0]][n[1]] = '.'
pos = next_poss[ndx+1]
else:
grid[n[0]][n[1]] = next_chars[ndx-1]
#input()
#nprint(grid,pos)
#print(len(instructions)-idx)
#time.sleep(0.05)
#input()
result = 0
for r,row in enumerate(grid):
for c,char in enumerate(row):
if char == 'O':
result += (100*r+c)
return result
start_time = time.time()
print('Part 1:',part1(), '\t\t', round((time.time() - start_time)*1000), 'ms')
#########################################
# #
# Part 2 #
# #
#########################################
def resizeGrid(grid:list) -> list:
newGrid = []
for r,row in enumerate(grid):
newRow = []
for c, char in enumerate(row):
if char == '#':
newRow.append('#')
newRow.append('#')
if char == 'O':
newRow.append('[')
newRow.append(']')
if char == '.':
newRow.append('.')
newRow.append('.')
if char == '@':
newRow.append('@')
newRow.append('.')
newGrid.append(newRow)
return newGrid
def part2():
def nprint2(grid, cur: set = None, sign: str = None, positions:list = None):
prepend = 0
for idx, i in enumerate(grid):
for jdx, j in enumerate(i):
if jdx == 0:
prepend = str(idx)+' '
else:
prepend = ''
if (idx, jdx) == cur:
if sign is not None:
if len(sign) > 1:
print(prepend+sign[0] + grid[idx][jdx] + sign[1], end=' ') # Print with sign
else:
print(prepend+colored(sign,'green',attrs=["underline","bold"]), end=' ') # Print sign
else:
print(prepend+colored(grid[idx][jdx],'green',attrs=["underline","bold"]), end='')
else:
if positions is not None:
if (idx,jdx) in positions:
print(prepend+colored(grid[idx][jdx],'red'),end=' ')
else:
print(prepend+grid[idx][jdx], end=' ')
else:
if grid[idx][jdx] == '.':
print(prepend+colored(grid[idx][jdx],'blue',attrs=["concealed"]),end='')
elif grid[idx][jdx] == 'O':
print(prepend+colored(grid[idx][jdx],'red'),end='')
elif grid[idx][jdx] == '#':
print(prepend+colored(grid[idx][jdx],'white'),end='')
else:
print(prepend+grid[idx][jdx], end='') # Regular grid element
print()
for r in range(len(grid[0])):
if r == 0:
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()
grid, instructions = loadFile(input_f)
start = ()
for r,row in enumerate(grid):
for c, col in enumerate(row):
if grid[r][c] == '@':
start = (r,c)
# translate arrows to something get_value_in_direction can use
directions = {
'^':('up',(-1, 0)),
'v':('down',(1, 0)),
'>':('right',(0, 1)),
'<':('left',(0, -1)),
}
pos = start
nprint(grid)
#print(grid)
grid = resizeGrid(grid)
print()
nprint2(grid)
#print(grid)
print()
for idx, inst in enumerate(instructions):
#print('Move',inst,'(',len(instructions)-idx,')')
dir = directions[inst][0]
next = get_value_in_direction(grid,pos,dir)
# If wall, don't do anything
if next == '#':
#nprint(grid,pos)
#input()
continue
# If free space, move there
if next == '.':
grid[pos[0]][pos[1]] = '.'
pos = addTuples(pos,directions[inst][1])
grid[pos[0]][pos[1]] = '@'
# If box, move the box and the stack of boxes.
if next == 'O':
#print('@',pos)
prev = pos
next_chars = ['@']
next_poss = [pos]
skip = False
while True:
nextPos = addTuples(pos,directions[inst][1])
nextChar = get_value_in_direction(grid,nextPos)
#print(nextPos,nextChar)
if nextChar == 'O':
next_chars.append(nextChar)
next_poss.append(nextPos)
pos = nextPos
if nextChar == '.':
next_chars.append(nextChar)
next_poss.append(nextPos)
break
if nextChar == '#':
skip = True
pos = prev
break
#input()
if not skip:
for ndx,n in enumerate(next_poss):
if ndx == 0:
grid[n[0]][n[1]] = '.'
pos = next_poss[ndx+1]
else:
grid[n[0]][n[1]] = next_chars[ndx-1]
start_time = time.time()
print('Part 2:',part2(), '\t\t', round((time.time() - start_time)*1000), 'ms')

Binary file not shown.

30
fred.py
View File

@ -267,21 +267,37 @@ def nprint(grid, cur: set = None, sign: str = None, positions:list = None):
if positions is not None and not isinstance(positions, list):
raise TypeError("Positions must be a list.")
prepend = 0
for idx, i in enumerate(grid):
for jdx, j in enumerate(i):
if (idx, jdx) == cur:
if len(sign) > 1:
print(sign[0] + grid[idx][jdx] + sign[1], end='') # Print with sign
if jdx == 0:
prepend = str(idx)+' '
else:
print(colored(sign,'blue'), end=' ') # Print sign
prepend = ''
if (idx, jdx) == cur:
if sign is not None:
if len(sign) > 1:
print(prepend+sign[0] + grid[idx][jdx] + sign[1], end='') # Print with sign
else:
print(prepend+colored(sign,'green',attrs=["underline"]), end=' ') # Print sign
else:
print(prepend+colored(grid[idx][jdx],'green',attrs=["underline"]), end=' ')
else:
if positions is not None:
if (idx,jdx) in positions:
print(colored(grid[idx][jdx],'red'),end=' ')
print(prepend+colored(grid[idx][jdx],'red'),end=' ')
else:
print(grid[idx][jdx], end=' ')
print(prepend+grid[idx][jdx], end=' ')
else:
print(grid[idx][jdx], end=' ') # Regular grid element
print(prepend+grid[idx][jdx], end=' ') # Regular grid element
print()
for r in range(len(grid[0])):
if r == 0:
print(' ',end='')
print(r,end=' ')
print()
def list2int(x):