219 lines
5.3 KiB
Python
219 lines
5.3 KiB
Python
import lib
|
|
|
|
EXAMPLE = """
|
|
..F7.
|
|
.FJ|.
|
|
SJ.L7
|
|
|F--J
|
|
LJ...
|
|
"""
|
|
|
|
EXAMPLE2 = """
|
|
...........
|
|
.S-------7.
|
|
.|F-----7|.
|
|
.||.....||.
|
|
.||.....||.
|
|
.|L-7.F-J|.
|
|
.|..|.|..|.
|
|
.L--J.L--J.
|
|
...........
|
|
"""
|
|
|
|
EXAMPLE3 = """
|
|
..........
|
|
.S------7.
|
|
.|F----7|.
|
|
.||OOOO||.
|
|
.||OOOO||.
|
|
.|L-7F-J|.
|
|
.|II||II|.
|
|
.L--JL--J.
|
|
..........
|
|
"""
|
|
|
|
EXAMPLE4 = """
|
|
FF7FSF7F7F7F7F7F---7
|
|
L|LJ||||||||||||F--J
|
|
FL-7LJLJ||||||LJL-77
|
|
F--JF--7||LJLJIF7FJ-
|
|
L---JF-JLJIIIIFJLJJ7
|
|
|F|F-JF---7IIIL7L|7|
|
|
|FFJF7L7F-JF7IIL---7
|
|
7-L-JL7||F7|L7F-7F7|
|
|
L.L7LFJ|||||FJL7||LJ
|
|
L7JLJL-JLJLJL--JLJ.L
|
|
"""
|
|
|
|
|
|
# | is a vertical pipe connecting north and south.
|
|
# - is a horizontal pipe connecting east and west.
|
|
# L is a 90-degree bend connecting north and east.
|
|
# J is a 90-degree bend connecting north and west.
|
|
# 7 is a 90-degree bend connecting south and west.
|
|
# F is a 90-degree bend connecting south and east.
|
|
# . is ground; there is no pipe in this tile.
|
|
# S is the starting position of the animal; there is a pipe on this tile, but your sketch doesn't show what shape the pipe has.
|
|
|
|
# Mapping of in-direction to out-direction for each piece.
|
|
DIRS = {
|
|
'|': {
|
|
(1, 0): (1, 0),
|
|
(-1, 0): (-1, 0),
|
|
},
|
|
'-': {
|
|
(0, -1): (0, -1),
|
|
(0, 1): (0, 1),
|
|
},
|
|
'L': {
|
|
(1, 0): (0, 1),
|
|
(0, -1): (-1, 0),
|
|
},
|
|
'J': {
|
|
(0, 1): (-1, 0),
|
|
(1, 0): (0, -1),
|
|
},
|
|
'7': {
|
|
(0, 1): (1, 0),
|
|
(-1, 0): (0, -1),
|
|
},
|
|
'F': {
|
|
(-1, 0): (0, 1),
|
|
(0, -1): (1, 0),
|
|
}
|
|
}
|
|
|
|
RIGHTS = {
|
|
'|': {
|
|
(1, 0): [(0, -1)],
|
|
(-1, 0): [(0, 1)],
|
|
},
|
|
'-': {
|
|
(0, -1): [(-1, 0)],
|
|
(0, 1): [(1, 0)],
|
|
},
|
|
'L': {
|
|
(1, 0): [(0, -1), (1, 0)],
|
|
(0, -1): [(-1, 1)],
|
|
},
|
|
'J': {
|
|
(0, 1): [(1, 0), (0, 1)],
|
|
(1, 0): [(-1, -1)],
|
|
},
|
|
'7': {
|
|
(0, 1): [(1, -1)],
|
|
(-1, 0): [(0, 1), (-1, 0)],
|
|
},
|
|
'F': {
|
|
(-1, 0): [(1, 1)],
|
|
(0, -1): [(-1, 0), (0, -1)],
|
|
}
|
|
}
|
|
|
|
def solve(lines: list[str], return_path=False):
|
|
res = 0
|
|
maze = []
|
|
start = ()
|
|
max_path, max_right = [], []
|
|
maze = list(map(list, lines))
|
|
for i, row in enumerate(maze):
|
|
for j, col in enumerate(row):
|
|
if col == "S":
|
|
start = (i , j)
|
|
row_max, col_max = len(maze), len(maze[0])
|
|
|
|
for current_dir in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
|
current_field = start
|
|
steps = 0
|
|
path = []
|
|
right = []
|
|
# print(f"START: {start} {current_dir=}")
|
|
while True:
|
|
path.append(current_field)
|
|
next_field = (current_field[0] + current_dir[0], current_field[1] + current_dir[1])
|
|
if next_field == start:
|
|
break
|
|
|
|
if next_field[0] < 0 or next_field[1] < 0 or next_field[0] >= row_max or next_field[1] >= col_max:
|
|
# print(f"BREAK: Cannot go to {next_field=}!")
|
|
break
|
|
|
|
prev_dir = current_dir
|
|
next_char = maze[next_field[0]][next_field[1]]
|
|
try:
|
|
current_dir = DIRS[next_char][current_dir]
|
|
except KeyError:
|
|
# print(f"BREAK: Cannot go from {current_field=} with {current_dir=} to {next_field=} ({next_char})!")
|
|
break
|
|
|
|
for rights in RIGHTS[next_char][prev_dir]:
|
|
rf = (next_field[0] + rights[0], next_field[1] + rights[1])
|
|
right.append(rf)
|
|
|
|
current_field = next_field
|
|
steps += 1
|
|
res = max(res, (steps + 1) // 2)
|
|
if len(path) > len(max_path):
|
|
max_path = path
|
|
max_right = right
|
|
|
|
if return_path:
|
|
return res, max_path, max_right
|
|
else:
|
|
return res
|
|
|
|
def solve2(lines: list[str]):
|
|
row_max = len(lines)
|
|
col_max = len(lines[0])
|
|
length, path, right = solve(lines, True)
|
|
|
|
def clean(fields):
|
|
fields = list(set(fields))
|
|
cleaned = []
|
|
for f in fields:
|
|
if f in path:
|
|
continue
|
|
if f[0] < 0 or f[1] < 0 or f[0] >= row_max or f[1] >= col_max:
|
|
continue
|
|
cleaned.append(f)
|
|
return cleaned
|
|
|
|
right = clean(right)
|
|
visited = set()
|
|
to_visit = list(right)
|
|
while to_visit:
|
|
current = to_visit.pop()
|
|
for nb in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
|
f = (current[0] + nb[0], current[1] + nb[1])
|
|
if f[0] < 0 or f[1] < 0 or f[0] >= row_max or f[1] >= col_max:
|
|
continue
|
|
|
|
if f not in visited and f not in path:
|
|
right.append(f)
|
|
to_visit.append(f)
|
|
visited.add(f)
|
|
|
|
right = clean(right)
|
|
|
|
n_all = row_max * col_max
|
|
n_right = len(right)
|
|
n_left = n_all - n_right - len(path)
|
|
|
|
return min(n_left, n_right)
|
|
|
|
def main():
|
|
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
|
print("Example 1:", solve(lines))
|
|
|
|
lines = lib.str_to_lines_no_empty(open("i10.txt").read())
|
|
print("Solution 1:", solve(lines))
|
|
|
|
lines = lib.str_to_lines_no_empty(EXAMPLE4)
|
|
print("Example 2:", solve2(lines))
|
|
|
|
lines = lib.str_to_lines_no_empty(open("i10.txt").read())
|
|
print("Solution 2:", solve2(lines))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|