141 lines
3.9 KiB
Python
141 lines
3.9 KiB
Python
from grid import Grid
|
|
from intcode import Intcode
|
|
from vector import *
|
|
|
|
|
|
def main():
|
|
with open("day17.txt", "rt") as input_file:
|
|
codes = Intcode.parse_input(input_file.read().rstrip())
|
|
|
|
# Part 1
|
|
robot = Robot(codes)
|
|
robot.run()
|
|
intersections = robot.intersect()
|
|
print("Sum:", sum(x * y for x, y in intersections))
|
|
|
|
# Part 2
|
|
start = robot.start
|
|
sx, sy, so = start
|
|
print(f"Start at {(sx, sy)} oriented {so}.")
|
|
path = compute_path(robot.img, start)
|
|
print(f"Traced path: {path}.")
|
|
# Patterns found in my editor from the above result...
|
|
patterns = {
|
|
"M": "ABBACBCCBA",
|
|
"A": ['R', 10, 'R', 8, 'L', 10, 'L', 10],
|
|
"B": ['R', 8, 'L', 6, 'L', 6],
|
|
"C": ['L', 10, 'R', 10, 'L', 6],
|
|
}
|
|
codes[0] = 2
|
|
robot = Robot(codes, patterns=patterns, video_fb=False)
|
|
robot.run()
|
|
|
|
|
|
N = ord("\n")
|
|
S = ord("#")
|
|
|
|
|
|
class Robot(Intcode):
|
|
|
|
ORI_U = (0, -1)
|
|
ORI_R = (1, 0)
|
|
ORI_D = (0, +1)
|
|
ORI_L = (-1, 0)
|
|
ORI_INDEXES = {ORI_U: 0, ORI_R: 1, ORI_D: 2, ORI_L: 3}
|
|
ASCII_ORI = {"^": ORI_U, ">": ORI_R, "v": ORI_D, "<": ORI_L}
|
|
|
|
def __init__(self, *args, patterns=None, video_fb=False, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.out_x = 0
|
|
self.out_y = 0
|
|
self.img = Grid()
|
|
self.start = None
|
|
self.moving = False
|
|
self.video_fb = video_fb
|
|
self.video_last_byte = None
|
|
self.patterns = patterns
|
|
self.robot_inputs = []
|
|
if self.patterns:
|
|
self.input_gen()
|
|
|
|
def input_data(self):
|
|
self.moving = True
|
|
data = self.robot_inputs.pop(0)
|
|
return data
|
|
|
|
def input_gen(self):
|
|
self.robot_inputs += [ord(x) for x in ",".join(self.patterns["M"])] + [N]
|
|
for pname in "ABC":
|
|
pdata = ",".join(map(lambda x: str(x), self.patterns[pname]))
|
|
self.robot_inputs += [ord(x) for x in pdata] + [N]
|
|
self.robot_inputs += [ord("y" if self.video_fb else "n"), N]
|
|
|
|
def output_data(self, data):
|
|
if data > 256:
|
|
print("Output", data)
|
|
return
|
|
|
|
if self.video_fb:
|
|
print(chr(data), end="")
|
|
if all(d == N for d in [data, self.video_last_byte]):
|
|
input()
|
|
self.video_last_byte = data
|
|
if self.moving:
|
|
return
|
|
|
|
if data == N:
|
|
self.out_x, self.out_y = 0, self.out_y + 1
|
|
else:
|
|
self.img.g[self.out_y][self.out_x] = data
|
|
self.out_x += 1
|
|
|
|
def intersect(self):
|
|
intersections = []
|
|
g = self.img.g
|
|
for y, row in list(g.items()).copy():
|
|
for x, v in list(row.items()).copy():
|
|
if all(e == S for e in [v, g[y][x-1], g[y][x+1], g[y+1][x], g[y-1][x]]):
|
|
intersections.append((x, y))
|
|
elif chr(v) in Robot.ASCII_ORI.keys():
|
|
self.start = (x, y, Robot.ASCII_ORI[chr(v)])
|
|
return intersections
|
|
|
|
|
|
def compute_path(grid, start):
|
|
pos, o = (start[0], start[1]), start[2]
|
|
path = []
|
|
steps = 0
|
|
while True:
|
|
forward_pos = v2a(pos, o)
|
|
near_pos = grid.near_items(pos)
|
|
if near_pos[forward_pos] == S:
|
|
pos = forward_pos
|
|
steps += 1
|
|
continue
|
|
if steps:
|
|
path.append(steps)
|
|
steps = 0
|
|
|
|
for n in [p for p, v in near_pos.items() if v == S]:
|
|
res = turn(pos, n, o)
|
|
if res is not None:
|
|
path.append(res[0])
|
|
o = res[1]
|
|
break
|
|
else:
|
|
break
|
|
|
|
return path
|
|
|
|
def turn(pos, next_pos, cur_ori):
|
|
new_ori = (next_pos[0] - pos[0], next_pos[1] - pos[1])
|
|
diff = (Robot.ORI_INDEXES[new_ori] - Robot.ORI_INDEXES[cur_ori]) % 4
|
|
if diff == 1:
|
|
return "R", new_ori
|
|
elif diff == 3:
|
|
return "L", new_ori
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|