Solution to problem 10 in Python
This commit is contained in:
parent
36d2884753
commit
90e3fb8ded
@ -134,53 +134,235 @@
|
|||||||
# it take to get from the starting position to the point farthest from the
|
# it take to get from the starting position to the point farthest from the
|
||||||
# starting position?
|
# starting position?
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
with open("files/P10.txt") as f:
|
with open("files/P10.txt") as f:
|
||||||
p = [row for row in f.read().strip().split("\n")]
|
grid = [row for row in f.read().strip().split("\n")]
|
||||||
|
|
||||||
|
U, D, L, R = (-1, 0), (1, 0), (0, -1), (0, 1)
|
||||||
|
directions = {
|
||||||
|
"|": (U, D),
|
||||||
|
"-": (L, R),
|
||||||
|
"L": (U, R),
|
||||||
|
"J": (U, L),
|
||||||
|
"7": (D, L),
|
||||||
|
"F": (D, R),
|
||||||
|
".": (),
|
||||||
|
}
|
||||||
|
|
||||||
def bfs(grid, directions, connections):
|
s_values = set(directions) - {"."}
|
||||||
start = [pos for pos, c in grid.items() if c == "S"][0]
|
|
||||||
queue, seen = [(0, start)], set()
|
# where is "S" located?
|
||||||
while queue:
|
ss = [
|
||||||
count, (x, y) = queue.pop(0)
|
(r, c)
|
||||||
seen.add((x, y))
|
for r, row in enumerate(grid)
|
||||||
for dx, dy in directions[grid[x, y]]:
|
for c, ch in enumerate(row)
|
||||||
new_x, new_y = x + dx, y + dy
|
if ch == "S"
|
||||||
if (new_x, new_y) not in grid or (new_x, new_y) in seen:
|
]
|
||||||
continue
|
# must be unique
|
||||||
if grid[new_x, new_y] not in connections[dx, dy]:
|
assert len(ss) == 1
|
||||||
continue
|
sr, sc = ss[0]
|
||||||
queue.append((count + 1, (new_x, new_y)))
|
|
||||||
return count, seen
|
# if we are in the first row, we can't go up or if the above row does not let us
|
||||||
|
# go down
|
||||||
|
if sr == 0 or D not in directions[grid[sr - 1][sc]]:
|
||||||
|
# s is not on top of a pipe that point upwards
|
||||||
|
s_values -= set("|LJ")
|
||||||
|
|
||||||
|
# if we are in the last row, we can't go down or we can't go up from the char
|
||||||
|
# below us
|
||||||
|
if sr == len(grid) - 1 or U not in directions[grid[sr + 1][sc]]:
|
||||||
|
# s is not on top of a pipe that point downwards
|
||||||
|
s_values -= set("|7F")
|
||||||
|
|
||||||
|
# if we are in the first column, we can´t go left or if the pipe towards left
|
||||||
|
# does not go right
|
||||||
|
if sc == 0 or R not in directions[grid[sr][sc - 1]]:
|
||||||
|
s_values -= set("-J7")
|
||||||
|
|
||||||
|
# if we are in the last column, we can't go to the right or if the pipe towards
|
||||||
|
# right does not go left
|
||||||
|
if sc == len(grid[0]) - 1 or L not in directions[grid[sr][sc + 1]]:
|
||||||
|
s_values -= set("-LF")
|
||||||
|
|
||||||
|
# only one value is possible
|
||||||
|
assert len(s_values) == 1
|
||||||
|
[S] = s_values
|
||||||
|
|
||||||
|
# replace grid value of S with its true value
|
||||||
|
grid = [row.replace("S", S) for row in grid]
|
||||||
|
|
||||||
|
|
||||||
def part1():
|
def part1():
|
||||||
N, S, W, E = (0, -1), (0, 1), (-1, 0), (1, 0)
|
# we will traverse our pipe to find the path length divided by 2, because we
|
||||||
directions = {
|
# need to find the furthest point from the starting position
|
||||||
"S": [N, S, W, E],
|
prev_r = sr
|
||||||
"|": [N, S],
|
prev_c = sc
|
||||||
"-": [E, W],
|
|
||||||
"L": [N, E],
|
|
||||||
"J": [N, W],
|
|
||||||
"7": [S, W],
|
|
||||||
"F": [S, E],
|
|
||||||
}
|
|
||||||
connections = {
|
|
||||||
N: {"|", "7", "F"},
|
|
||||||
S: {"|", "L", "J"},
|
|
||||||
E: {"-", "J", "7"},
|
|
||||||
W: {"-", "L", "F"},
|
|
||||||
}
|
|
||||||
grid = {
|
|
||||||
(x, y): c
|
|
||||||
for y, row in enumerate(p)
|
|
||||||
for x, c in enumerate(row)
|
|
||||||
if c != "."
|
|
||||||
}
|
|
||||||
total, _ = bfs(grid, directions, connections)
|
|
||||||
|
|
||||||
|
# we pick a random direction, first one for example
|
||||||
|
dr, dc = directions[grid[sr][sc]][0]
|
||||||
|
|
||||||
|
next_r = prev_r + dr
|
||||||
|
next_c = prev_c + dc
|
||||||
|
|
||||||
|
# # we count the start position
|
||||||
|
seen = {(sr, sc)}
|
||||||
|
|
||||||
|
while (next_r, next_c) != (sr, sc):
|
||||||
|
seen.add((next_r, next_c))
|
||||||
|
# for each direction we can take in the pipe for the next position
|
||||||
|
for dr, dc in directions[grid[next_r][next_c]]:
|
||||||
|
if (next_r + dr, next_c + dc) != (prev_r, prev_c):
|
||||||
|
# set previous to our current position
|
||||||
|
prev_r = next_r
|
||||||
|
prev_c = next_c
|
||||||
|
# moves to our next position
|
||||||
|
next_r += dr
|
||||||
|
next_c += dc
|
||||||
|
# we are done!
|
||||||
|
break
|
||||||
|
|
||||||
|
total = len(seen) // 2
|
||||||
print(f"You need {total} steps")
|
print(f"You need {total} steps")
|
||||||
|
# for part 2
|
||||||
|
return grid, seen
|
||||||
|
|
||||||
|
|
||||||
|
# --- Part Two ---
|
||||||
|
|
||||||
|
# You quickly reach the farthest point of the loop, but the animal never
|
||||||
|
# emerges. Maybe its nest is within the area enclosed by the loop?
|
||||||
|
|
||||||
|
# To determine whether it's even worth taking the time to search for such a
|
||||||
|
# nest, you should calculate how many tiles are contained within the loop. For
|
||||||
|
# example:
|
||||||
|
|
||||||
|
# ...........
|
||||||
|
# .S-------7.
|
||||||
|
# .|F-----7|.
|
||||||
|
# .||.....||.
|
||||||
|
# .||.....||.
|
||||||
|
# .|L-7.F-J|.
|
||||||
|
# .|..|.|..|.
|
||||||
|
# .L--J.L--J.
|
||||||
|
# ...........
|
||||||
|
|
||||||
|
# The above loop encloses merely four tiles - the two pairs of . in the
|
||||||
|
# southwest and southeast (marked I below). The middle . tiles (marked O below)
|
||||||
|
# are not in the loop. Here is the same loop again with those regions marked:
|
||||||
|
|
||||||
|
# ...........
|
||||||
|
# .S-------7.
|
||||||
|
# .|F-----7|.
|
||||||
|
# .||OOOOO||.
|
||||||
|
# .||OOOOO||.
|
||||||
|
# .|L-7OF-J|.
|
||||||
|
# .|II|O|II|.
|
||||||
|
# .L--JOL--J.
|
||||||
|
# .....O.....
|
||||||
|
|
||||||
|
# In fact, there doesn't even need to be a full tile path to the outside for
|
||||||
|
# tiles to count as outside the loop - squeezing between pipes is also allowed!
|
||||||
|
# Here, I is still within the loop and O is still outside the loop:
|
||||||
|
|
||||||
|
# ..........
|
||||||
|
# .S------7.
|
||||||
|
# .|F----7|.
|
||||||
|
# .||OOOO||.
|
||||||
|
# .||OOOO||.
|
||||||
|
# .|L-7F-J|.
|
||||||
|
# .|II||II|.
|
||||||
|
# .L--JL--J.
|
||||||
|
# ..........
|
||||||
|
|
||||||
|
# In both of the above examples, 4 tiles are enclosed by the loop.
|
||||||
|
|
||||||
|
# Here's a larger example:
|
||||||
|
|
||||||
|
# .F----7F7F7F7F-7....
|
||||||
|
# .|F--7||||||||FJ....
|
||||||
|
# .||.FJ||||||||L7....
|
||||||
|
# FJL7L7LJLJ||LJ.L-7..
|
||||||
|
# L--J.L7...LJS7F-7L7.
|
||||||
|
# ....F-J..F7FJ|L7L7L7
|
||||||
|
# ....L7.F7||L7|.L7L7|
|
||||||
|
# .....|FJLJ|FJ|F7|.LJ
|
||||||
|
# ....FJL-7.||.||||...
|
||||||
|
# ....L---J.LJ.LJLJ...
|
||||||
|
|
||||||
|
# The above sketch has many random bits of ground, some of which are in the
|
||||||
|
# loop (I) and some of which are outside it (O):
|
||||||
|
|
||||||
|
# OF----7F7F7F7F-7OOOO
|
||||||
|
# O|F--7||||||||FJOOOO
|
||||||
|
# O||OFJ||||||||L7OOOO
|
||||||
|
# FJL7L7LJLJ||LJIL-7OO
|
||||||
|
# L--JOL7IIILJS7F-7L7O
|
||||||
|
# OOOOF-JIIF7FJ|L7L7L7
|
||||||
|
# OOOOL7IF7||L7|IL7L7|
|
||||||
|
# OOOOO|FJLJ|FJ|F7|OLJ
|
||||||
|
# OOOOFJL-7O||O||||OOO
|
||||||
|
# OOOOL---JOLJOLJLJOOO
|
||||||
|
|
||||||
|
# In this larger example, 8 tiles are enclosed by the loop.
|
||||||
|
|
||||||
|
# Any tile that isn't part of the main loop can count as being enclosed by the
|
||||||
|
# loop. Here's another example with many bits of junk pipe lying around that
|
||||||
|
# aren't connected to the main loop at all:
|
||||||
|
|
||||||
|
# FF7FSF7F7F7F7F7F---7
|
||||||
|
# L|LJ||||||||||||F--J
|
||||||
|
# FL-7LJLJ||||||LJL-77
|
||||||
|
# F--JF--7||LJLJ7F7FJ-
|
||||||
|
# L---JF-JLJ.||-FJLJJ7
|
||||||
|
# |F|F-JF---7F7-L7L|7|
|
||||||
|
# |FFJF7L7F-JF7|JL---7
|
||||||
|
# 7-L-JL7||F7|L7F-7F7|
|
||||||
|
# L.L7LFJ|||||FJL7||LJ
|
||||||
|
# L7JLJL-JLJLJL--JLJ.L
|
||||||
|
|
||||||
|
# Here are just the tiles that are enclosed by the loop marked with I:
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# In this last example, 10 tiles are enclosed by the loop.
|
||||||
|
|
||||||
|
# Figure out whether you have time to search for the nest by calculating the
|
||||||
|
# area within the loop. How many tiles are enclosed by the loop?
|
||||||
|
|
||||||
|
|
||||||
|
def part2(grid, seen):
|
||||||
|
# get rid of all garbage pipes
|
||||||
|
grid = [
|
||||||
|
"".join(ch if (r, c) in seen else "." for c, ch in enumerate(row))
|
||||||
|
for r, row in enumerate(grid)
|
||||||
|
]
|
||||||
|
|
||||||
|
inside = set()
|
||||||
|
for r, row in enumerate(grid):
|
||||||
|
# ignore pipes that face up or down on both ends because they don't matter
|
||||||
|
row = re.sub("L-*J|F-*7", lambda match: "." * len(match.group()), row)
|
||||||
|
within = False
|
||||||
|
# toggle on left
|
||||||
|
for c, ch in enumerate(row):
|
||||||
|
if ch in "|FL":
|
||||||
|
within = not within
|
||||||
|
if within:
|
||||||
|
inside.add((r, c))
|
||||||
|
|
||||||
|
total = len(inside - seen)
|
||||||
|
print(f"There are {total} tiles enclosed by the loop")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
part1()
|
g, sol1 = part1()
|
||||||
|
part2(g, sol1)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user