1
1
package pl .apostaremczak .aoc ;
2
2
3
+ import pl .apostaremczak .aoc .util .CharacterMap2d ;
3
4
import pl .apostaremczak .aoc .util .Coord2D ;
4
- import pl .apostaremczak .aoc .util .Map2D ;
5
5
6
6
import java .util .*;
7
7
8
8
public class Day20 extends PuzzleSolution {
9
9
Coord2D StartPosition ;
10
10
Coord2D EndPosition ;
11
- Map <CheatPosition , Long > CheatedPathLengths = new HashMap <>();
12
11
Long ShortestLegalPath = Long .MAX_VALUE ;
13
- // Set<Coord2D> Walls;
14
- Map2D <Character > Maze ;
12
+ CharacterMap2d Maze ;
15
13
Long SavedTimeThreshold ;
16
- Set <CheatPosition > ExploredCheats = new HashSet <>();
14
+ Map <Coord2D , Long > DistancesFromStart ;
15
+ Map <Coord2D , Long > DistancesFromEnd ;
16
+
17
17
18
18
public Day20 (String inputFilename , Long savedTimeThreshold ) {
19
19
super (inputFilename );
20
- Maze = Map2D .fromStringInputLines (inputLines );
20
+ Maze = CharacterMap2d .fromStringInputLines (inputLines , '#' );
21
21
StartPosition = Maze .findFirst ('S' ).get ();
22
22
EndPosition = Maze .findFirst ('E' ).get ();
23
23
SavedTimeThreshold = savedTimeThreshold ;
24
- }
25
-
26
- @ Override
27
- public Long solvePart1 () {
28
- findRaceTrackLength (new LinkedList <>(), StartPosition , Optional .empty ());
29
- // How many cheats would save you at least 100 picoseconds?
30
- long result = 0 ;
31
- for (long cheatedPathLength : CheatedPathLengths .values ()) {
32
- if (ShortestLegalPath - cheatedPathLength >= SavedTimeThreshold ) {
33
- result ++;
34
- }
35
- }
36
- return result ;
37
- }
38
24
39
- @ Override
40
- public Long solvePart2 () {
41
- return 0L ;
25
+ DistancesFromStart = Maze . getDistancesFrom ( StartPosition );
26
+ DistancesFromEnd = Maze . getDistancesFrom ( EndPosition );
27
+ ShortestLegalPath = DistancesFromStart . get ( EndPosition ) ;
42
28
}
43
29
44
- private Boolean isWall (Coord2D position ) {
45
- return Maze .safeGetAt (position ).orElse ('X' ).equals ('#' );
46
- }
47
-
48
- // DFS for finding track lengths
49
- private void findRaceTrackLength (LinkedList <Coord2D > visited , Coord2D currentNode , Optional <CheatPosition > cheatPosition ) {
50
- visited .add (currentNode );
51
-
52
- if (currentNode .equals (EndPosition )) {
53
- long currentPathLength = visited .size ();
54
- if (cheatPosition .isEmpty ()) {
55
- if (currentPathLength < ShortestLegalPath ) {
56
- ShortestLegalPath = currentPathLength ;
57
- }
58
- } else {
59
- CheatedPathLengths .put (cheatPosition .get (), currentPathLength );
60
- }
61
- }
30
+ private Long countShortcuts (Integer maxShortcutLength ) {
31
+ long shorterPathCount = 0L ;
62
32
63
- for (Coord2D neighbor : currentNode .getStraightSurrounding ()) {
64
- if (!visited .contains (neighbor ) && Maze .isWithinBounds (neighbor )) {
65
- // Try cheating if it hasn't been done already and if the next tile after the wall is not a wall itself
66
- if (isWall (neighbor ) && cheatPosition .isEmpty ()) {
67
- Coord2D direction = neighbor .minus (currentNode );
68
- Coord2D afterWallNode = neighbor .add (direction );
69
- if (Maze .isWithinBounds (afterWallNode ) && !isWall (afterWallNode ) && !visited .contains (afterWallNode )) {
70
- LinkedList <Coord2D > cheatVisited = new LinkedList <>(visited );
71
- cheatVisited .add (neighbor );
72
- CheatPosition cheat = new CheatPosition (neighbor , afterWallNode );
73
- if (!ExploredCheats .contains (cheat )) {
74
- ExploredCheats .add (cheat );
75
- findRaceTrackLength (cheatVisited , afterWallNode , Optional .of (cheat ));
33
+ // Iterate over all non-wall points on the grid
34
+ for (int rowIdx = 0 ; rowIdx <= Maze .MAX_ROW_INDEX ; rowIdx ++) {
35
+ for (int colIdx = 0 ; colIdx <= Maze .MAX_COLUMN_INDEX ; colIdx ++) {
36
+ Coord2D currentPosition = new Coord2D (rowIdx , colIdx );
37
+ if (!Maze .isWall (currentPosition )) {
38
+ // Check all the neighbors and check if the tile after the neighbor is free
39
+ for (Coord2D teleport : Maze .getManhattanClosedBallPoints (currentPosition , maxShortcutLength )) {
40
+ if (!Maze .isWall (teleport )) {
41
+ // Teleporting from p to q through a cheat
42
+ // d(S, p) + d(p, q) + d(q, E)
43
+ long cheatedDistance = DistancesFromStart .get (currentPosition ) + currentPosition .manhattanDistanceFrom (teleport ) + DistancesFromEnd .get (teleport );
44
+ if (ShortestLegalPath - cheatedDistance >= SavedTimeThreshold ) {
45
+ shorterPathCount ++;
46
+ }
76
47
}
77
48
}
78
49
}
79
- // Turn and proceed normally
80
- if (!isWall (neighbor )) {
81
- findRaceTrackLength (new LinkedList <>(visited ), neighbor , cheatPosition );
82
- }
83
50
}
84
51
}
52
+ return shorterPathCount ;
53
+ }
54
+
55
+ @ Override
56
+ public Long solvePart1 () {
57
+ return countShortcuts (2 );
58
+ }
59
+
60
+ @ Override
61
+ public Long solvePart2 () {
62
+ return countShortcuts (20 );
85
63
}
86
64
87
65
public static void main (String [] args ) {
@@ -93,7 +71,3 @@ public static void main(String[] args) {
93
71
}
94
72
}
95
73
96
- record CheatPosition (Coord2D first , Coord2D second ) {
97
-
98
- }
99
-
0 commit comments