#%%
# define up, down, left, right directions
U, D, L, R = (0, -1), (0, 1), (-1, 0), (1, 0)

# map input characters to directions
directions = {'^': U, 'v': D, '<': L, '>': R}

# map directions to new directions for various scenarios
straight = {U: U, D: D, L: L, R: R}
left_turn = {U: L, D: R, L: D, R: U}
right_turn = {U: R, D: L, L: U, R: D}
forward_slash_turn = {U: R, D: L, L: D, R: U}
back_slash_turn = {U: L, D: R, L: U, R: D}

class Cart:
    def __init__(self, p, d):
        self.p = p # position
        self.d = d # direction
        self.i = 0 # turn index
        self.ok = True # ok is set to False after a collision
    def step(self, grid):
        # make one step in the current direction
        self.p = (self.p[0] + self.d[0], self.p[1] + self.d[1])
        # lookup the grid character at the new position
        c = grid[self.p]
        if c == '+':
            # intersection; make a turn based on the current index
            turn = [left_turn, straight, right_turn][self.i % 3]
            self.d = turn[self.d]
            self.i += 1
        elif c == '/':
            self.d = forward_slash_turn[self.d]
        elif c == '\\':
            self.d = back_slash_turn[self.d]
    def hits(self, other):
        # return True if this cart hits the other
        return self != other and self.ok and other.ok and self.p == other.p

    def __repr__(self):
        return f'{self.p}'

grid = {}  # grid maps (x, y) positions to characters from the input
carts = [] # carts is a list of Cart instances

with open('input.txt') as fp:
    for y, line in enumerate(fp):
        for x, c in enumerate(line):
            grid[(x, y)] = c
            if c in directions:
                carts.append(Cart((x, y), directions[c]))

# debug
out_filename = 'fogleman_out.txt'
if os.path.isfile(out_filename):
    os.unlink(out_filename)

part1 = part2 = None
step = 0
try:
    while True:
        # for each new tick, sort the carts by Y and then X
        carts = sorted(carts, key=lambda x: (x.p[1], x.p[0]))

        # debug
        with open(out_filename, 'a') as fp:
            fp.write(str(step) + ':\n')
            [fp.write(str(c)+ '\n') for c in carts]

        step += 1

        for cart in carts:
            # update this cart
            cart.step(grid)
            # check for collisions
            for other in carts:
                if cart.hits(other):
                    cart.ok = other.ok = False
                    # first collision is our part 1 result
                    part1 = part1 or cart.p
                    raise StopIteration
            
        # remove carts that crashed
        # carts = [x for x in carts if x.ok]
        # # if only one cart is left, part 2 is done
        # if len(carts) == 1:
        #     part2 = carts[0].p
        #     break
except StopIteration:
    print(part1)

a79a05fc15566bd9a8a42d9852077ac64921371b
<mask token>


class Cart:
    <mask token>
    <mask token>
    <mask token>

    def __repr__(self):
        return f'{self.p}'


<mask token>

<mask token>


class Cart:

    def __init__(self, p, d):
        self.p = p
        self.d = d
        self.i = 0
        self.ok = True

    def step(self, grid):
        self.p = self.p[0] + self.d[0], self.p[1] + self.d[1]
        c = grid[self.p]
        if c == '+':
            turn = [left_turn, straight, right_turn][self.i % 3]
            self.d = turn[self.d]
            self.i += 1
        elif c == '/':
            self.d = forward_slash_turn[self.d]
        elif c == '\\':
            self.d = back_slash_turn[self.d]

    def hits(self, other):
        return self != other and self.ok and other.ok and self.p == other.p

    def __repr__(self):
        return f'{self.p}'


<mask token>

<mask token>


class Cart:

    def __init__(self, p, d):
        self.p = p
        self.d = d
        self.i = 0
        self.ok = True

    def step(self, grid):
        self.p = self.p[0] + self.d[0], self.p[1] + self.d[1]
        c = grid[self.p]
        if c == '+':
            turn = [left_turn, straight, right_turn][self.i % 3]
            self.d = turn[self.d]
            self.i += 1
        elif c == '/':
            self.d = forward_slash_turn[self.d]
        elif c == '\\':
            self.d = back_slash_turn[self.d]

    def hits(self, other):
        return self != other and self.ok and other.ok and self.p == other.p

    def __repr__(self):
        return f'{self.p}'


<mask token>
with open('input.txt') as fp:
    for y, line in enumerate(fp):
        for x, c in enumerate(line):
            grid[x, y] = c
            if c in directions:
                carts.append(Cart((x, y), directions[c]))
<mask token>
if os.path.isfile(out_filename):
    os.unlink(out_filename)
<mask token>
try:
    while True:
        carts = sorted(carts, key=lambda x: (x.p[1], x.p[0]))
        with open(out_filename, 'a') as fp:
            fp.write(str(step) + ':\n')
            [fp.write(str(c) + '\n') for c in carts]
        step += 1
        for cart in carts:
            cart.step(grid)
            for other in carts:
                if cart.hits(other):
                    cart.ok = other.ok = False
                    part1 = part1 or cart.p
                    raise StopIteration
except StopIteration:
    print(part1)

U, D, L, R = (0, -1), (0, 1), (-1, 0), (1, 0)
directions = {'^': U, 'v': D, '<': L, '>': R}
straight = {U: U, D: D, L: L, R: R}
left_turn = {U: L, D: R, L: D, R: U}
right_turn = {U: R, D: L, L: U, R: D}
forward_slash_turn = {U: R, D: L, L: D, R: U}
back_slash_turn = {U: L, D: R, L: U, R: D}


class Cart:

    def __init__(self, p, d):
        self.p = p
        self.d = d
        self.i = 0
        self.ok = True

    def step(self, grid):
        self.p = self.p[0] + self.d[0], self.p[1] + self.d[1]
        c = grid[self.p]
        if c == '+':
            turn = [left_turn, straight, right_turn][self.i % 3]
            self.d = turn[self.d]
            self.i += 1
        elif c == '/':
            self.d = forward_slash_turn[self.d]
        elif c == '\\':
            self.d = back_slash_turn[self.d]

    def hits(self, other):
        return self != other and self.ok and other.ok and self.p == other.p

    def __repr__(self):
        return f'{self.p}'


grid = {}
carts = []
with open('input.txt') as fp:
    for y, line in enumerate(fp):
        for x, c in enumerate(line):
            grid[x, y] = c
            if c in directions:
                carts.append(Cart((x, y), directions[c]))
out_filename = 'fogleman_out.txt'
if os.path.isfile(out_filename):
    os.unlink(out_filename)
part1 = part2 = None
step = 0
try:
    while True:
        carts = sorted(carts, key=lambda x: (x.p[1], x.p[0]))
        with open(out_filename, 'a') as fp:
            fp.write(str(step) + ':\n')
            [fp.write(str(c) + '\n') for c in carts]
        step += 1
        for cart in carts:
            cart.step(grid)
            for other in carts:
                if cart.hits(other):
                    cart.ok = other.ok = False
                    part1 = part1 or cart.p
                    raise StopIteration
except StopIteration:
    print(part1)

#%%
# define up, down, left, right directions
U, D, L, R = (0, -1), (0, 1), (-1, 0), (1, 0)

# map input characters to directions
directions = {'^': U, 'v': D, '<': L, '>': R}

# map directions to new directions for various scenarios
straight = {U: U, D: D, L: L, R: R}
left_turn = {U: L, D: R, L: D, R: U}
right_turn = {U: R, D: L, L: U, R: D}
forward_slash_turn = {U: R, D: L, L: D, R: U}
back_slash_turn = {U: L, D: R, L: U, R: D}

class Cart:
    def __init__(self, p, d):
        self.p = p # position
        self.d = d # direction
        self.i = 0 # turn index
        self.ok = True # ok is set to False after a collision
    def step(self, grid):
        # make one step in the current direction
        self.p = (self.p[0] + self.d[0], self.p[1] + self.d[1])
        # lookup the grid character at the new position
        c = grid[self.p]
        if c == '+':
            # intersection; make a turn based on the current index
            turn = [left_turn, straight, right_turn][self.i % 3]
            self.d = turn[self.d]
            self.i += 1
        elif c == '/':
            self.d = forward_slash_turn[self.d]
        elif c == '\\':
            self.d = back_slash_turn[self.d]
    def hits(self, other):
        # return True if this cart hits the other
        return self != other and self.ok and other.ok and self.p == other.p

    def __repr__(self):
        return f'{self.p}'

grid = {}  # grid maps (x, y) positions to characters from the input
carts = [] # carts is a list of Cart instances

with open('input.txt') as fp:
    for y, line in enumerate(fp):
        for x, c in enumerate(line):
            grid[(x, y)] = c
            if c in directions:
                carts.append(Cart((x, y), directions[c]))

# debug
out_filename = 'fogleman_out.txt'
if os.path.isfile(out_filename):
    os.unlink(out_filename)

part1 = part2 = None
step = 0
try:
    while True:
        # for each new tick, sort the carts by Y and then X
        carts = sorted(carts, key=lambda x: (x.p[1], x.p[0]))

        # debug
        with open(out_filename, 'a') as fp:
            fp.write(str(step) + ':\n')
            [fp.write(str(c)+ '\n') for c in carts]

        step += 1

        for cart in carts:
            # update this cart
            cart.step(grid)
            # check for collisions
            for other in carts:
                if cart.hits(other):
                    cart.ok = other.ok = False
                    # first collision is our part 1 result
                    part1 = part1 or cart.p
                    raise StopIteration
            
        # remove carts that crashed
        # carts = [x for x in carts if x.ok]
        # # if only one cart is left, part 2 is done
        # if len(carts) == 1:
        #     part2 = carts[0].p
        #     break
except StopIteration:
    print(part1)
