Skip to content

Commit 400aa60

Browse files
committed
Day 16, part 1
1 parent 6737b6e commit 400aa60

File tree

2 files changed

+241
-0
lines changed

2 files changed

+241
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package pl.apostaremczak.aoc;
2+
3+
import pl.apostaremczak.aoc.util.Coord2D;
4+
import pl.apostaremczak.aoc.util.DirectionSupport;
5+
import pl.apostaremczak.aoc.util.Map2D;
6+
7+
import java.util.*;
8+
9+
public class Day16 extends PuzzleSolution {
10+
public Day16(String inputFilename) {
11+
super(inputFilename);
12+
}
13+
14+
@Override
15+
public Long solvePart1() {
16+
Maze maze = new Maze(inputLines);
17+
return maze.getBestPathScore();
18+
}
19+
20+
@Override
21+
public Long solvePart2() {
22+
Maze maze = new Maze(inputLines);
23+
return maze.countTilesOnBestPaths();
24+
}
25+
26+
public static void main(String[] args) {
27+
Day16 day16 = new Day16("src/main/resources/16.txt");
28+
Long part1Solution = day16.solvePart1();
29+
System.out.println("Part 1: " + part1Solution);
30+
Long part2Solution = day16.solvePart2();
31+
System.out.println("Part 2: " + part2Solution);
32+
}
33+
}
34+
35+
class MazeTile implements Comparable<MazeTile>, DirectionSupport {
36+
private Coord2D position;
37+
38+
private LinkedList<StackEntry> shortestPath = new LinkedList<>();
39+
40+
public final Set<MazeTile> allPotentialPathEntries = new HashSet<>();
41+
42+
private Integer distance = Integer.MAX_VALUE;
43+
44+
public Integer getDistance() {
45+
return distance;
46+
}
47+
48+
public void setDistance(Integer newDistance) {
49+
this.distance = newDistance;
50+
}
51+
52+
public Coord2D getPosition() {
53+
return this.position;
54+
}
55+
56+
public LinkedList<StackEntry> getShortestPath() {
57+
return this.shortestPath;
58+
}
59+
60+
public void setShortestPath(LinkedList<StackEntry> shortestPath) {
61+
this.shortestPath = shortestPath;
62+
}
63+
64+
public static MazeTile fromPosition(Coord2D tilePosition) {
65+
MazeTile tile = new MazeTile();
66+
tile.position = tilePosition;
67+
return tile;
68+
}
69+
70+
public Long countPotentialPaths() {
71+
return (long) allPotentialPathEntries.size();
72+
}
73+
74+
public String printShortestPath() {
75+
StringBuilder representation = new StringBuilder();
76+
for (StackEntry entry : this.getShortestPath()) {
77+
Coord2D position = entry.tile().getPosition();
78+
String positionRepresentation = "[" + position.row() + ", " + position.column() + "]";
79+
Coord2D direction = entry.direction();
80+
representation.append(DirectionSupport.directionToString(direction));
81+
representation.append(positionRepresentation);
82+
representation.append(" ");
83+
}
84+
85+
return representation.toString();
86+
}
87+
88+
@Override
89+
public int compareTo(MazeTile o) {
90+
return Integer.compare(this.getDistance(), o.getDistance());
91+
}
92+
}
93+
94+
class Maze implements DirectionSupport {
95+
private final Coord2D StartTilePosition;
96+
private final Coord2D EndTilePosition;
97+
private final Coord2D StartDirection;
98+
private final Map<Coord2D, MazeTile> Tiles;
99+
private final Integer ShortestDistance;
100+
101+
public Maze(String[] inputLines) {
102+
Map2D<Character> mazeAsArray = Map2D.fromStringInputLines(inputLines);
103+
Coord2D startTilePosition = null;
104+
Coord2D endTilePosition = null;
105+
Map<Coord2D, MazeTile> tiles = new HashMap<>();
106+
107+
for (int rowIdx = 0; rowIdx <= mazeAsArray.MAX_ROW_INDEX; rowIdx++) {
108+
for (int colIdx = 0; colIdx <= mazeAsArray.MAX_COLUMN_INDEX; colIdx++) {
109+
Coord2D position = new Coord2D(rowIdx, colIdx);
110+
Character value = mazeAsArray.getAt(position);
111+
switch (value) {
112+
case 'S':
113+
startTilePosition = position;
114+
tiles.put(position, MazeTile.fromPosition(position));
115+
break;
116+
case 'E':
117+
endTilePosition = position;
118+
tiles.put(position, MazeTile.fromPosition(position));
119+
break;
120+
case '.':
121+
tiles.put(position, MazeTile.fromPosition(position));
122+
break;
123+
}
124+
}
125+
}
126+
127+
assert startTilePosition != null : "Could not find the start tile";
128+
assert endTilePosition != null : "Could not find the end tile";
129+
this.StartTilePosition = startTilePosition;
130+
this.EndTilePosition = endTilePosition;
131+
// The Reindeer start on the Start Tile (marked S) facing East
132+
this.StartDirection = RIGHT;
133+
this.Tiles = tiles;
134+
this.dijkstra();
135+
this.ShortestDistance = this.Tiles.get(this.EndTilePosition).getDistance();
136+
}
137+
138+
public Long getBestPathScore() {
139+
return (long) this.ShortestDistance;
140+
}
141+
142+
public Long countTilesOnBestPaths() {
143+
return this.Tiles.get(this.EndTilePosition).countPotentialPaths();
144+
}
145+
146+
private void dijkstra() {
147+
MazeTile startTile = this.Tiles.get(this.StartTilePosition);
148+
startTile.setDistance(0);
149+
startTile.allPotentialPathEntries.add(startTile);
150+
151+
Set<StackEntry> settledTiles = new HashSet<>();
152+
PriorityQueue<StackEntry> unsettledTiles = new PriorityQueue<>();
153+
154+
unsettledTiles.add(new StackEntry(startTile, StartDirection));
155+
156+
while (!unsettledTiles.isEmpty()) {
157+
StackEntry currentTile = unsettledTiles.poll();
158+
unsettledTiles.remove(currentTile);
159+
// Get all the possible next steps
160+
// Try continuing in the same direction or rotating 90 degrees clockwise and counterclockwise
161+
Coord2D currentDirection = currentTile.direction();
162+
Coord2D[] potentialDirections = {currentDirection, currentDirection.rotateRightAngle(90), currentDirection.rotateRightAngle(-90)};
163+
164+
Coord2D currentPosition = currentTile.tile().getPosition();
165+
for (int i = 0; i < 3; i++) {
166+
Coord2D direction = potentialDirections[i];
167+
168+
Coord2D nextTilePosition = currentPosition.add(direction);
169+
if (this.Tiles.containsKey(nextTilePosition)) {
170+
MazeTile nextTile = this.Tiles.get(nextTilePosition);
171+
StackEntry nextTileEntry = new StackEntry(nextTile, direction);
172+
173+
if (!settledTiles.contains(nextTileEntry)) {
174+
unsettledTiles.add(nextTileEntry);
175+
calculateMinimumDistance(nextTile, currentTile.tile(), direction);
176+
}
177+
}
178+
}
179+
settledTiles.add(currentTile);
180+
}
181+
}
182+
183+
private void calculateMinimumDistance(MazeTile evaluationTile, MazeTile sourceTile, Coord2D direction) {
184+
int sourceDistance = sourceTile.getDistance();
185+
int newEdgeWeight = sourceDistance + 1;
186+
LinkedList<StackEntry> shortestPath = new LinkedList<>(sourceTile.getShortestPath());
187+
Coord2D lastDirection = !shortestPath.isEmpty() ? shortestPath.getLast().direction() : StartDirection;
188+
189+
if (!direction.equals(lastDirection)) {
190+
newEdgeWeight = sourceDistance + 1001;
191+
}
192+
193+
if (newEdgeWeight <= evaluationTile.getDistance()) {
194+
evaluationTile.setDistance(newEdgeWeight);
195+
shortestPath.add(new StackEntry(sourceTile, direction));
196+
evaluationTile.setShortestPath(shortestPath);
197+
}
198+
}
199+
}
200+
201+
record StackEntry(MazeTile tile, Coord2D direction) implements Comparable<StackEntry> {
202+
@Override
203+
public int compareTo(StackEntry o) {
204+
return this.tile.compareTo(o.tile);
205+
}
206+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package pl.apostaremczak.aoc;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
7+
public class Day16Tests {
8+
private final Day16 day16 = new Day16("src/test/resources/16.txt");
9+
10+
@Test
11+
public void testSolvePart1() {
12+
Long result = day16.solvePart1();
13+
assertEquals(7036L, result);
14+
}
15+
16+
@Test
17+
public void testSolvePart1SecondExample() {
18+
Day16 day = new Day16("src/test/resources/16_2.txt");
19+
Long result = day.solvePart1();
20+
assertEquals(11048L, result);
21+
}
22+
23+
@Test
24+
public void testSolvePart2() {
25+
Long result = day16.solvePart2();
26+
assertEquals(45L, result);
27+
}
28+
29+
@Test
30+
public void testSolvePart2SecondExample() {
31+
Day16 day = new Day16("src/test/resources/16_2.txt");
32+
Long result = day.solvePart2();
33+
assertEquals(64L, result);
34+
}
35+
}

0 commit comments

Comments
 (0)