Skip to content

Commit

Permalink
Merge pull request #12 from Flashky/feature/day_16
Browse files Browse the repository at this point in the history
Feature/day 16
  • Loading branch information
Flashky authored Dec 23, 2024
2 parents e8a1d63 + 958b935 commit f601e3e
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 111 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
- [Day 13 - Claw Contraption](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day13)
- [Day 14 - Restroom Redoubt](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day14)
- [Day 15 - Warehouse Woes](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day15)
- [Day 16](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day16)
- [Day 16 - Reindeer Maze](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day16)
- [Day 17](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day17)
- [Day 18 - RAM Run](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day18)
- [Day 19 - Linen Layout](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day19)
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/adventofcode/flashk/common/Vector2.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ public static Vector2 transform(Vector2 start, Vector2 end) {

return new Vector2(x,y);
}


public static Vector2 multiply(Vector2 vector, int scalar) {
return new Vector2(vector.x * scalar, vector.y * scalar);
}

/**
* Substracts the right operand vector to the left operand vector, applying absolute value to the result.
*
Expand Down
184 changes: 98 additions & 86 deletions src/main/java/com/adventofcode/flashk/day16/ReindeerMaze.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import com.adventofcode.flashk.common.Vector2;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;

public class ReindeerMaze {
Expand Down Expand Up @@ -60,17 +63,13 @@ public long solveA2() {
return dijkstra(startTile, Vector2.right());
}

// TODO add also start direction
private long dijkstra(Tile start, Vector2 direction) {

reset();

// Initialize start tile
start.setScore(0);
start.setDirection(direction);
//if(start.getDirection() == null) {
// start.setDirection(Vector2.right());
//}

// Execute dijkstra
PriorityQueue<Tile> tiles = new PriorityQueue<>();
Expand Down Expand Up @@ -141,112 +140,126 @@ private long dijkstra(Tile start, Vector2 direction) {
- 11 steps - 11 steps - 11 steps
*/
public long solveB(){
// Idea:

// 1. Primero aplicamos un dijkstra normal para encontrar el camino más corto.
// 2, Metemos en una lista los tiles del camino más corto.

// Ahora recorremos cada tile del camino más corto
// 1. Calculamos los adyacentes de un tile.
// 2. Si la distancia del tile adyacente al inicio + la distancia del tile al final es igual que la distancia
// del camino más corto, entonces estamos ante un camino alternativo.

long score = dijkstra(startTile, Vector2.right());
paint();
// Parte 1, encontrar las intersecciones que hay en el camino más corto

Set<Tile> intersections = findPathIntersections();
//Set<Tile> intersections = findPathSections();

for(Tile intersection : intersections) {

// calcular dijkstra para adyacentes
Set<Tile> adjacents = getAdjacentsIncludingVisited(intersection);
// Add also direction

for(Tile adjacent : adjacents) {
//if(adjacent.isPath()) {
// continue;
//}
//Vector2 direction = Vector2.substract(intersection.getPosition(), adjacent.getPosition());
Vector2 direction = Vector2.substract(adjacent.getPosition(),intersection.getPosition());
dijkstra(adjacent, direction);
long adjacentScore = endTile.getScore() + startTile.getScore();
if(adjacentScore == score) {
fillPath(adjacent, endTile);
fillPath(adjacent, startTile);
paint();
System.out.println();

public long solveB2() {
long result = 0;
dijkstra(startTile, Vector2.right());
reverseDijkstra();
for(int row = 0; row < rows; row++) {
for(int col = 0; col < cols; col++) {
if(map[row][col].isPath()) {
result++;
}
}
}
// Pasada 2, buscamos caminos alternativos en cada una de las intersecciones.

startTile.setPath(true);
endTile.setPath(true);

paint();
return countPathTiles();
return result;
}

private long countPathTiles() {
long count = 0;
for(int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
if (map[row][col].isPath()) {
count++;
/// Executes a reverse dijkstra that starts at the end node and attempts to reach the start node.
///
/// This method must be executed after [dijkstra(Tile start, Vector2 direction)]()
private void reverseDijkstra() {

// Initialize start tile
endTile.setScoreToEnd(0);

Deque<Tile> tiles = new ArrayDeque<>();
tiles.add(endTile);

while(!tiles.isEmpty()) {

Tile tile = tiles.poll();
tile.setVisitedReverse(true);
tile.setPath(true);

// Calcular adyacentes.
Set<Tile> adjacentTiles = getAdjacentsReverse(tile);
for(Tile adjacentTile : adjacentTiles) {

// Tiles should be added to the queue only if they can meet the following formula:
// d(S,E) == estimated score
// estimated score = d(S,A,E) = d(S,A) + d(A,B) + d(B,E)

// Where:
// S: Starting tile
// E: Ending tile
// A: Adjacent tile
// B: Tile
// Where nodes have this order: S -> A -> B -> E

// Known parameters:
// d(S,E): endTile.getScore()
// d(S,A): adjacentTile.getScore()
// d(A,B): 1 if A and B directions are the same, 1001 if A and B directions is different.
// d(B,E): tile.getScoreToEnd()
//
// Also adjacent tile score must be modified by 1000 when taking corners.

Vector2 newDirection = Vector2.substract(adjacentTile.getPosition(), tile.getPosition());

// d(S,A)
long scoreAdjacentTile = calculateAdjacentTileScore(adjacentTile, newDirection);

// d(A,B)
long scoreAdjacentToTile = calculateAdjacentTileToTileScore(adjacentTile, tile, newDirection);

// d(B,E)
long scoreTileToEnd = tile.getScoreToEnd();

// estimated score = d(S,A) + d(A,B) + d(B,E)
long estimatedScore = scoreAdjacentTile + scoreAdjacentToTile + scoreTileToEnd;
if(estimatedScore == endTile.getScore()) {
// Adjacent tile has a path that is the same as the best score

// d(A,E) = d(A,B) + d(B,E)
adjacentTile.setScoreToEnd(scoreAdjacentToTile+scoreTileToEnd);
adjacentTile.setDirectionReverse(newDirection);

tiles.add(adjacentTile);

}

}
}
return count;
}
//paint();

private Set<Tile> findPathIntersections() {
Set<Tile> intersections = new HashSet<>();
}

Tile end = map[endPos.getY()][endPos.getX()];
Tile start = map[startPos.getY()][startPos.getX()];
private long calculateAdjacentTileScore(Tile adjacentTile, Vector2 newDirection) {

Tile current = end;
do {
current.setPath(true);
if(getAdjacentsIncludingVisited(current).size() > 2) {
intersections.add(current);
}
current = current.getParent();
} while(current != start);
long scoreAdjacentTile = adjacentTile.getScore();

start.setPath(true);
// If there is a corner, adjacent tile must modify its score
Vector2 reversedNewDirection = Vector2.multiply(newDirection,-1);
if(!reversedNewDirection.equals(adjacentTile.getDirection())) {
scoreAdjacentTile += 1000;
}

return intersections;
return scoreAdjacentTile;
}

private long calculateAdjacentTileToTileScore(Tile adjacentTile, Tile tile, Vector2 newDirection) {
long scoreAdjacentToTile;
if(tile == endTile) {
scoreAdjacentToTile = 1;
} else {
scoreAdjacentToTile = newDirection.equals(tile.getDirectionReverse()) ? 1 : 1001;
}

return scoreAdjacentToTile;
}

private void reset() {
for(int row = 0; row < rows; row++) {
for(int col = 0; col < cols; col++) {
map[row][col].setVisited(false);
map[row][col].setScore(Long.MAX_VALUE);
map[row][col].setParent(null);
//map[row][col].setPath(false);
}
}
}

private void fillPath(Tile start, Tile end) {

Tile current = end;
while(current != start) {
current = current.getParent();
current.setPath(true);
}

start.setPath(true);
end.setPath(true); // just in case

}

private Set<Tile> getAdjacents(Tile parent) {
Set<Tile> adjacents = new HashSet<>();
for(Vector2 dir : directions) {
Expand All @@ -257,15 +270,14 @@ private Set<Tile> getAdjacents(Tile parent) {
}
}
return adjacents;

}

private Set<Tile> getAdjacentsIncludingVisited(Tile parent) {
private Set<Tile> getAdjacentsReverse(Tile parent) {
Set<Tile> adjacents = new HashSet<>();
for(Vector2 dir : directions) {
Vector2 newPos = Vector2.transform(parent.getPosition(), dir);
Tile newTile = map[newPos.getY()][newPos.getX()];
if(!newTile.isWall()) {
if(!newTile.isWall() && !newTile.isVisitedReverse()) {
adjacents.add(newTile);
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/com/adventofcode/flashk/day16/Tile.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ public class Tile implements Comparable<Tile>{
private final Vector2 position;
private final char value;

// Dijkstra values
private long score = Long.MAX_VALUE;
private long partialScore;
private Tile parent;
private boolean visited = false;
private Vector2 direction;
private Tile parent;

// Reversed Dijkstra values
private long scoreToEnd = Long.MAX_VALUE;
private boolean visitedReverse = false;
private Vector2 directionReverse;

private boolean path = false;

public Tile(Vector2 position, char value) {
Expand Down
Loading

0 comments on commit f601e3e

Please sign in to comment.