Skip to content

Commit ea04793

Browse files
committed
[2024/20] p1 & p2 solved
1 parent 61ea803 commit ea04793

File tree

4 files changed

+118
-2
lines changed

4 files changed

+118
-2
lines changed

.aoc_tiles/tiles/2024/20.png

5.7 KB
Loading

2024/20/example

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
###############
2+
#...#...#.....#
3+
#.#.#.#.#.###.#
4+
#S#...#.#.#...#
5+
#######.#.#.###
6+
#######.#.#...#
7+
#######.#.###.#
8+
###..E#...#...#
9+
###.#######.###
10+
#...###...#...#
11+
#.#####.#.###.#
12+
#.#...#.#.#...#
13+
#.#.#.#.#.#.###
14+
#...#...#...###
15+
###############

2024/20/script.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from GhostyUtils import aoc
2+
from GhostyUtils.grid import Grid
3+
from GhostyUtils.vec2 import Vec2, manhattan_distance as mh_dist
4+
from GhostyUtils.pathfinding import bfs
5+
from collections import defaultdict
6+
from functools import partial
7+
8+
9+
aoc.argparser.add_argument("-s", "--save", type=int, default=100, help="picoseconds to save")
10+
11+
12+
# non-walls 1 space UDLR from pos
13+
def neighbours(pos: tuple, grid: Grid) -> list[tuple]:
14+
return [n for n in grid.neighbours(pos, diagonal=False) if grid[n] != '#']
15+
16+
17+
# non-walls behind walls, 2 spaces UDLR from pos
18+
def cheat(pos: tuple, grid: Grid) -> list[Vec2]:
19+
pos = Vec2(pos)
20+
21+
def end_pos(pos, n):
22+
return pos+(n-pos)*2
23+
24+
return [end_pos(pos, n) for n in grid.neighbours(pos, diagonal=False)
25+
if grid[n] == '#' and grid.in_bounds(end_pos(pos, n)) and grid[end_pos(pos, n)] != '#']
26+
27+
28+
def cheat_savings(saves: dict[int], minimum: int):
29+
for saving, cheats in sorted(list(saves.items())):
30+
if saving < minimum:
31+
continue
32+
print(f"There are {cheats} cheats that save {saving} picoseconds.")
33+
34+
35+
def main():
36+
racetrack = Grid(aoc.read_lines())
37+
start = racetrack.find('S')
38+
end = racetrack.find('E')
39+
target = aoc.args.save
40+
41+
neighbours_func = partial(neighbours, grid=racetrack)
42+
clean_path = next(bfs(start, end, neighbours=neighbours_func))
43+
clean_time = len(clean_path)
44+
# map clean path positions to their index in the list, for faster lookup
45+
clean_path_index = {pos: i for i, pos in enumerate(clean_path)}
46+
47+
# print the clean path
48+
if aoc.args.progress or aoc.args.verbose:
49+
overlays = [
50+
{path: '.' for path in clean_path},
51+
{start: 'S', end: 'E'},
52+
]
53+
print(racetrack.render_with_overlays(overlays))
54+
print(f"clean time: {clean_time} picoseconds")
55+
56+
saves = defaultdict(int)
57+
cheats = {}
58+
for i, pos in enumerate(clean_path[:-1]):
59+
for c in cheat(pos, racetrack):
60+
# skip cheats that take us backwards
61+
if clean_path_index[c.as_tuple()] < i:
62+
continue
63+
64+
# calculate the time saved
65+
saved = -(i - clean_path_index[c.as_tuple()] + 2)
66+
saves[saved] += 1
67+
cheats[(pos, c.as_tuple())] = saved
68+
69+
# draw the cheats on the map
70+
if aoc.args.verbose:
71+
overlays.append({(pos+(c-pos)/2).as_tuple(): '1',
72+
c.as_tuple(): '2'})
73+
print(racetrack.render_with_overlays(overlays))
74+
print(f"^ saves {saved} picoseconds")
75+
overlays.pop()
76+
77+
if aoc.args.progress or aoc.args.verbose:
78+
cheat_savings(saves, target)
79+
print(f"p1: {sum(times for saved, times in saves.items() if saved >= 100)}")
80+
81+
saves = defaultdict(int)
82+
for i, pos in enumerate(clean_path[:-target]):
83+
# loop over path positions that are over 100 positions forward along the path and under
84+
# 20 manhattan distance away from the current position
85+
for candidate, md in filter(lambda md: md[1] <= 20,
86+
((other, mh_dist(pos, other))
87+
for other in clean_path[i+target:])):
88+
# calculate the time saved
89+
saved = -(i - clean_path_index[candidate] + md)
90+
saves[saved] += 1
91+
92+
if aoc.args.progress or aoc.args.verbose:
93+
cheat_savings(saves, target)
94+
print(f"p2: {sum(times for saved, times in saves.items() if saved >= target)}")
95+
96+
97+
if __name__ == "__main__":
98+
main()

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ My solutions to the yearly Advents of Code
33

44
<!-- AOC TILES BEGIN -->
55
<h1 align="center">
6-
Advent of Code - 197/488
6+
Advent of Code - 199/490
77
</h1>
88
<h1 align="center">
9-
2024 - 36 ⭐ - Python
9+
2024 - 38 ⭐ - Python
1010
</h1>
1111
<a href="2024/1/script.py">
1212
<img src=".aoc_tiles/tiles/2024/01.png" width="161px">
@@ -65,6 +65,9 @@ My solutions to the yearly Advents of Code
6565
<a href="2024/19/script.py">
6666
<img src=".aoc_tiles/tiles/2024/19.png" width="161px">
6767
</a>
68+
<a href="2024/20/script.py">
69+
<img src=".aoc_tiles/tiles/2024/20.png" width="161px">
70+
</a>
6871
<h1 align="center">
6972
2023 - 47 ⭐ - Python
7073
</h1>

0 commit comments

Comments
 (0)