117 lines
3.4 KiB
Python
117 lines
3.4 KiB
Python
from tools.aoc import AOCDay
|
|
from tools.coordinate import Coordinate
|
|
from typing import Any
|
|
|
|
from tools.grid import Grid
|
|
|
|
DIRS = [
|
|
Coordinate(-1, 0),
|
|
Coordinate(0, -1),
|
|
Coordinate(1, 0),
|
|
Coordinate(0, 1),
|
|
] # left, up, right, down
|
|
|
|
|
|
class Cart:
|
|
def __init__(self, num: int, pos: Coordinate, cur_dir: int = 0):
|
|
self.destroyed = False
|
|
self.cur_dir = cur_dir
|
|
self.next_turn = 0
|
|
self.pos = pos
|
|
self.num = num
|
|
|
|
def crossing_turn(self):
|
|
self.cur_dir = (self.cur_dir + self.next_turn - 1) % 4
|
|
self.next_turn = (self.next_turn + 1) % 3
|
|
|
|
def corner_turn(self, corner: tuple):
|
|
self.cur_dir = (self.cur_dir + corner[self.cur_dir]) % 4
|
|
|
|
def move(self):
|
|
self.pos += DIRS[self.cur_dir]
|
|
|
|
def __str__(self):
|
|
return "Cart(%s, %s, %s)" % (self.num, self.pos, self.cur_dir)
|
|
|
|
|
|
class Day(AOCDay):
|
|
inputs = [
|
|
[
|
|
("7,3", "input13_test"),
|
|
("53,133", "input13"),
|
|
],
|
|
[
|
|
("6,4", "input13_test2"),
|
|
("111,68", "input13"),
|
|
],
|
|
]
|
|
|
|
def parse_input(self) -> (Grid, set[Cart]):
|
|
grid = Grid()
|
|
carts = set()
|
|
num = 0
|
|
for y, line in enumerate(self.getInput()):
|
|
for x, c in enumerate(line):
|
|
if c == " ":
|
|
continue
|
|
elif c == "/":
|
|
grid.set(Coordinate(x, y), (-1, 1, -1, 1))
|
|
elif c == "\\":
|
|
grid.set(Coordinate(x, y), (1, -1, 1, -1))
|
|
elif c == "+":
|
|
grid.set(Coordinate(x, y), (1,))
|
|
elif c == "|" or c == "-":
|
|
grid.set(Coordinate(x, y), (0,))
|
|
elif c in ["^", "<", "v", ">"]:
|
|
grid.set(Coordinate(x, y), (0,))
|
|
carts.add(
|
|
Cart(num, Coordinate(x, y), ["<", "^", ">", "v"].index(c))
|
|
)
|
|
num += 1
|
|
|
|
return grid, carts
|
|
|
|
def get_crash(self, return_first: bool) -> str:
|
|
track, carts = self.parse_input()
|
|
while True:
|
|
if len(carts) == 1:
|
|
winner = carts.pop()
|
|
return "%s,%s" % (winner.pos.x, winner.pos.y)
|
|
|
|
cart_order = list(sorted(carts, key=lambda c: c.pos))
|
|
cart_pos = [c.pos for c in cart_order]
|
|
for cart in cart_order:
|
|
if cart.destroyed:
|
|
continue
|
|
|
|
cart.move()
|
|
if cart.pos in cart_pos:
|
|
if return_first:
|
|
return "%s,%s" % (cart.pos.x, cart.pos.y)
|
|
carts.remove(cart)
|
|
cart.destroyed = True
|
|
for x in cart_order:
|
|
if x.pos == cart.pos and cart.num != x.num:
|
|
x.destroyed = True
|
|
carts.remove(x)
|
|
break
|
|
|
|
cart_pos = [c.pos for c in carts]
|
|
|
|
track_pos = track.get(cart.pos)
|
|
if len(track_pos) > 1:
|
|
cart.corner_turn(track_pos)
|
|
elif track_pos[0] == 1:
|
|
cart.crossing_turn()
|
|
|
|
def part1(self) -> Any:
|
|
return self.get_crash(True)
|
|
|
|
def part2(self) -> Any:
|
|
return self.get_crash(False)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
day = Day(2018, 13)
|
|
day.run(verbose=True)
|