195 lines
4.8 KiB
Python
195 lines
4.8 KiB
Python
import sys
|
|
sys.path.append('../lib')
|
|
|
|
import tools
|
|
|
|
START = 'S'
|
|
|
|
class Direction(object):
|
|
def __init__(self, val: str):
|
|
self.val = val
|
|
|
|
def __eq__(self, other):
|
|
return self.val == other.val
|
|
|
|
def __hash__(self):
|
|
return hash(self.val)
|
|
|
|
def opposite(self):
|
|
if self == N:
|
|
return S
|
|
elif self == S:
|
|
return N
|
|
elif self == E:
|
|
return W
|
|
elif self == W:
|
|
return E
|
|
|
|
N = Direction('N')
|
|
E = Direction('E')
|
|
S = Direction('S')
|
|
W = Direction('W')
|
|
|
|
class Tile(object):
|
|
def __init__(self, val: str, x, y):
|
|
self.val = val
|
|
self.x = x
|
|
self.y = y
|
|
self.links = []
|
|
if val == '|':
|
|
self.links = [N, S]
|
|
elif val == '-':
|
|
self.links = [E, W]
|
|
elif val == 'L':
|
|
self.links = [N, E]
|
|
elif val == 'J':
|
|
self.links = [N, W]
|
|
elif val == '7':
|
|
self.links = [W, S]
|
|
elif val == 'F':
|
|
self.links = [S, E]
|
|
|
|
def __str__(self):
|
|
return f'{self.val}' # x: {self.x} y: {self.y}'
|
|
|
|
def __eq__(self, obj) -> bool:
|
|
return self.x == obj.x and self.y == obj.y
|
|
|
|
def __hash__(self):
|
|
return hash((self.x, self.y))
|
|
|
|
def opposite_pipe_end(self, d: Direction) -> Direction:
|
|
if not d in self.links:
|
|
return None
|
|
if d == self.links[0]:
|
|
return self.links[1]
|
|
else:
|
|
return self.links[0]
|
|
|
|
def has_link_from(self, d: Direction) -> bool:
|
|
return (d in self.links)
|
|
|
|
|
|
class Field(object):
|
|
def __init__(self, lines: list[str]):
|
|
self.grid = [list(l) for l in lines]
|
|
for y in range(len(self.grid)):
|
|
for x in range(len(self.grid[0])):
|
|
self.grid[y][x] = Tile(self.grid[y][x], x, y)
|
|
|
|
def __str__(self):
|
|
out = 'Field:\n'
|
|
for row in self.grid:
|
|
out += ''.join([str(tile) for tile in row]) + '\n'
|
|
return out
|
|
|
|
def _get(self, x, y: int) -> Tile:
|
|
if y < 0 or x < 0 or y >= len(self.grid) or x >= len(self.grid):
|
|
return None
|
|
return self.grid[y][x]
|
|
|
|
def get_adjacent(self, t: Tile, d: Direction) -> Tile:
|
|
x, y = t.x, t.y
|
|
if d == E:
|
|
x += 1
|
|
elif d == S:
|
|
y += 1
|
|
elif d == W:
|
|
x -= 1
|
|
elif d == N:
|
|
y -= 1
|
|
return self._get(x, y)
|
|
|
|
|
|
|
|
|
|
class Walker(Field):
|
|
def __init__(self, lines):
|
|
super().__init__(lines)
|
|
self.loop = []
|
|
self.visited = set()
|
|
|
|
def _get_start(self) -> Tile:
|
|
for y in range(len(self.grid)):
|
|
for x in range(len(self.grid[0])):
|
|
t = self._get(x, y)
|
|
if t.val == START:
|
|
return t
|
|
return None
|
|
|
|
def _find_loop(self):
|
|
start = self._get_start()
|
|
for d in [N, E, S, W]:
|
|
curr = start
|
|
dir = d
|
|
self.loop = []
|
|
while True:
|
|
self.loop.append(curr)
|
|
nxt = self.get_adjacent(curr, dir)
|
|
if not nxt:
|
|
break
|
|
if nxt.val == START:
|
|
return
|
|
if not nxt.has_link_from(dir.opposite()):
|
|
break
|
|
curr = nxt
|
|
dir = nxt.opposite_pipe_end(dir.opposite())
|
|
|
|
|
|
def find_max_steps_in_loop(self) -> int:
|
|
self._find_loop()
|
|
return len(self.loop) // 2
|
|
|
|
def find_captured_by_loop(self) -> int:
|
|
if not self.loop:
|
|
# this fills self.loop slice
|
|
# with loop tiles
|
|
self._find_loop()
|
|
self.visited = set(self.loop)
|
|
inside_loop = set()
|
|
for t in self.loop:
|
|
for d in [N, E, S, W]:
|
|
newt = self.get_adjacent(t, d)
|
|
if not newt or newt in self.visited:
|
|
continue
|
|
if self._is_inside_loop(newt, [N, E, S, W]):
|
|
inside_loop.add(newt)
|
|
self.visited.add(newt)
|
|
return len(inside_loop)
|
|
|
|
|
|
def _is_inside_loop(self, t: Tile, dirs: list[Direction]) -> bool:
|
|
if not t:
|
|
return False
|
|
|
|
if t in self.loop:
|
|
return True
|
|
|
|
for d in dirs:
|
|
newt = self.get_adjacent(t, d)
|
|
if not self._is_inside_loop(newt, [d]):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
r = tools.Reader('test_input.txt')
|
|
lines = r.read()
|
|
|
|
w = Walker(lines)
|
|
print(w)
|
|
n = w.find_captured_by_loop()
|
|
print(n)
|
|
|
|
r = tools.Reader('input.txt')
|
|
lines = r.read()
|
|
|
|
w = Walker(lines)
|
|
steps = w.find_max_steps_in_loop()
|
|
print(steps)
|
|
n = w.find_captured_by_loop()
|
|
print(n)
|
|
|