Skip to content

Commit

Permalink
Added solution for 2023/Day 23
Browse files Browse the repository at this point in the history
  • Loading branch information
npanuhin authored and github-actions[bot] committed Dec 26, 2023
1 parent 32c799f commit 001b444
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 6 deletions.
4 changes: 2 additions & 2 deletions .github/website/2023/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ <h1>Advent of Code 2023</h1>
</tr>
<tr>
<td><a href="https://github.com/npanuhin/Advent-of-Code/tree/master/2023/Day%2023">Day 23</a></td>
<td></td>
<td></td>
<td><a href="https://github.com/npanuhin/Advent-of-Code/tree/master/2023/Day%2023/part1.py"></a></td>
<td><a href="https://github.com/npanuhin/Advent-of-Code/tree/master/2023/Day%2023/part2.py"></a></td>
</tr>
<tr>
<td><a href="https://github.com/npanuhin/Advent-of-Code/tree/master/2023/Day%2024">Day 24</a></td>
Expand Down
141 changes: 141 additions & 0 deletions 2023/Day 23/input.txt

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions 2023/Day 23/part1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from sys import setrecursionlimit


setrecursionlimit(10 ** 6)

DIRECTIONS = [(0, 1), (1, 0), (0, -1), (-1, 0)]


with open('input.txt') as file:
terrain = list(filter(None, map(str.strip, file)))

width, height = len(terrain[0]), len(terrain)

start_y, start_x = 0, terrain[0].index('.')


def find_logest_path(x: int, y: int, visited: list[list[bool]]) -> int:
if y == height - 1:
return 0

length = float('-inf')

visited[y][x] = True

for dy, dx in DIRECTIONS:
if not (0 <= x + dx < width and 0 <= y + dy < height) or visited[y + dy][x + dx]:
continue

if any((
terrain[y + dy][x + dx] == '.',
terrain[y + dy][x + dx] == '>' and dx == 1,
terrain[y + dy][x + dx] == '<' and dx == -1,
terrain[y + dy][x + dx] == '^' and dy == -1,
terrain[y + dy][x + dx] == 'v' and dy == 1
)):
length = max(length, find_logest_path(x + dx, y + dy, visited))

visited[y][x] = False

return length + 1


visited = [[False] * width for _ in range(height)]
print(find_logest_path(start_x, start_y, visited))
68 changes: 68 additions & 0 deletions 2023/Day 23/part2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from collections import defaultdict


DIRECTIONS = [(0, 1), (1, 0), (0, -1), (-1, 0)]


with open('input.txt') as file:
terrain = list(filter(None, map(str.strip, file)))

width, height = len(terrain[0]), len(terrain)

start_y, start_x = 0, terrain[0].index('.')
end_y, end_x = height - 1, terrain[height - 1].index('.')


def get_neighbours(x: int, y: int) -> list[tuple[int, int]]:
yield from (
(x + dx, y + dy)
for dy, dx in DIRECTIONS
if 0 <= x + dx < width and 0 <= y + dy < height
)


def find_intersections(x, y, prev=None, distance=0):
if prev and sum(terrain[test_y][test_x] != '#' for test_x, test_y in get_neighbours(x, y)) > 2:
yield ((x, y), distance)
return

for new_x, new_y in get_neighbours(x, y):
if (new_x, new_y) == prev:
continue

if terrain[new_y][new_x] != '#':
yield from find_intersections(new_x, new_y, (x, y), distance + 1)


first_intersection, before_first_distance = next(find_intersections(start_x, start_y))
last_intersection, after_last_distance = next(find_intersections(end_x, end_y))

intesections_graph = defaultdict(dict)
todo = [first_intersection]
while todo:
x, y = todo.pop()
intesections_graph[y][x] = list(find_intersections(x, y))
todo.extend(
(x, y)
for (x, y), _ in intesections_graph[y][x]
if x not in intesections_graph.get(y, [])
)


def find_logest_path(x: int, y: int, visited: list[list[bool]]) -> int:
if (x, y) == last_intersection:
return 0

length = float('-inf')
visited[y][x] = True

for (new_x, new_y), distance in intesections_graph[y][x]:
if not visited[new_y][new_x]:
length = max(length, find_logest_path(new_x, new_y, visited) + distance)

visited[y][x] = False
return length


visited = [[False] * width for _ in range(height)]
print(before_first_distance + find_logest_path(*first_intersection, visited) + after_last_distance)
102 changes: 102 additions & 0 deletions 2023/Day 23/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from collections import defaultdict
from sys import setrecursionlimit


setrecursionlimit(10 ** 6)

DIRECTIONS = [(0, 1), (1, 0), (0, -1), (-1, 0)]


def get_neighbours(x: int, y: int) -> list[tuple[int, int]]:
yield from (
(x + dx, y + dy)
for dy, dx in DIRECTIONS
if 0 <= x + dx < width and 0 <= y + dy < height
)


def part1(terrain):
def find_logest_path(x: int, y: int, visited: list[list[bool]]) -> int:
if y == height - 1:
return 0

length = float('-inf')

visited[y][x] = True

for dy, dx in DIRECTIONS:
if not (0 <= x + dx < width and 0 <= y + dy < height) or visited[y + dy][x + dx]:
continue

if any((
terrain[y + dy][x + dx] == '.',
terrain[y + dy][x + dx] == '>' and dx == 1,
terrain[y + dy][x + dx] == '<' and dx == -1,
terrain[y + dy][x + dx] == '^' and dy == -1,
terrain[y + dy][x + dx] == 'v' and dy == 1
)):
length = max(length, find_logest_path(x + dx, y + dy, visited))

visited[y][x] = False

return length + 1

visited = [[False] * width for _ in range(height)]
return find_logest_path(start_x, start_y, visited)


def part2(terrain):
def find_intersections(x, y, prev=None, distance=0):
if prev and sum(terrain[test_y][test_x] != '#' for test_x, test_y in get_neighbours(x, y)) > 2:
yield ((x, y), distance)
return

for new_x, new_y in get_neighbours(x, y):
if (new_x, new_y) == prev:
continue

if terrain[new_y][new_x] != '#':
yield from find_intersections(new_x, new_y, (x, y), distance + 1)

first_intersection, before_first_distance = next(find_intersections(start_x, start_y))
last_intersection, after_last_distance = next(find_intersections(end_x, end_y))

intesections_graph = defaultdict(dict)
todo = [first_intersection]
while todo:
x, y = todo.pop()
intesections_graph[y][x] = list(find_intersections(x, y))
todo.extend(
(x, y)
for (x, y), _ in intesections_graph[y][x]
if x not in intesections_graph.get(y, [])
)

def find_logest_path(x: int, y: int, visited: list[list[bool]]) -> int:
if (x, y) == last_intersection:
return 0

length = float('-inf')
visited[y][x] = True

for (new_x, new_y), distance in intesections_graph[y][x]:
if not visited[new_y][new_x]:
length = max(length, find_logest_path(new_x, new_y, visited) + distance)

visited[y][x] = False
return length

visited = [[False] * width for _ in range(height)]
return before_first_distance + find_logest_path(*first_intersection, visited) + after_last_distance


with open('input.txt') as file:
terrain = list(filter(None, map(str.strip, file)))

width, height = len(terrain[0]), len(terrain)

start_y, start_x = 0, terrain[0].index('.')
end_y, end_x = height - 1, terrain[height - 1].index('.')

print(part1(terrain))
print(part2(terrain))
6 changes: 3 additions & 3 deletions 2023/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,16 @@
</tr>
<tr>
<td>Day 23: A Long Walk</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"><a href="Day%2023/part1.py">⭐</a></td>
<td align="center"><a href="Day%2023/part2.py">⭐</a></td>
</tr>
<tr>
<td>Day 24: Never Tell Me The Odds</td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td>Day 25</td>
<td>Day 25: Snowverload</td>
<td align="center" colspan="2"></td>
</tr>
</table>
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ I try to optimize each solution as much as possible, so although they are writte
<td>Day 23</td>
<td align="center"><a href="2020/Day%2023/part1.py">⭐</a><a href="2020/Day%2023/part2.py">⭐</a></td>
<td align="center"><a href="2021/Day%2023/part1.py">⭐</a><a href="2021/Day%2023/part2.py">⭐</a></td>
<td align="center"></td>
<td align="center"><a href="2023/Day%2023/part1.py">⭐</a><a href="2023/Day%2023/part2.py">⭐</a></td>
</tr>
<tr>
<td>Day 24</td>
Expand Down

0 comments on commit 001b444

Please sign in to comment.