This repository has been archived on 2024-12-22. You can view files and clone it, but cannot push or open issues or pull requests.
aoc2023/d10.py
2023-12-15 18:20:31 -05:00

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()