improve Day10 in python
This commit is contained in:
parent
a5dca38d0e
commit
1d298812da
101
Day10/README.md
101
Day10/README.md
@ -100,4 +100,103 @@ Here are the distances for each tile on that loop:
|
|||||||
14567
|
14567
|
||||||
23...
|
23...
|
||||||
```
|
```
|
||||||
Find the single giant loop starting at S. How many steps along the loop does it take to get from the starting position to the point farthest from the starting position?
|
Find the single giant loop starting at S. How many steps along the loop does it take to get from the starting position to the point farthest from the starting position?
|
||||||
|
|
||||||
|
# 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?
|
@ -1,114 +1,105 @@
|
|||||||
def parse_grid(file_path):
|
def parse_grid(file_path):
|
||||||
"""Parses the grid from a file and returns it as a 2D list."""
|
"""Parses the grid from a file and returns the graph and starting position."""
|
||||||
|
print(f"Parsing grid from file: {file_path}")
|
||||||
|
grid = {}
|
||||||
|
start = None
|
||||||
try:
|
try:
|
||||||
with open(file_path, "r") as file:
|
with open(file_path, 'r') as file:
|
||||||
grid = [list(line.strip()) for line in file]
|
for y, line in enumerate(file.read().splitlines()):
|
||||||
print(f"Grid parsed from {file_path}:")
|
for x, char in enumerate(line):
|
||||||
[print("".join(row)) for row in grid]
|
match char:
|
||||||
return grid
|
case "|":
|
||||||
|
grid[(y, x)] = {(y - 1, x), (y + 1, x)}
|
||||||
|
case "-":
|
||||||
|
grid[(y, x)] = {(y, x - 1), (y, x + 1)}
|
||||||
|
case "L":
|
||||||
|
grid[(y, x)] = {(y - 1, x), (y, x + 1)}
|
||||||
|
case "J":
|
||||||
|
grid[(y, x)] = {(y, x - 1), (y - 1, x)}
|
||||||
|
case "7":
|
||||||
|
grid[(y, x)] = {(y, x - 1), (y + 1, x)}
|
||||||
|
case "F":
|
||||||
|
grid[(y, x)] = {(y + 1, x), (y, x + 1)}
|
||||||
|
case "S":
|
||||||
|
start = (y, x)
|
||||||
|
grid[(y, x)] = {(y, x - 1), (y, x + 1), (y - 1, x), (y + 1, x)}
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if start is None:
|
||||||
|
raise ValueError("Start position 'S' not found in the grid.")
|
||||||
|
grid[start] = {dst for dst in grid[start] if start in grid.get(dst, set())}
|
||||||
|
return grid, start
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"File not found: {file_path}")
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading file {file_path}: {e}")
|
print(f"Error parsing grid: {str(e)}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def find_cycle(grid, start):
|
||||||
def create_graph(grid):
|
"""Finds all nodes in the cycle starting from the starting position."""
|
||||||
"""Creates a graph from the grid."""
|
print(f"Finding cycle from start position: {start}")
|
||||||
graph = {}
|
seen = {start}
|
||||||
rows, cols = len(grid), len(grid[0])
|
queue = [start]
|
||||||
for r in range(rows):
|
|
||||||
for c in range(cols):
|
|
||||||
if grid[r][c] in "|-LJ7FS":
|
|
||||||
graph[(r, c)] = get_neighbors(grid, r, c)
|
|
||||||
print("Graph created from grid:")
|
|
||||||
print(graph)
|
|
||||||
return graph
|
|
||||||
|
|
||||||
|
|
||||||
def get_neighbors(grid, r, c):
|
|
||||||
"""Finds the neighbors of a cell in the grid."""
|
|
||||||
neighbors = []
|
|
||||||
rows, cols = len(grid), len(grid[0])
|
|
||||||
|
|
||||||
# Directions: North, East, South, West
|
|
||||||
directions = [(r - 1, c), (r, c + 1), (r + 1, c), (r, c - 1)]
|
|
||||||
connected = {
|
|
||||||
"|": [0, 2],
|
|
||||||
"-": [1, 3],
|
|
||||||
"L": [0, 1],
|
|
||||||
"J": [0, 3],
|
|
||||||
"7": [2, 3],
|
|
||||||
"F": [1, 2],
|
|
||||||
"S": [0, 1, 2, 3], # 'S' connects in all directions for initial identification
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, (dr, dc) in enumerate(directions):
|
|
||||||
if 0 <= dr < rows and 0 <= dc < cols and grid[dr][dc] != ".":
|
|
||||||
neighbor_type = grid[dr][dc]
|
|
||||||
# Check if there is a valid connection
|
|
||||||
if neighbor_type in connected:
|
|
||||||
if (
|
|
||||||
i in connected[grid[r][c]] and (3 - i) in connected[neighbor_type]
|
|
||||||
): # Check reverse direction
|
|
||||||
neighbors.append((dr, dc))
|
|
||||||
|
|
||||||
print(f"Neighbors for ({r}, {c}): {neighbors}")
|
|
||||||
return neighbors
|
|
||||||
|
|
||||||
|
|
||||||
def bfs(graph, start):
|
|
||||||
"""Performs BFS on the graph and returns the maximum distance from the start."""
|
|
||||||
visited = set()
|
|
||||||
queue = [(start, 0)]
|
|
||||||
max_distance = 0
|
|
||||||
while queue:
|
while queue:
|
||||||
node, distance = queue.pop(0)
|
current = queue.pop(0)
|
||||||
if node != start or len(visited) == 0: # Allow revisiting start only initially
|
for dst in grid[current]:
|
||||||
visited.add(node)
|
if dst not in seen:
|
||||||
|
seen.add(dst)
|
||||||
|
queue.append(dst)
|
||||||
|
return seen
|
||||||
|
|
||||||
|
def bfs_distance(grid, start, target):
|
||||||
|
"""Performs BFS to find distance from start to target."""
|
||||||
|
#print(f"Calculating BFS distance from {start} to {target}")
|
||||||
|
queue = [(start, 0)]
|
||||||
|
visited = set()
|
||||||
|
while queue:
|
||||||
|
current, distance = queue.pop(0)
|
||||||
|
if current == target:
|
||||||
|
return distance
|
||||||
|
visited.add(current)
|
||||||
|
for neighbor in grid[current]:
|
||||||
|
if neighbor not in visited:
|
||||||
|
queue.append((neighbor, distance + 1))
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def find_longest_distance(grid, cycle, start):
|
||||||
|
"""Finds the longest distance from the start in the cycle."""
|
||||||
|
print("Finding longest distance from start in the cycle.")
|
||||||
|
max_distance = 0
|
||||||
|
for node in cycle:
|
||||||
|
if node != start:
|
||||||
|
distance = bfs_distance(grid, start, node)
|
||||||
max_distance = max(max_distance, distance)
|
max_distance = max(max_distance, distance)
|
||||||
print(f"Visited node: {node}, Distance: {distance}")
|
|
||||||
for neighbor in graph[node]:
|
|
||||||
if neighbor not in visited or (neighbor == start and len(visited) > 1):
|
|
||||||
queue.append((neighbor, distance + 1))
|
|
||||||
print(f"Maximum distance from start: {max_distance}")
|
|
||||||
return max_distance
|
return max_distance
|
||||||
|
|
||||||
|
def run(file_path):
|
||||||
|
"""Runs the entire algorithm for the given file path."""
|
||||||
|
print(f"Running algorithm for file: {file_path}")
|
||||||
|
grid, start = parse_grid(file_path)
|
||||||
|
cycle = find_cycle(grid, start)
|
||||||
|
longest_distance = find_longest_distance(grid, cycle, start)
|
||||||
|
print(f"Longest distance: {longest_distance}")
|
||||||
|
return longest_distance
|
||||||
|
|
||||||
def find_start(grid):
|
def test():
|
||||||
"""Finds the starting position 'S' in the grid."""
|
"""Test function to validate the algorithm with a test file."""
|
||||||
for r, row in enumerate(grid):
|
print("Starting test...")
|
||||||
for c, cell in enumerate(row):
|
expected_result = 8 # Expected result for the test file
|
||||||
if cell == "S":
|
result = run("../test.txt")
|
||||||
print(f"Starting position found at: ({r}, {c})")
|
assert result == expected_result, f"Test failed: Expected {expected_result}, got {result}"
|
||||||
return r, c
|
print("Test passed successfully.")
|
||||||
raise ValueError("Starting position 'S' not found in the grid")
|
|
||||||
|
|
||||||
|
|
||||||
def run_test(file_path):
|
|
||||||
"""Runs the algorithm on a test file and asserts the result."""
|
|
||||||
print(f"Running test with file: {file_path}")
|
|
||||||
grid = parse_grid(file_path)
|
|
||||||
graph = create_graph(grid)
|
|
||||||
start = find_start(grid)
|
|
||||||
max_distance = bfs(graph, start)
|
|
||||||
print(f"Max distance for test: {max_distance}")
|
|
||||||
return max_distance
|
|
||||||
|
|
||||||
|
|
||||||
def main(file_path):
|
|
||||||
"""Main function to run the algorithm on the input file."""
|
|
||||||
print(f"Running main algorithm with file: {file_path}")
|
|
||||||
grid = parse_grid(file_path)
|
|
||||||
graph = create_graph(grid)
|
|
||||||
start = find_start(grid)
|
|
||||||
max_distance = bfs(graph, start)
|
|
||||||
print(f"Max distance for input: {max_distance}")
|
|
||||||
return max_distance
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function to run the test and then the algorithm for the input file."""
|
||||||
|
try:
|
||||||
|
test()
|
||||||
|
final_result = run("../input.txt")
|
||||||
|
print(f"Final Result: {final_result}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred: {str(e)}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_result = run_test("../test.txt")
|
main()
|
||||||
assert test_result == 8, f"Test failed: expected 8, got {test_result}"
|
|
||||||
print(f"Test passed with {test_result}")
|
|
||||||
|
|
||||||
input_result = main("../input.txt")
|
|
||||||
print(f"Result for input file: {input_result}")
|
|
||||||
|
66
Day10/python/solution2.py
Normal file
66
Day10/python/solution2.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
def parse_grid(data):
|
||||||
|
grid = {}
|
||||||
|
for y, line in enumerate(data):
|
||||||
|
for x, char in enumerate(line):
|
||||||
|
match char:
|
||||||
|
case "|":
|
||||||
|
grid[(y, x)] = {(y - 1, x), (y + 1, x)}
|
||||||
|
case "-":
|
||||||
|
grid[(y, x)] = {(y, x - 1), (y, x + 1)}
|
||||||
|
case "L":
|
||||||
|
grid[(y, x)] = {(y - 1, x), (y, x + 1)}
|
||||||
|
case "J":
|
||||||
|
grid[(y, x)] = {(y, x - 1), (y - 1, x)}
|
||||||
|
case "7":
|
||||||
|
grid[(y, x)] = {(y, x - 1), (y + 1, x)}
|
||||||
|
case "F":
|
||||||
|
grid[(y, x)] = {(y + 1, x), (y, x + 1)}
|
||||||
|
case "S":
|
||||||
|
start = (y, x)
|
||||||
|
grid[(y, x)] = {
|
||||||
|
(y, x - 1),
|
||||||
|
(y, x + 1),
|
||||||
|
(y - 1, x),
|
||||||
|
(y + 1, x),
|
||||||
|
}
|
||||||
|
case _:
|
||||||
|
pass
|
||||||
|
|
||||||
|
grid[start] = {dst for dst in grid[start] if start in grid.get(dst, set())}
|
||||||
|
return grid, start
|
||||||
|
|
||||||
|
|
||||||
|
def find_cycle(grid, start):
|
||||||
|
seen = {start}
|
||||||
|
queue = [start]
|
||||||
|
while queue:
|
||||||
|
current = queue.pop(0)
|
||||||
|
for dst in grid[current]:
|
||||||
|
if dst not in seen:
|
||||||
|
seen.add(dst)
|
||||||
|
queue.append(dst)
|
||||||
|
|
||||||
|
return seen
|
||||||
|
|
||||||
|
|
||||||
|
def find_inside(grid, cycle):
|
||||||
|
max_y = max(y for y, _ in grid.keys())
|
||||||
|
max_x = max(x for _, x in grid.keys())
|
||||||
|
|
||||||
|
inside = set()
|
||||||
|
for y in range(max_y):
|
||||||
|
pipe = 0
|
||||||
|
for x in range(max_x):
|
||||||
|
pos = (y, x)
|
||||||
|
if pos in cycle:
|
||||||
|
if (y - 1, x) in grid[pos]:
|
||||||
|
pipe += 1
|
||||||
|
elif pipe % 2 == 1:
|
||||||
|
inside.add(pos)
|
||||||
|
|
||||||
|
return len(inside)
|
||||||
|
|
||||||
|
|
||||||
|
input_grid, input_start = parse_grid(open("../input.txt").read().splitlines())
|
||||||
|
print(len(find_cycle(input_grid, input_start)) // 2)
|
||||||
|
print(find_inside(input_grid, find_cycle(input_grid, input_start)))
|
9
Day10/test2.txt
Normal file
9
Day10/test2.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
...........
|
||||||
|
.S-------7.
|
||||||
|
.|F-----7|.
|
||||||
|
.||.....||.
|
||||||
|
.||.....||.
|
||||||
|
.|L-7.F-J|.
|
||||||
|
.|..|.|..|.
|
||||||
|
.L--J.L--J.
|
||||||
|
...........
|
Loading…
Reference in New Issue
Block a user