163 lines
4.5 KiB
Python
163 lines
4.5 KiB
Python
from pathlib import Path
|
|
from itertools import cycle
|
|
import numpy as np
|
|
|
|
filepath = Path("./data/6_example")
|
|
filepath = Path("./data/6_input")
|
|
|
|
UP = ord("^")
|
|
RIGHT = ord(">")
|
|
DOWN = ord("v")
|
|
LEFT = ord("<")
|
|
EMPTY = ord(".")
|
|
FULL = ord("#")
|
|
WALKED_X = ord("|")
|
|
WALKED_Y = ord("-")
|
|
WALKED_B = ord("+")
|
|
BLOCKED = ord("O")
|
|
|
|
|
|
def eval_pos(state):
|
|
for i, line in enumerate(state):
|
|
for j, tile in enumerate(line):
|
|
if tile in "^v><":
|
|
return np.array([i, j]), tile
|
|
|
|
|
|
class surveillance:
|
|
|
|
dirs = cycle(
|
|
[
|
|
np.array([-1, 0]),
|
|
np.array([0, 1]),
|
|
np.array([1, 0]),
|
|
np.array([0, -1]),
|
|
]
|
|
)
|
|
|
|
def __init__(self, state):
|
|
map = [[ord(c) for c in line.replace("^", ".")] for line in state]
|
|
self.map = np.array(list(map))
|
|
self.history = np.copy(self.map)
|
|
self.pos, dir = eval_pos(state)
|
|
self.heading = next(self.dirs)
|
|
self.next_heading = next(self.dirs)
|
|
self.present = True
|
|
self.path = list()
|
|
self.potential_loops = list()
|
|
self.ignore_next = False
|
|
|
|
def __str__(self):
|
|
x, y = self.pos
|
|
map = self.history[:]
|
|
n, m = map.shape
|
|
if (x >= 0 and x < n) and (y >= 0 and y < m):
|
|
map[x, y] = self.view
|
|
for x, y in self.potential_loops:
|
|
map[x, y] = BLOCKED
|
|
return "\n".join(["".join([chr(i) for i in line]) for line in map])
|
|
|
|
def advance(self):
|
|
self.update_history()
|
|
if self.path_clear():
|
|
self.loop_check()
|
|
self.path.append(tuple([*self.pos, self.view]))
|
|
self.pos += self.heading
|
|
else:
|
|
self.heading = self.next_heading
|
|
self.next_heading = next(self.dirs)
|
|
self.ignore_next = True
|
|
|
|
def update_history(self):
|
|
x, y = self.pos
|
|
trail = WALKED_X if self.heading[0] else WALKED_Y
|
|
self.history[x, y] = trail if self.history[x, y] == EMPTY else WALKED_B
|
|
|
|
def path_clear(self):
|
|
n, m = self.map.shape
|
|
u, v = self.pos + self.heading
|
|
if (u < 0 or u >= n) or (v < 0 or v >= m):
|
|
self.present = False
|
|
return True
|
|
if self.map[u, v] == FULL:
|
|
return False
|
|
return True
|
|
|
|
def loop_check(self):
|
|
if self.ignore_next:
|
|
self.ignore_next = False
|
|
return
|
|
x, y = self.pos
|
|
u, v = self.next_heading
|
|
|
|
x_slice = slice(x, None, u) if u else x
|
|
y_slice = slice(y, None, v) if v else y
|
|
line = self.history[x_slice, y_slice]
|
|
try:
|
|
first_block = np.argwhere(line == FULL).flatten()[0]
|
|
except IndexError:
|
|
first_block = len(line)
|
|
|
|
line = line[:first_block]
|
|
walked_2 = WALKED_X if u else WALKED_Y
|
|
qualified = [WALKED_B, walked_2]
|
|
in_line = any([np.any(line == w) for w in qualified])
|
|
|
|
if not in_line:
|
|
return
|
|
idx1 = np.argwhere(line == walked_2).flatten()
|
|
idx1 = idx1[0] if len(idx1) else len(line)
|
|
idx2 = np.argwhere(line == WALKED_B).flatten()
|
|
idx2 = idx2[0] if len(idx2) else len(line)
|
|
idx = min(idx1, idx2)
|
|
p = self.pos + self.next_heading * idx
|
|
if (*p, self.next_view) in self.path:
|
|
self.potential_loops.append(self.pos + self.heading)
|
|
|
|
def _view(self, heading):
|
|
view = {
|
|
(-1, 0): UP,
|
|
(1, 0): DOWN,
|
|
(0, 1): RIGHT,
|
|
(0, -1): LEFT,
|
|
}
|
|
return view[tuple(heading)]
|
|
|
|
@property
|
|
def view(self):
|
|
return self._view(self.heading)
|
|
|
|
@property
|
|
def next_view(self):
|
|
return self._view(self.next_heading)
|
|
|
|
@property
|
|
def walked_tiles(self):
|
|
return {tuple([x, y]) for x, y, _ in self.path}
|
|
|
|
def tick(self, n, plot=True):
|
|
for _ in range(n):
|
|
if not self.present:
|
|
break
|
|
self.advance()
|
|
if plot:
|
|
print(self)
|
|
print(len({tuple([x, y]) for x, y, _ in self.path}))
|
|
print(len(self.potential_loops))
|
|
|
|
def walk(self, plot=True):
|
|
while self.present:
|
|
self.advance()
|
|
if plot:
|
|
print(self)
|
|
print(len({tuple([x, y]) for x, y, _ in self.path}))
|
|
print(len(self.potential_loops))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
with open(filepath) as filein:
|
|
state_0 = [line.rstrip() for line in filein.readlines()]
|
|
|
|
nppsm_lab = surveillance(state_0)
|
|
nppsm_lab.walk(plot=True)
|
|
# nppsm_lab.tick(14)
|