Solution to problem 13 in Python

This commit is contained in:
David Doblas Jiménez 2023-06-25 19:12:02 +02:00
parent 3ad989a0cb
commit ef40eb1932

341
src/Year_2018/P13.py Normal file
View File

@ -0,0 +1,341 @@
# --- Day 13: Mine Cart Madness ---
# A crop of this size requires significant logistics to transport produce, soil,
# fertilizer, and so on. The Elves are very busy pushing things around in carts
# on some kind of rudimentary system of tracks they've come up with.
# Seeing as how cart-and-track systems don't appear in recorded history for
# another 1000 years, the Elves seem to be making this up as they go along.
# They haven't even figured out how to avoid collisions yet.
# You map out the tracks (your puzzle input) and see where you can help.
# Tracks consist of straight paths (| and -), curves (/ and \), and
# intersections (+). Curves connect exactly two perpendicular pieces of track;
# for example, this is a closed loop:
# /----\
# | |
# | |
# \----/
# Intersections occur when two perpendicular paths cross. At an intersection,
# a cart is capable of turning left, turning right, or continuing straight.
# Here are two loops connected by two intersections:
# /-----\
# | |
# | /--+--\
# | | | |
# \--+--/ |
# | |
# \-----/
# Several carts are also on the tracks. Carts always face either up (^), down
# (v), left (<), or right (>). (On your initial map, the track under each cart
# is a straight path matching the direction the cart is facing.)
# Each time a cart has the option to turn (by arriving at any intersection), it
# turns left the first time, goes straight the second time, turns right the
# third time, and then repeats those directions starting again with left the
# fourth time, straight the fifth time, and so on. This process is independent
# of the particular intersection at which the cart has arrived - that is, the
# cart has no per-intersection memory.
# Carts all move at the same speed; they take turns moving a single step at a
# time. They do this based on their current location: carts on the top row move
# first (acting from left to right), then carts on the second row move (again
# from left to right), then carts on the third row, and so on. Once each cart
# has moved one step, the process repeats; each of these loops is called a tick.
# For example, suppose there are two carts on a straight track:
# | | | | |
# v | | | |
# | v v | |
# | | | v X
# | | ^ ^ |
# ^ ^ | | |
# | | | | |
# First, the top cart moves. It is facing down (v), so it moves down one square.
# Second, the bottom cart moves. It is facing up (^), so it moves up one square.
# Because all carts have moved, the first tick ends. Then, the process repeats,
# starting with the first cart. The first cart moves down, then the second cart
# moves up - right into the first cart, colliding with it! (The location of the
# crash is marked with an X.) This ends the second and last tick.
# Here is a longer example:
# /->-\
# | | /----\
# | /-+--+-\ |
# | | | | v |
# \-+-/ \-+--/
# \------/
# /-->\
# | | /----\
# | /-+--+-\ |
# | | | | | |
# \-+-/ \->--/
# \------/
# /---v
# | | /----\
# | /-+--+-\ |
# | | | | | |
# \-+-/ \-+>-/
# \------/
# /---\
# | v /----\
# | /-+--+-\ |
# | | | | | |
# \-+-/ \-+->/
# \------/
# /---\
# | | /----\
# | /->--+-\ |
# | | | | | |
# \-+-/ \-+--^
# \------/
# /---\
# | | /----\
# | /-+>-+-\ |
# | | | | | ^
# \-+-/ \-+--/
# \------/
# /---\
# | | /----\
# | /-+->+-\ ^
# | | | | | |
# \-+-/ \-+--/
# \------/
# /---\
# | | /----<
# | /-+-->-\ |
# | | | | | |
# \-+-/ \-+--/
# \------/
# /---\
# | | /---<\
# | /-+--+>\ |
# | | | | | |
# \-+-/ \-+--/
# \------/
# /---\
# | | /--<-\
# | /-+--+-v |
# | | | | | |
# \-+-/ \-+--/
# \------/
# /---\
# | | /-<--\
# | /-+--+-\ |
# | | | | v |
# \-+-/ \-+--/
# \------/
# /---\
# | | /<---\
# | /-+--+-\ |
# | | | | | |
# \-+-/ \-<--/
# \------/
# /---\
# | | v----\
# | /-+--+-\ |
# | | | | | |
# \-+-/ \<+--/
# \------/
# /---\
# | | /----\
# | /-+--v-\ |
# | | | | | |
# \-+-/ ^-+--/
# \------/
# /---\
# | | /----\
# | /-+--+-\ |
# | | | X | |
# \-+-/ \-+--/
# \------/
# After following their respective paths for a while, the carts eventually
# crash. To help prevent crashes, you'd like to know the location of the first
# crash. Locations are given in X,Y coordinates, where the furthest left column
# is X=0 and the furthest top row is Y=0:
# 111
# 0123456789012
# 0/---\
# 1| | /----\
# 2| /-+--+-\ |
# 3| | | X | |
# 4\-+-/ \-+--/
# 5 \------/
# In this example, the location of the first crash is 7,3.
directions = {">": [1, 0], "v": [0, 1], "<": [-1, 0], "^": [0, -1]}
map = []
carts = []
with open("files/P13.txt") as f:
for y, line in enumerate(f):
line = line.strip("\n")
for x, pos in enumerate(line):
if pos in directions:
dx, dy = directions[pos]
carts.append(
{
"x": x,
"y": y,
"dx": dx,
"dy": dy,
"turn_index": 0,
"deleted": False,
}
)
map.append(pos)
cols = len(line)
def move_cart(cart):
cart["x"] += cart["dx"]
cart["y"] += cart["dy"]
def rotate_cart(cart, current):
if current in {"/", "\\", "+"}:
if current == "/":
cart["dx"], cart["dy"] = -cart["dy"], -cart["dx"]
elif current == "\\":
cart["dx"], cart["dy"] = cart["dy"], cart["dx"]
elif current == "+":
if cart["turn_index"] == 0:
cart["dx"], cart["dy"] = cart["dy"], -cart["dx"]
elif cart["turn_index"] == 2:
cart["dx"], cart["dy"] = -cart["dy"], cart["dx"]
cart["turn_index"] = (cart["turn_index"] + 1) % 3
def part_1():
is_collision = False
counter = 0
while not is_collision:
counter += 1
carts.sort(key=lambda s: (s["y"], s["x"]))
for cart in carts:
pos = cart["y"] * cols + cart["x"]
current = map[pos]
if current in {"/", "\\", "+"}:
rotate_cart(cart, current)
move_cart(cart)
coords = set()
for cart in carts:
pos = cart["x"], cart["y"]
if pos in coords:
is_collision = True
crash = pos
break
else:
coords.add(pos)
print(f"The first crash is located at {crash}")
# --- Part Two ---
# There isn't much you can do to prevent crashes in this ridiculous system.
# However, by predicting the crashes, the Elves know where to be in advance and
# instantly remove the two crashing carts the moment any crash occurs.
# They can proceed like this for a while, but eventually, they're going to run
# out of carts. It could be useful to figure out where the last cart that
# hasn't crashed will end up.
# For example:
# />-<\
# | |
# | /<+-\
# | | | v
# \>+</ |
# | ^
# \<->/
# /---\
# | |
# | v-+-\
# | | | |
# \-+-/ |
# | |
# ^---^
# /---\
# | |
# | /-+-\
# | v | |
# \-+-/ |
# ^ ^
# \---/
# /---\
# | |
# | /-+-\
# | | | |
# \-+-/ ^
# | |
# \---/
# After four very expensive crashes, a tick ends with only one cart remaining;
# its final location is 6,4.
# What is the location of the last cart at the end of the first tick where it is
# the only cart left?
def part_2():
global map, carts
counter = 0
while len(carts) > 1:
counter += 1
carts.sort(key=lambda s: (s["y"], s["x"]))
for cart in carts:
pos = cart["y"] * cols + cart["x"]
current = map[pos]
if current in {"/", "\\", "+"}:
rotate_cart(cart, current)
move_cart(cart)
coords = {}
for n, cart in enumerate(carts):
pos = cart["x"], cart["y"]
if pos not in coords:
coords[pos] = n
else:
cart["deleted"] = True
carts[coords[pos]]["deleted"] = True
carts = [cart for cart in carts if not cart["deleted"]]
last_cart_pos = (carts[0]["x"], carts[0]["y"])
print(f"The last cart is located at {last_cart_pos}")
if __name__ == "__main__":
part_1()
part_2()