Skip to content

Commit

Permalink
aoc 2024 day 20
Browse files Browse the repository at this point in the history
  • Loading branch information
TheCK committed Dec 20, 2024
1 parent 996c7fe commit e5f96d3
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 17 deletions.
6 changes: 3 additions & 3 deletions adventofcode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1454,7 +1454,7 @@
[20232402tests]: src/test/java/org/ck/adventofcode/year2023/Day24Test.java
[20232501tests]: src/test/java/org/ck/adventofcode/year2023/Day25Test.java

# 2024 (38/49)
# 2024 (40/49)

| # | Name | Solution | Test |
|---------:|-----------------------------------------------------|:------------------------------------:|:---------------------------------:|
Expand Down Expand Up @@ -1496,8 +1496,8 @@
| 20241802 | [Day 18: RAM Run - Part 2][20241802] | ✅[💾][20241802solution] | ✅[💾][20241802tests] |
| 20241901 | [Day 19: Linen Layout][20241901] | ✅[💾][20241901solution] | ✅[💾][20241901tests] |
| 20241902 | [Day 19: Linen Layout - Part 2][20241902] | ✅[💾][20241902solution] | ✅[💾][20241902tests] |
| 20242001 | [Day 20: ?][20242001] | [💾][20242001solution] | [💾][20242001tests] |
| 20242002 | [Day 20: ? - Part 2][20242002] | [💾][20242002solution] | [💾][20242002tests] |
| 20242001 | [Day 20: Race Condition][20242001] | ✅[💾][20242001solution] | ✅[💾][20242001tests] |
| 20242002 | [Day 20: Race Condition - Part 2][20242002] | ✅[💾][20242002solution] | ✅[💾][20242002tests] |
| 20242101 | [Day 21: ?][20242101] | [💾][20242101solution] | [💾][20242101tests] |
| 20242102 | [Day 21: ? - Part 2][20242102] | [💾][20242102solution] | [💾][20242102tests] |
| 20242201 | [Day 22: ?][20242201] | [💾][20242201solution] | [💾][20242201tests] |
Expand Down
166 changes: 155 additions & 11 deletions adventofcode/src/main/java/org/ck/adventofcode/year2024/Day20.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,178 @@
package org.ck.adventofcode.year2024;

import java.util.Scanner;
import java.util.*;
import org.ck.adventofcode.util.AOCSolution;
import org.ck.codechallengelib.annotation.Solution;

@Solution(
id = 20242001,
name = "Day 20: ?",
name = "Day 20: Race Condition",
url = "https://adventofcode.com/2024/day/20",
category = "2024",
solved = false)
category = "2024")
@Solution(
id = 20242002,
name = "Day 20: ? - Part 2",
name = "Day 20: Race Condition - Part 2",
url = "https://adventofcode.com/2024/day/20#part2",
category = "2024",
solved = false)
category = "2024")
public class Day20 extends AOCSolution {

@Override
protected void runPartOne(final Scanner in) {
run(in);
run(in, 2);
}

@Override
protected void runPartTwo(final Scanner in) {
run(in);
run(in, 20);
}

private void run(final Scanner in) {
print("Whoopsie");
private void run(final Scanner in, final int allowedCheatTime) {
final int wantedSaveAmount = Integer.parseInt(in.nextLine());

final List<String> grid = new ArrayList<>();
Coordinate start = null;
Coordinate end = null;

int row = 0;
while (in.hasNextLine()) {
final String line = in.nextLine();

if (line.contains("S")) {
start = new Coordinate(line.indexOf('S'), row);
}
if (line.contains("E")) {
end = new Coordinate(line.indexOf('E'), row);
}

grid.add(line);
++row;
}

if (start == null || end == null) {
throw new IllegalArgumentException("Start and end coordinates must be provided");
}

final long[][] distances = new long[grid.size()][grid.getFirst().length()];
for (final long[] distanceRow : distances) {
Arrays.fill(distanceRow, Long.MAX_VALUE);
}

fillFromStartToEnd(distances, grid, start, end);

fillReachableFromEnd(distances, grid, end);

final Map<Long, Long> savesPerAmount = getSavesPerAmount(distances, allowedCheatTime);

long savesOverValue = 0;
for (final Map.Entry<Long, Long> entry : savesPerAmount.entrySet()) {
if (entry.getKey() >= wantedSaveAmount) {
savesOverValue += entry.getValue();
}
}

print(savesOverValue);
}

private static void fillFromStartToEnd(
final long[][] distances,
final List<String> grid,
final Coordinate start,
final Coordinate end) {
final Queue<State> queue = new PriorityQueue<>(Comparator.comparingLong(State::steps));
queue.add(new State(start, 0));

while (!queue.isEmpty()) {
final State state = queue.poll();
final Coordinate coordinate = state.coordinate();
final long steps = state.steps();

distances[coordinate.y()][coordinate.x()] = steps;

if (coordinate.equals(end)) {
break;
}

final long nextSteps = steps + 1;
visitNeighbours(distances, grid, coordinate, nextSteps, queue);
}
}

private static void fillReachableFromEnd(
final long[][] distances, final List<String> grid, final Coordinate end) {
final Queue<State> queue = new PriorityQueue<>(Comparator.comparingLong(State::steps));
queue.add(new State(end, distances[end.y()][end.x()]));

while (!queue.isEmpty()) {
final State state = queue.poll();
final Coordinate coordinate = state.coordinate();
final long steps = state.steps();

distances[coordinate.y()][coordinate.x()] = steps;

final long nextSteps = steps - 1;
visitNeighbours(distances, grid, coordinate, nextSteps, queue);
}
}

private static void visitNeighbours(
final long[][] distances,
final List<String> grid,
final Coordinate coordinate,
final long nextSteps,
final Queue<State> queue) {
for (final Coordinate next :
Set.of(
new Coordinate(coordinate.x() - 1, coordinate.y()),
new Coordinate(coordinate.x() + 1, coordinate.y()),
new Coordinate(coordinate.x(), coordinate.y() - 1),
new Coordinate(coordinate.x(), coordinate.y() + 1))) {
if (grid.get(next.y).charAt(next.x()) != '#' && distances[next.y()][next.x()] > nextSteps) {
queue.add(new State(next, nextSteps));
}
}
}

private static Map<Long, Long> getSavesPerAmount(
final long[][] distances, final int allowedCheatTime) {
final Map<Long, Long> savesPerAmount = new HashMap<>();

for (int y1 = 1; y1 < distances.length - 1; ++y1) {
for (int x1 = 1; x1 < distances[y1].length - 1; ++x1) {
final long distance1 = distances[y1][x1];

if (distance1 < Long.MAX_VALUE) {
getCheatsPerStart(distances, allowedCheatTime, y1, x1, distance1, savesPerAmount);
}
}
}
return savesPerAmount;
}

private static void getCheatsPerStart(
final long[][] distances,
final int allowedCheatTime,
final int y1,
final int x1,
final long distance1,
final Map<Long, Long> savesPerAmount) {
for (int y2 = 1; y2 < distances.length - 1; ++y2) {
for (int x2 = 1; x2 < distances[y1].length - 1; ++x2) {
final long distance2 = distances[y2][x2];
final long cheatTime = (long) Math.abs(y1 - y2) + Math.abs(x1 - x2);

if (distance2 < Long.MAX_VALUE && distance1 < distance2 && cheatTime <= allowedCheatTime) {
final long saveAmount = distance2 - distance1 - cheatTime;

if (saveAmount > 0) {
savesPerAmount.put(
saveAmount, savesPerAmount.computeIfAbsent(saveAmount, key -> 0L) + 1);
}
}
}
}
}

private record Coordinate(int x, int y) {}

private record State(Coordinate coordinate, long steps) {}
}
Loading

0 comments on commit e5f96d3

Please sign in to comment.