Skip to content

Commit

Permalink
Merge pull request #17 from Flashky/feature/day_21
Browse files Browse the repository at this point in the history
Feature/day 21
  • Loading branch information
Flashky authored Dec 29, 2024
2 parents 45bfdc9 + 22645bf commit 017012a
Show file tree
Hide file tree
Showing 12 changed files with 355 additions and 17 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
- [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)
- [Day 20 - Race Condition](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day20)
- [Day 21](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day21)
- [Day 21 - Keypad Conundrum](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day21)
- [Day 22 - Monkey Market](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day22)
- [Day 23 - LAN Party](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day23)
- [Day 24 - Crossed Wires](https://github.com/Flashky/advent-of-code-2024/tree/master/src/main/java/com/adventofcode/flashk/day24)
Expand Down
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
<!-- Google Libraries -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.4.0-jre</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/adventofcode/flashk/common/Input.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.adventofcode.flashk.common;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public final class Input {

private static final String PATH_INPUTS = "src/main/resources";

private Input() {
}

public static List<String> readStringLines(String inputFolder, String inputFile) {

List<String> input;

try {
Path path = Paths.get(PATH_INPUTS, inputFolder, inputFile).toAbsolutePath();
input = Files.lines(path).collect(Collectors.toList());

} catch (IOException e) {
input = new ArrayList<>();
e.printStackTrace();
}

return input;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.adventofcode.flashk.common.jgrapht;

import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.nio.Attribute;
import org.jgrapht.nio.AttributeType;

/// Labeled edge class based on [JGraphT LabeledEdges](https://jgrapht.org/guide/LabeledEdges)
///
/// Allows creating an edge with label.
///
/// It also implements the JGraphT Attribute interface to allow exporting the labels in DOT representation
public class LabeledEdge extends DefaultEdge implements Attribute {

private final String label;

public LabeledEdge(String label) {
this.label = label;
}

@Override
public String getValue() {
return label;
}

@Override
public AttributeType getType() {
return AttributeType.STRING;
}

@Override
public String toString() {
return "(" + getSource() + " : " + getTarget() + " : " + label + ")";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.adventofcode.flashk.day21;

public record CharacterPress(char currentButton, char nextButton) {
}
160 changes: 160 additions & 0 deletions src/main/java/com/adventofcode/flashk/day21/Keypad.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package com.adventofcode.flashk.day21;

import com.adventofcode.flashk.common.Input;
import com.adventofcode.flashk.common.jgrapht.LabeledEdge;
import lombok.Setter;
import org.jgrapht.Graph;
import org.jgrapht.GraphPath;
import org.jgrapht.alg.shortestpath.AllDirectedPaths;
import org.jgrapht.graph.DirectedMultigraph;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class Keypad {

private static final String FILE_DIRECTIONAL_MAPPINGS = "directional_mappings.txt";
private static final String FILE_NUMPAD_MAPPINGS = "numpad_mappings.txt";

private final Graph<String, LabeledEdge> graph = new DirectedMultigraph<>(LabeledEdge.class);
private final boolean directional;

// Memoization maps
private static final Map<CharacterPress,Set<String>> memoGetPaths = new HashMap<>();
private final Map<CharacterPress,Long> memoMinButtonLength = new HashMap<>();

@Setter
private Keypad nextKeypad;

public Keypad(boolean isDirectional) {

// Create the graph using mappings files

// Each line of the mappings files is a comma delimited list which represents:
// source_vertex,edge_label,target_vertex

// This allows to create a complete graph based on the contents of the graph.
directional = isDirectional;

// Read the key mappings from the file
String filename = directional ? FILE_DIRECTIONAL_MAPPINGS : FILE_NUMPAD_MAPPINGS;
List<String> lines = Input.readStringLines("day21", filename);

// Create the vertexes and edges described at each line
for(String line : lines) {

String[] parts = line.split(",");
String vertexSource = parts[0];
String edgeLabel = parts[1];
String vertexTarget = parts[2];

if(!graph.containsVertex(vertexSource)) {
graph.addVertex(vertexSource);
}

if(!graph.containsVertex(vertexTarget)) {
graph.addVertex(vertexTarget);
}

graph.addEdge(vertexSource, vertexTarget, new LabeledEdge(edgeLabel));

}

}

public long press(String code) {

char[] buttons = code.toCharArray();

long minimumLength = 0;

char previousButton = 'A';

for(char button : buttons) {

button = mapDirectionalButton(button);
long minimumButtonLength = getMinimumButtonLength(previousButton, button);
previousButton = button;

minimumLength += minimumButtonLength;

}

return minimumLength;
}

private long getMinimumButtonLength(char previousButton, char button) {

// Memoization
CharacterPress state = new CharacterPress(previousButton, button);
if(memoMinButtonLength.containsKey(state)) {
return memoMinButtonLength.get(state);
}

// Obtain from memo or compute. Add it to memoization if it didn't exist.
Set<String> buttonPaths = getPaths(previousButton, button);

long minimumButtonLength = Long.MAX_VALUE;

for (String buttonPath : buttonPaths) {
long buttonLength = nextKeypad != null ? nextKeypad.press(buttonPath) : buttonPath.length();
minimumButtonLength = Math.min(buttonLength, minimumButtonLength);
}

// Memoization
memoMinButtonLength.put(state, minimumButtonLength);

return minimumButtonLength;
}

private char mapDirectionalButton(char button) {
if(directional) {
return switch(button) {
case '<' -> 'L';
case '>' -> 'R';
case '^' -> 'U';
case 'v' -> 'D';
case 'A' -> 'A';
default -> throw new IllegalStateException("Unexpected button value: " + button);
};
}

return button;
}

public Set<String> getPaths(char origin, char destination) {

// Use cache first
CharacterPress state = new CharacterPress(origin, destination);
if(memoGetPaths.containsKey(state)) {
return memoGetPaths.get(state);
}

AllDirectedPaths<String, LabeledEdge> adp = new AllDirectedPaths<>(graph);

List<GraphPath<String, LabeledEdge>> graphPaths = adp.getAllPaths(String.valueOf(origin), String.valueOf(destination),
true,4);

int shortestPathSize = Integer.MAX_VALUE;

Set<String> paths = new HashSet<>();
for(GraphPath<String, LabeledEdge> graphPath : graphPaths) {
String path = graphPath.getEdgeList().stream().map(LabeledEdge::getValue).collect(Collectors.joining()) + "A";
shortestPathSize = Math.min(path.length(), shortestPathSize);
paths.add(path);
}

int finalShortestPathSize = shortestPathSize;
paths = paths.stream().filter(p -> p.length() == finalShortestPathSize).collect(Collectors.toSet());

// Memoize paths
memoGetPaths.put(state, paths);

return paths;
}

}
50 changes: 50 additions & 0 deletions src/main/java/com/adventofcode/flashk/day21/KeypadConundrum.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.adventofcode.flashk.day21;



import org.apache.commons.lang3.StringUtils;

import java.util.List;

public class KeypadConundrum {

private final List<String> codes;

private final Keypad numpad;

public KeypadConundrum(List<String> inputs, int keypadsNumber) {
codes = inputs;

// Create all keypad robots and link between them
Keypad previousRobot = null;

for(int i = 0; i < keypadsNumber; i++) {
Keypad currentRobot = new Keypad(true);
if(previousRobot != null) {
currentRobot.setNextKeypad(previousRobot);
}
previousRobot = currentRobot;
}

numpad = new Keypad(false);
numpad.setNextKeypad(previousRobot);
}

public long solveA() {

long result = 0;
for(String code : codes) {
result += press(code);
}

return result;
}

private long press(String code) {
long shortestSequenceLength = numpad.press(code);
long numericValue = Long.parseLong(code.replace("A", StringUtils.EMPTY));
return shortestSequenceLength * numericValue;
}


}
2 changes: 1 addition & 1 deletion src/main/java/com/adventofcode/flashk/day21/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Day 21:
# Day 21: Keypad Conundrum

[https://adventofcode.com/2024/day/21](https://adventofcode.com/2024/day/21)
10 changes: 10 additions & 0 deletions src/main/resources/day21/directional_mappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
U,v,D
U,>,A
A,<,U
A,v,R
L,>,D
D,<,L
D,^,U
D,>,R
R,<,D
R,^,A
30 changes: 30 additions & 0 deletions src/main/resources/day21/numpad_mappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
7,>,8
7,v,4
8,<,7
8,>,9
8,v,5
9,<,8
9,v,6
4,^,7
4,>,5
4,v,1
5,<,4
5,^,8
5,>,6
5,v,2
6,<,5
6,^,9
6,v,3
1,>,2
1,^,4
2,<,1
2,^,5
2,>,3
2,v,0
3,<,2
3,^,6
3,v,A
0,>,A
0,^,2
A,<,0
A,^,3
Loading

0 comments on commit 017012a

Please sign in to comment.