Skip to content

Commit

Permalink
Day 20
Browse files Browse the repository at this point in the history
  • Loading branch information
bqcuong committed Dec 21, 2024
1 parent 4982e76 commit 0a451d7
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This repository contains my Java solutions for the [Advent of Code](https://adve
| 17 | [Chronospatial Computer](https://adventofcode.com/2024/day/17) | ⭐⭐ | [Code](src/main/java/net/bqc/aoc/year2024/Day17.java), [Test](src/test/java/net/bqc/aoc/year2024/Day17Test.java) |
| 18 | [RAM Run](https://adventofcode.com/2024/day/18) | ⭐⭐ | [Code](src/main/java/net/bqc/aoc/year2024/Day18.java), [Test](src/test/java/net/bqc/aoc/year2024/Day18Test.java) |
| 19 | [Linen Layout](https://adventofcode.com/2024/day/19) | ⭐⭐ | [Code](src/main/java/net/bqc/aoc/year2024/Day19.java), [Test](src/test/java/net/bqc/aoc/year2024/Day19Test.java) |
| 20 | [](https://adventofcode.com/2024/day/20) | | [Code](src/main/java/net/bqc/aoc/year2024/Day20.java), [Test](src/test/java/net/bqc/aoc/year2024/Day20Test.java) |
| 20 | [Race Condition](https://adventofcode.com/2024/day/20) | ⭐⭐ | [Code](src/main/java/net/bqc/aoc/year2024/Day20.java), [Test](src/test/java/net/bqc/aoc/year2024/Day20Test.java) |
| 21 | [](https://adventofcode.com/2024/day/21) | | [Code](src/main/java/net/bqc/aoc/year2024/Day21.java), [Test](src/test/java/net/bqc/aoc/year2024/Day21Test.java) |
| 22 | [](https://adventofcode.com/2024/day/22) | | [Code](src/main/java/net/bqc/aoc/year2024/Day22.java), [Test](src/test/java/net/bqc/aoc/year2024/Day22Test.java) |
| 23 | [](https://adventofcode.com/2024/day/23) | | [Code](src/main/java/net/bqc/aoc/year2024/Day23.java), [Test](src/test/java/net/bqc/aoc/year2024/Day23Test.java) |
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/net/bqc/aoc/utils/MathUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import java.util.List;

public class MathUtils {
public static int manhattanDistance(int x1, int y1, int x2, int y2) {
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
}

public static BigDecimal[] solveQuadraticEquation(BigDecimal a, BigDecimal b, BigDecimal c) {
BigDecimal discriminant = b.pow(2).subtract(new BigDecimal("4").multiply(a).multiply(c));

Expand Down
113 changes: 113 additions & 0 deletions src/main/java/net/bqc/aoc/year2024/Day20.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package net.bqc.aoc.year2024;

import net.bqc.aoc.Solution;
import net.bqc.aoc.utils.Array2DUtils;
import net.bqc.aoc.utils.MathUtils;
import net.bqc.aoc.utils.Pos;

import java.util.*;

public class Day20 extends Solution<Long> {

private enum Direction {
NORTH("^", -1, 0), EAST(">", 0, 1),
SOUTH("v", 1, 0), WEST("<", 0, -1);

final String symbol;
final int dx, dy;

Direction(String symbol, int dx, int dy) {
this.symbol = symbol;
this.dx = dx;
this.dy = dy;
}

public static Direction[] nextDirection(Direction d) {
if (d == null) return new Direction[]{NORTH, EAST, SOUTH, WEST};
return switch (d) {
case NORTH -> new Direction[]{NORTH, EAST, WEST};
case SOUTH -> new Direction[]{SOUTH, EAST, WEST};
case EAST -> new Direction[]{NORTH, SOUTH, EAST};
case WEST -> new Direction[]{NORTH, SOUTH, WEST};
};
}
}

private static class PathNode {
Pos pos;
long cost;
Direction direction;
PathNode prev;

public PathNode(Pos pos, long cost, Direction direction) {
this.pos = pos;
this.cost = cost;
this.direction = direction;
}
}

@Override
public Long solve(Part part, List<String> inputLines) {
super.solve(part, inputLines);
char[][] map = Array2DUtils.readAsMatrix(inputLines);
return countCheats(map, isPart2() ? 20 : 2);
}

private long countCheats(char[][] map, int maxDist) {
Pos startPos = Array2DUtils.getXPos(map, 'S').get(0);
Pos endPos = Array2DUtils.getXPos(map, 'E').get(0);

List<PathNode> initialPath = shortestPath(map, startPos, endPos);
long initialBestCost = initialPath.get(initialPath.size() - 1).cost;
long cheatCount = 0;

for (int i = 0; i < initialPath.size() - 1; i++) {
PathNode pi = initialPath.get(i);
for (int j = i + 1; j < initialPath.size(); j++) {
PathNode pj = initialPath.get(j);
int dist = MathUtils.manhattanDistance(pi.pos.x, pi.pos.y, pj.pos.x, pj.pos.y);
if (dist <= maxDist && (j - i - dist) >= 100) cheatCount++;
}
}

return cheatCount;
}

private List<PathNode> shortestPath(char[][] map, Pos startPos, Pos endPos) {
PathNode[][] costMap = new PathNode[map.length][map[0].length];
costMap[startPos.x][startPos.y] = new PathNode(startPos, 0, Direction.EAST);

PriorityQueue<PathNode> pq = new PriorityQueue<>((o1, o2) -> (int) (o2.cost - o1.cost));
pq.add(costMap[startPos.x][startPos.y]);
while (!pq.isEmpty()) {
PathNode curr = pq.poll();

for (Direction d : Direction.nextDirection(curr.direction)) {
int newX = curr.pos.x + d.dx;
int newY = curr.pos.y + d.dy;
long nextCost = curr.cost + 1;

if (map[newX][newY] == '#') continue;

if (costMap[newX][newY] == null) {
costMap[newX][newY] = new PathNode(new Pos(newX, newY), Long.MAX_VALUE, d);
}

if (nextCost < costMap[newX][newY].cost) {
costMap[newX][newY].cost = nextCost;
costMap[newX][newY].prev = curr;
pq.add(costMap[newX][newY]);
}
}
}

List<PathNode> path = new ArrayList<>();
PathNode curr = costMap[endPos.x][endPos.y];
while (curr != null) {
path.add(curr);
curr = curr.prev;
}
Collections.reverse(path);
return path;
}
}
49 changes: 49 additions & 0 deletions src/test/java/net/bqc/aoc/year2024/Day20Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package net.bqc.aoc.year2024;

import net.bqc.aoc.AbstractTest;
import net.bqc.aoc.Solution;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.List;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class Day20Test extends AbstractTest {

private Day20 solution;

@BeforeAll
static void setUp() {
INPUT_PATH = "year2024/Day20_Input.txt";
SAMPLE_INPUT_PATH = "year2024/Day20_SampleInput.txt";
}

@BeforeEach
void init() {
solution = new Day20();
}

@ParameterizedTest
@MethodSource("inputDataSource")
void testSolver(Solution.Part part, List<String> inputLines, long expectedOutput) {
long computedOutput = solution.solve(part, inputLines);
assertEquals(expectedOutput, computedOutput);
}

static Stream<Arguments> inputDataSource() {
List<String> sampleInputLines = getSampleInput();
List<String> inputLines = getInput();
return Stream.of(
Arguments.of(Solution.Part.ONE, sampleInputLines, 0),
Arguments.of(Solution.Part.ONE, inputLines, 1289),

Arguments.of(Solution.Part.TWO, sampleInputLines, 0),
Arguments.of(Solution.Part.TWO, inputLines, 982425)
);
}
}
Loading

0 comments on commit 0a451d7

Please sign in to comment.