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)