diff --git a/pom.xml b/pom.xml
index 7a55bd3..e9a3931 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,11 @@
jgrapht-core
1.5.2
+
+ org.jgrapht
+ jgrapht-io
+ 1.5.2
+
com.fasterxml.jackson.core
diff --git a/src/main/java/com/adventofcode/flashk/day24/CrossedWiresVisual.java b/src/main/java/com/adventofcode/flashk/day24/CrossedWiresVisual.java
new file mode 100644
index 0000000..03abbc0
--- /dev/null
+++ b/src/main/java/com/adventofcode/flashk/day24/CrossedWiresVisual.java
@@ -0,0 +1,177 @@
+package com.adventofcode.flashk.day24;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jgrapht.Graph;
+import org.jgrapht.graph.DirectedMultigraph;
+import org.jgrapht.nio.dot.DOTExporter;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class CrossedWiresVisual {
+
+ private static final Pattern GATE_PATTERN = Pattern.compile("(\\w*) (AND|XOR|OR) (\\w*) -> (\\w*)");
+
+ private final Graph graph = new DirectedMultigraph<>(String.class);
+
+ private final Map gatesPerOutput = new HashMap<>();
+ private final Map wires = new HashMap<>();
+ private final List gates = new ArrayList<>();
+ private List endWires;
+
+ public CrossedWiresVisual(List inputs) {
+ initializeWires(inputs);
+ initializeGates(inputs);
+ createStartEndVertexes();
+ connectGates();
+ }
+
+ private void connectGates() {
+ // Por cada puerta
+ // Buscar si existe en gatesPerOutput
+ for(GateVisual gate : gatesPerOutput.values()) {
+ String output = gate.getOutput();
+
+ // Search gates that have this output gate as input 1.
+ List gatesInput1 = gates.stream().filter(g -> output.equals(g.getInput1())).toList();
+ for(GateVisual gateInput1 : gatesInput1) {
+ String edgeId = output + "_" + UUID.randomUUID();
+ graph.addEdge(gate, gateInput1, edgeId);
+ }
+
+ // Search gates that have this output gate as input 2.
+ List gatesInput2 = gates.stream().filter(g -> output.equals(g.getInput2())).toList();
+ for(GateVisual gateInput2 : gatesInput2) {
+ String edgeId = output + "_" + UUID.randomUUID();
+ graph.addEdge(gate, gateInput2, edgeId);
+ }
+
+ }
+
+ }
+
+ private void createStartEndVertexes() {
+ wires.keySet().stream().filter(this::isStartOrEnd).forEach(this::createStartEndVertex);
+ }
+
+
+ private void initializeGates(List inputs) {
+ List gatesInputs = inputs.stream().skip(inputs.indexOf(StringUtils.EMPTY)+1).toList();
+
+ for(String input : gatesInputs) {
+ Matcher matcher = GATE_PATTERN.matcher(input);
+ if(matcher.find()) {
+ String operation = matcher.group(2);
+ String input1 = matcher.group(1);
+ String input2 = matcher.group(3);
+ String output = matcher.group(4);
+
+ GateVisual gateVisual = new GateVisual(operation, input1, input2, output);
+ gates.add(gateVisual);
+ gatesPerOutput.put(output, gateVisual);
+ graph.addVertex(gateVisual);
+ }
+ }
+ }
+
+ private void initializeWires(List inputs) {
+
+ boolean inputGates = false;
+ for (String input : inputs) {
+
+ if (StringUtils.EMPTY.equals(input)) {
+ inputGates = true;
+ continue;
+ }
+
+ if (!inputGates) {
+ String[] wireInput = input.replace(StringUtils.SPACE, StringUtils.EMPTY).split(":");
+ wires.put(wireInput[0], Integer.parseInt(wireInput[1]));
+ } else {
+ Matcher matcher = GATE_PATTERN.matcher(input);
+ if (matcher.find()) {
+ wires.putIfAbsent(matcher.group(1), -1);
+ wires.putIfAbsent(matcher.group(3), -1);
+ wires.putIfAbsent(matcher.group(4), -1);
+ }
+ }
+ }
+
+ endWires = wires.keySet().stream().filter(w -> w.startsWith("z")).sorted().toList().reversed();
+ }
+
+
+ public String solveB() {
+
+ // Obtain the graph in DOT format:
+ paint();
+
+ // Visual finding of invalid edges
+ System.out.println("Tools:");
+ System.out.println("- (Optional) graphviz: sudo apt install graphviz");
+ System.out.println("- graphviz2drawio: pip install graphviz2drawio");
+ System.out.println();
+ System.out.println("Procedure:");
+ System.out.println("1. Add the result to a file: dotgraph_day24.txt");
+ System.out.println("2. (Optional) Execute command: cat day24.txt | dot -Tsvg > day24.svg");
+ System.out.println("3. Execute command: graphviz2drawio dotgraph_day24.txt");
+ System.out.println("4. Open the exported 'dotgraph_day24.xml' file at https://draw.io");
+ System.out.println("5. Order the graph nodes until you see the invalid connections");
+
+ // Invalid gates are:
+ // bbp AND wwg -> z09
+ // wwg XOR bbp -> hnd
+ // ncj OR pwk -> z16
+ // jkw XOR mqf -> tdv
+ // x23 AND y23 -> z23
+ // mfr XOR scw -> bks
+ // x37 AND y37 -> tjp
+ // x37 XOR y37 -> nrn
+
+ // So the outputs are: z09,hnd,z16,tdv,z23,bks,tjp,nrn
+ List outputGates = List.of("z09","hnd","z16","tdv","z23","bks","tjp","nrn");
+
+ return outputGates.stream().sorted().collect(Collectors.joining(","));
+ }
+
+ private void createStartEndVertex(String wire) {
+ GateVisual gateVisual = new GateVisual(wire);
+ graph.addVertex(gateVisual);
+
+ if(isStart(wire)) {
+ gatesPerOutput.put(wire, gateVisual);
+ } else if(isEnd(wire)) {
+ gates.add(gateVisual);
+ }
+
+ }
+
+ private boolean isStart(String wire) {
+ return wire.startsWith("x") || wire.startsWith("y");
+ }
+
+ private boolean isEnd(String wire) {
+ return wire.startsWith("z");
+ }
+
+ private boolean isStartOrEnd(String wire) {
+ return isStart(wire) || isEnd(wire);
+ }
+
+ private void paint() {
+ DOTExporter exporter = new DOTExporter<>(v -> v.getLabel());
+ Writer writer = new StringWriter();
+ exporter.exportGraph(graph, writer);
+ System.out.println(writer.toString());
+ }
+
+
+}
diff --git a/src/main/java/com/adventofcode/flashk/day24/GateVisual.java b/src/main/java/com/adventofcode/flashk/day24/GateVisual.java
new file mode 100644
index 0000000..cc51bb4
--- /dev/null
+++ b/src/main/java/com/adventofcode/flashk/day24/GateVisual.java
@@ -0,0 +1,44 @@
+package com.adventofcode.flashk.day24;
+
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+@Getter
+public class GateVisual {
+
+ private static int id = 0;
+
+ private final String label;
+ private final String operation;
+ private String input1;
+ private String input2;
+ private String output;
+
+ public GateVisual(String label) {
+ this.label = label;
+ this.operation = StringUtils.EMPTY;
+ if(label.startsWith("x") || label.startsWith("y")) {
+ this.output = label;
+ } else if(label.startsWith("z")) {
+ this.input1 = label;
+ }
+ }
+
+ public GateVisual(String operation, String input1, String input2, String output) {
+ this.label = operation + "_" + output;
+ this.operation = operation;
+ this.input1 = input1;
+ this.input2 = input2;
+ this.output = output;
+ }
+
+ public int operate(int value1, int value2) {
+ return switch (operation) {
+ case "AND" -> value1 & value2;
+ case "XOR" -> value1 ^ value2;
+ case "OR" -> value1 | value2;
+ case null, default -> throw new IllegalStateException("Unknown gate operation: " + operation);
+ };
+ }
+
+}
diff --git a/src/test/java/com/adventofcode/flashk/common/test/constants/TestFilename.java b/src/test/java/com/adventofcode/flashk/common/test/constants/TestFilename.java
index 06f010e..fbfd10f 100644
--- a/src/test/java/com/adventofcode/flashk/common/test/constants/TestFilename.java
+++ b/src/test/java/com/adventofcode/flashk/common/test/constants/TestFilename.java
@@ -8,6 +8,7 @@ private TestFilename() {}
public static final String INPUT_FILE = "data.input";
public static final String INPUT_FILE_SAMPLE = "sample.input";
public static final String INPUT_FILE_SAMPLE_2 = "sample_2.input";
+ public static final String INPUT_FILE_SAMPLE_3 = "sample_3.input";
public static final String INPUT_FILE_SAMPLE_PART_2 = "sample_part_2.input";
public static final String INPUT_FILE_SINGLE_SAMPLE = "single_sample.input";
public static final String INPUT_FILE_SINGLE_SAMPLE_2 = "single_sample_2.input";
diff --git a/src/test/java/com/adventofcode/flashk/day24/Day24Test.java b/src/test/java/com/adventofcode/flashk/day24/Day24Test.java
index a85b890..01838f7 100644
--- a/src/test/java/com/adventofcode/flashk/day24/Day24Test.java
+++ b/src/test/java/com/adventofcode/flashk/day24/Day24Test.java
@@ -14,15 +14,13 @@
import com.adventofcode.flashk.common.test.constants.TestFilename;
import com.adventofcode.flashk.common.test.constants.TestFolder;
import com.adventofcode.flashk.common.test.constants.TestTag;
-import com.adventofcode.flashk.common.test.utils.PuzzleTest;
import com.adventofcode.flashk.common.test.utils.Input;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DisplayName(TestDisplayName.DAY_24)
@TestMethodOrder(OrderAnnotation.class)
-@Disabled // TODO Remove comment when implemented
-public class Day24Test extends PuzzleTest {
+class Day24Test {
private static final String INPUT_FOLDER = TestFolder.DAY_24;
@@ -31,7 +29,7 @@ public class Day24Test extends PuzzleTest {
@Tag(TestTag.PART_1)
@Tag(TestTag.SAMPLE)
@DisplayName(TestDisplayName.PART_1_SAMPLE)
- public void testSolvePart1Sample() {
+ void part1SampleTest() {
// Read input file
List inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE);
@@ -47,7 +45,7 @@ public void testSolvePart1Sample() {
@Tag(TestTag.PART_1)
@Tag(TestTag.SAMPLE)
@DisplayName(TestDisplayName.PART_1_SAMPLE_2)
- public void testSolvePart1Sample2() {
+ void part1Sample2Test() {
// Read input file
List inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE_2);
@@ -62,7 +60,7 @@ public void testSolvePart1Sample2() {
@Tag(TestTag.PART_1)
@Tag(TestTag.INPUT)
@DisplayName(TestDisplayName.PART_1_INPUT)
- public void testSolvePart1Input() {
+ void part1InputTest() {
// Read input file
List inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE);
@@ -78,11 +76,28 @@ public void testSolvePart1Input() {
@Tag(TestTag.PART_2)
@Tag(TestTag.SAMPLE)
@DisplayName(TestDisplayName.PART_2_SAMPLE)
- public void testSolvePart2Sample() {
+ void part2SampleTest() {
// Read input file
List inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE);
+ CrossedWiresVisual crossedWires = new CrossedWiresVisual(inputs);
+ crossedWires.solveB();
+ assertEquals(0L,0L);
+ }
+
+ @Test
+ @Order(3)
+ @Tag(TestTag.PART_2)
+ @Tag(TestTag.SAMPLE)
+ @DisplayName(TestDisplayName.PART_2_SAMPLE)
+ void part2Sample3Test() {
+
+ // Read input file
+ List inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE_SAMPLE_3);
+ CrossedWiresVisual crossedWires = new CrossedWiresVisual(inputs);
+
+ crossedWires.solveB();
assertEquals(0L,0L);
}
@@ -91,13 +106,13 @@ public void testSolvePart2Sample() {
@Tag(TestTag.PART_2)
@Tag(TestTag.INPUT)
@DisplayName(TestDisplayName.PART_2_INPUT)
- public void testSolvePart2Input() {
+ void part2InputTest() {
// Read input file
List inputs = Input.readStringLines(INPUT_FOLDER, TestFilename.INPUT_FILE);
+ CrossedWiresVisual crossedWires = new CrossedWiresVisual(inputs);
- System.out.println("Solution: ");
- assertEquals(0L,0L);
+ assertEquals("bks,hnd,nrn,tdv,tjp,z09,z16,z23", crossedWires.solveB());
}