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
|
||||
# starting position?
|
||||
|
||||
import re
|
||||
|
||||
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):
|
||||
start = [pos for pos, c in grid.items() if c == "S"][0]
|
||||
queue, seen = [(0, start)], set()
|
||||
while queue:
|
||||
count, (x, y) = queue.pop(0)
|
||||
seen.add((x, y))
|
||||
for dx, dy in directions[grid[x, y]]:
|
||||
new_x, new_y = x + dx, y + dy
|
||||
if (new_x, new_y) not in grid or (new_x, new_y) in seen:
|
||||
continue
|
||||
if grid[new_x, new_y] not in connections[dx, dy]:
|
||||
continue
|
||||
queue.append((count + 1, (new_x, new_y)))
|
||||
return count, seen
|
||||
s_values = set(directions) - {"."}
|
||||
|
||||
# where is "S" located?
|
||||
ss = [
|
||||
(r, c)
|
||||
for r, row in enumerate(grid)
|
||||
for c, ch in enumerate(row)
|
||||
if ch == "S"
|
||||
]
|
||||
# must be unique
|
||||
assert len(ss) == 1
|
||||
sr, sc = ss[0]
|
||||
|
||||
# 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():
|
||||
N, S, W, E = (0, -1), (0, 1), (-1, 0), (1, 0)
|
||||
directions = {
|
||||
"S": [N, S, W, E],
|
||||
"|": [N, S],
|
||||
"-": [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 will traverse our pipe to find the path length divided by 2, because we
|
||||
# need to find the furthest point from the starting position
|
||||
prev_r = sr
|
||||
prev_c = sc
|
||||
|
||||
# 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")
|
||||
# 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__":
|
||||
part1()
|
||||
g, sol1 = part1()
|
||||
part2(g, sol1)
|
||||
|
Loading…
Reference in New Issue
Block a user