diff --git a/AdventOfCodeData b/AdventOfCodeData
index 576bbc8..33e3e1a 160000
--- a/AdventOfCodeData
+++ b/AdventOfCodeData
@@ -1 +1 @@
-Subproject commit 576bbc83b60aaebc010c3e6e520e0dc1ec9f8257
+Subproject commit 33e3e1a6760b6550060c34838f4ecbdec4034add
diff --git a/pom.xml b/pom.xml
index 2fb3684..a81e1d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,11 @@
jgrapht-core
1.5.2
+
+ tools.aqua
+ z3-turnkey
+ 4.14.1
+
diff --git a/src/main/java/com/example/adventofcode/year2025/day10/Factory.java b/src/main/java/com/example/adventofcode/year2025/day10/Factory.java
new file mode 100644
index 0000000..495edf7
--- /dev/null
+++ b/src/main/java/com/example/adventofcode/year2025/day10/Factory.java
@@ -0,0 +1,180 @@
+package com.example.adventofcode.year2025.day10;
+
+import com.microsoft.z3.*;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.IntStream;
+
+import static com.example.adventofcode.utils.FileUtils.readLines;
+
+public class Factory {
+ private static final String FILENAME = "AdventOfCodeData/2025/day10/input";
+ private static final String EXAMPLE_FILENAME = "AdventOfCodeData/2025/day10/example_input";
+
+ public static void main(String[] args) throws IOException {
+ System.out.println(countMinButtonPressesForIndicatorLights(EXAMPLE_FILENAME));
+ System.out.println(countMinButtonPressesForIndicatorLights(FILENAME));
+ System.out.println(countMinButtonPressesForJoltageLevelCounters(EXAMPLE_FILENAME));
+ System.out.println(countMinButtonPressesForJoltageLevelCounters(FILENAME));
+ }
+
+ record State(String[] lights, int steps) {
+ }
+
+ public static long countMinButtonPressesForIndicatorLights(final String filename) throws IOException {
+ List lines = readLines(filename);
+ int steps = 0;
+ for (String line : lines) {
+ String[] elements = line.split(" ");
+ String[] targetLights = elements[0].substring(1, elements[0].length() - 1).split("");
+ List> buttons = parseButtons(elements);
+
+ steps += countMinButtonPressesForIndicatorLights(targetLights, buttons);
+ }
+
+ return steps;
+ }
+
+ public static long countMinButtonPressesForJoltageLevelCounters(final String filename) throws IOException {
+ List lines = readLines(filename);
+ long steps = 0;
+ for (String line : lines) {
+ String[] elements = line.split(" ");
+ List> buttons = parseButtons(elements);
+ List joltages = parseJoltages(elements);
+
+ steps += solveLinearProblem(buttons, joltages);
+ }
+
+ return steps;
+ }
+
+ private static List> parseButtons(String[] elements) {
+ List> buttons = new ArrayList<>();
+ for (int i = 1; i < elements.length - 1; i++) {
+ String buttonsString = elements[i].substring(1, elements[i].length() - 1);
+
+ String[] buttonString = buttonsString.split(",");
+
+ List buttonElements = new ArrayList<>();
+ for (String b: buttonString) {
+ buttonElements.add(Integer.parseInt(b));
+ }
+ buttons.add(buttonElements);
+ }
+ return buttons;
+ }
+
+ private static int countMinButtonPressesForIndicatorLights(final String[] targetLights, final List> buttons) {
+ Deque p = new ArrayDeque<>();
+ p.addLast(new State(parseInitialState(targetLights), 0));
+
+ while (!p.isEmpty()) {
+ State current = p.pollFirst();
+ for (List button : buttons) {
+ String[] nextLights = Arrays.copyOf(current.lights, current.lights.length);
+
+ for (int lightPosition : button) {
+ if (lightPosition >= nextLights.length) {
+ continue;
+ }
+
+ String light = nextLights[lightPosition];
+
+ if (light.equals(".")) {
+ nextLights[lightPosition] = "#";
+ } else {
+ nextLights[lightPosition] = ".";
+ }
+ }
+ if (checkIfTargetReached(targetLights, nextLights)) {
+ return current.steps + 1;
+ } else {
+ p.addLast(new State(nextLights, current.steps + 1));
+ }
+ }
+ }
+ return 0;
+ }
+
+ private static String[] parseInitialState(String[] targetLights) {
+ String[] initialState = new String[targetLights.length];
+ for (int i = 0; i < targetLights.length; i++) {
+ initialState[i] = ".";
+ }
+ return initialState;
+ }
+
+ private static boolean checkIfTargetReached(String[] targetLights, String[] currentLights) {
+ for (int k = 0; k < targetLights.length; k++) {
+ if (!targetLights[k].equals(currentLights[k])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static List parseJoltages(String[] elements) {
+ String joltageString = elements[elements.length - 1].substring(1, elements[elements.length - 1].length() - 1);
+ return Arrays.stream(joltageString.split(",")).map(Integer::parseInt).toList();
+ }
+
+ private static int solveLinearProblem(List> buttons, List joltages) {
+ Context ctx = new Context();
+ Optimize opt = ctx.mkOptimize();
+
+ IntExpr[] buttonVariables = buildButtonVariables(buttons, ctx);
+ addButtonGreaterOrEqualToZeroEquations(ctx, buttonVariables, opt);
+ addJoltageEquations(buttons, joltages, buttonVariables, ctx, opt);
+
+ IntExpr total = ctx.mkIntConst("total");
+ addButtonPressesEquation(ctx, buttonVariables, total, opt);
+
+ opt.MkMinimize(total);
+
+ Status status = opt.Check();
+ if (status == Status.SATISFIABLE) {
+ return ((IntNum) opt.getModel()
+ .evaluate(total, false))
+ .getInt();
+ }
+
+ return -1;
+ }
+
+ private static IntExpr[] buildButtonVariables(List> buttons, Context ctx) {
+ return IntStream.range(0, buttons.size())
+ .mapToObj(i -> ctx.mkIntConst("b" + i))
+ .toArray(IntExpr[]::new);
+ }
+
+ private static void addButtonGreaterOrEqualToZeroEquations(Context ctx, IntExpr[] buttonVariables, Optimize opt) {
+ IntExpr zeroVariable = ctx.mkInt(0);
+ for (IntExpr buttonVar : buttonVariables) {
+ opt.Add(ctx.mkGe(buttonVar, zeroVariable));
+ }
+ }
+
+ private static void addJoltageEquations(List> buttons, List joltages, IntExpr[] buttonVariables, Context ctx, Optimize opt) {
+ for (int joltageNumber = 0; joltageNumber < joltages.size(); joltageNumber++) {
+ List buttonsUsed = new ArrayList<>();
+ for (int i = 0; i < buttons.size(); i++) {
+ if (buttons.get(i).contains(joltageNumber)) {
+ buttonsUsed.add(buttonVariables[i]);
+ }
+ }
+ IntExpr joltageButtonsSum = (IntExpr) ctx.mkAdd(buttonsUsed.toArray(new IntExpr[0]));
+ IntExpr targetValue = ctx.mkInt(joltages.get(joltageNumber));
+
+ BoolExpr equation = ctx.mkEq(joltageButtonsSum, targetValue);
+ opt.Add(equation);
+ }
+ }
+
+ private static void addButtonPressesEquation(Context ctx, IntExpr[] buttonVariables, IntExpr total, Optimize opt) {
+ IntExpr sumOfAllButtonVariables = (IntExpr) ctx.mkAdd(buttonVariables);
+ BoolExpr totalPresses = ctx.mkEq(total, sumOfAllButtonVariables);
+ opt.Add(totalPresses);
+ }
+}
diff --git a/src/main/java/com/example/adventofcode/year2025/day12/ChristmasTreeFarm.java b/src/main/java/com/example/adventofcode/year2025/day12/ChristmasTreeFarm.java
new file mode 100644
index 0000000..1ef87df
--- /dev/null
+++ b/src/main/java/com/example/adventofcode/year2025/day12/ChristmasTreeFarm.java
@@ -0,0 +1,64 @@
+package com.example.adventofcode.year2025.day12;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.example.adventofcode.utils.FileUtils.readLines;
+
+public class ChristmasTreeFarm {
+ private static final String FILENAME = "AdventOfCodeData/2025/day12/input";
+ private static final String EXAMPLE_FILENAME = "AdventOfCodeData/2025/day12/example_input";
+
+ public static void main(String[] args) throws IOException {
+ System.out.println(countRegionsFittingAllPresents(EXAMPLE_FILENAME));
+ System.out.println(countRegionsFittingAllPresents(FILENAME));
+ }
+
+ record Dimensions(int x, int y) {
+ }
+
+ record Area(Dimensions dimensions, List indexes){}
+
+ public static long countRegionsFittingAllPresents(final String filename) throws IOException {
+ List lines = readLines(filename);
+ List areas = parseAreas(lines);
+
+ int regionsCount = 0;
+ for (Area area: areas) {
+ long space = (long) area.dimensions.x * area.dimensions.y;
+
+ int count = 0;
+ for (int i = 0; i < area.indexes.size(); i++) {
+ count += area.indexes.get(i);
+ }
+
+ if (space >= count * 9L) {
+ regionsCount++;
+ }
+ }
+
+ return regionsCount;
+ }
+
+ private static List parseAreas(List lines) {
+ List areas = new ArrayList<>();
+ for (String line : lines) {
+ if (line.contains("x")) {
+ String[] split1 = line.split(":");
+ String[] dimensions = split1[0].split("x");
+ int dimensionX = Integer.parseInt(dimensions[0]);
+ int dimensionY = Integer.parseInt(dimensions[1]);
+
+ String[] indexes = split1[1].strip().split(" ");
+ List integerIndexes = Arrays.stream(indexes)
+ .map(Integer::valueOf)
+ .toList();
+
+ areas.add(new Area(new Dimensions(dimensionX, dimensionY), integerIndexes));
+ }
+ }
+ return areas;
+ }
+}
diff --git a/src/test/java/com/example/adventofcode/year2025/day09/MovieTheaterTest.java b/src/test/java/com/example/adventofcode/year2025/day09/MovieTheaterTest.java
index 1ab0936..d8933a6 100644
--- a/src/test/java/com/example/adventofcode/year2025/day09/MovieTheaterTest.java
+++ b/src/test/java/com/example/adventofcode/year2025/day09/MovieTheaterTest.java
@@ -11,7 +11,7 @@
class MovieTheaterTest {
- private static Stream filepathsAndConnectionsAndExpectedLargestArea() {
+ private static Stream filepathsAndExpectedLargestArea() {
return Stream.of(
Arguments.of("AdventOfCodeData/2025/day09/example_input", 50),
Arguments.of("AdventOfCodeData/2025/day09/input", 4769758290L)
@@ -19,20 +19,20 @@ private static Stream filepathsAndConnectionsAndExpectedLargestArea()
}
@ParameterizedTest
- @MethodSource("filepathsAndConnectionsAndExpectedLargestArea")
+ @MethodSource("filepathsAndExpectedLargestArea")
void calculateLargestAreaBruteForce(final String filename,
final long expectedLargestArea) throws IOException {
assertEquals(expectedLargestArea, MovieTheater.calculateLargestAreaBruteForce(filename));
}
@ParameterizedTest
- @MethodSource("filepathsAndConnectionsAndExpectedLargestArea")
+ @MethodSource("filepathsAndExpectedLargestArea")
void calculateLargestArea(final String filename,
final long expectedLargestArea) throws IOException {
assertEquals(expectedLargestArea, MovieTheater.calculateLargestArea(filename));
}
- private static Stream filepathsAndConnectionsAndExpectedLargestAreaInsideOfPolygon() {
+ private static Stream filepathsAndExpectedLargestAreaInsideOfPolygon() {
return Stream.of(
Arguments.of("AdventOfCodeData/2025/day09/example_input", 24),
Arguments.of("AdventOfCodeData/2025/day09/input", 1588990708L)
@@ -40,7 +40,7 @@ private static Stream filepathsAndConnectionsAndExpectedLargestAreaIn
}
@ParameterizedTest
- @MethodSource("filepathsAndConnectionsAndExpectedLargestAreaInsideOfPolygon")
+ @MethodSource("filepathsAndExpectedLargestAreaInsideOfPolygon")
void calculateLargestAreaInsideOfPolygon(final String filename,
final long expectedLargestArea) throws IOException {
assertEquals(expectedLargestArea, MovieTheater.calculateLargestAreaInsideOfPolygon(filename));
diff --git a/src/test/java/com/example/adventofcode/year2025/day10/FactoryTest.java b/src/test/java/com/example/adventofcode/year2025/day10/FactoryTest.java
new file mode 100644
index 0000000..f9041bf
--- /dev/null
+++ b/src/test/java/com/example/adventofcode/year2025/day10/FactoryTest.java
@@ -0,0 +1,41 @@
+package com.example.adventofcode.year2025.day10;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.IOException;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class FactoryTest {
+
+ private static Stream filepathsAndExpectedMinButtonPressesForIndicatorLights() {
+ return Stream.of(
+ Arguments.of("AdventOfCodeData/2025/day10/example_input", 7),
+ Arguments.of("AdventOfCodeData/2025/day10/input", 507)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("filepathsAndExpectedMinButtonPressesForIndicatorLights")
+ void countMinButtonPressesForIndicatorLights(final String filename,
+ final long expectedMinButtonPresses) throws IOException {
+ assertEquals(expectedMinButtonPresses, Factory.countMinButtonPressesForIndicatorLights(filename));
+ }
+
+ private static Stream filepathsAndExpectedMinButtonPressesForJoltageLevelCounters() {
+ return Stream.of(
+ Arguments.of("AdventOfCodeData/2025/day10/example_input", 33),
+ Arguments.of("AdventOfCodeData/2025/day10/input", 18981)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("filepathsAndExpectedMinButtonPressesForJoltageLevelCounters")
+ void countMinButtonPressesForJoltageLevelCounters(final String filename,
+ final long expectedMinButtonPresses) throws IOException {
+ assertEquals(expectedMinButtonPresses, Factory.countMinButtonPressesForJoltageLevelCounters(filename));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/example/adventofcode/year2025/day11/ReactorTest.java b/src/test/java/com/example/adventofcode/year2025/day11/ReactorTest.java
index cf32d31..c309c4f 100644
--- a/src/test/java/com/example/adventofcode/year2025/day11/ReactorTest.java
+++ b/src/test/java/com/example/adventofcode/year2025/day11/ReactorTest.java
@@ -11,7 +11,7 @@
class ReactorTest {
- private static Stream filepathsAndConnectionsAndExpectedWaysOut() {
+ private static Stream filepathsAndExpectedWaysOut() {
return Stream.of(
Arguments.of("AdventOfCodeData/2025/day11/example_input", 5),
Arguments.of("AdventOfCodeData/2025/day11/input", 615)
@@ -19,13 +19,13 @@ private static Stream filepathsAndConnectionsAndExpectedWaysOut() {
}
@ParameterizedTest
- @MethodSource("filepathsAndConnectionsAndExpectedWaysOut")
+ @MethodSource("filepathsAndExpectedWaysOut")
void countWaysOut(final String filename,
final long expectedWaysOut) throws IOException {
assertEquals(expectedWaysOut, Reactor.countWaysOut(filename));
}
- private static Stream filepathsAndConnectionsAndExpectedWaysOutThroughNodesSubset() {
+ private static Stream filepathsAndExpectedWaysOutThroughNodesSubset() {
return Stream.of(
Arguments.of("AdventOfCodeData/2025/day11/example_input2", 2),
Arguments.of("AdventOfCodeData/2025/day11/input", 303012373210128L)
@@ -33,7 +33,7 @@ private static Stream filepathsAndConnectionsAndExpectedWaysOutThroug
}
@ParameterizedTest
- @MethodSource("filepathsAndConnectionsAndExpectedWaysOutThroughNodesSubset")
+ @MethodSource("filepathsAndExpectedWaysOutThroughNodesSubset")
void countWaysOutThroughNodesSubset(final String filename,
final long expectedWaysOut) throws IOException {
assertEquals(expectedWaysOut, Reactor.countWaysOutThroughNodesSubset(filename));
diff --git a/src/test/java/com/example/adventofcode/year2025/day12/ChristmasTreeFarmTest.java b/src/test/java/com/example/adventofcode/year2025/day12/ChristmasTreeFarmTest.java
new file mode 100644
index 0000000..a0ba45d
--- /dev/null
+++ b/src/test/java/com/example/adventofcode/year2025/day12/ChristmasTreeFarmTest.java
@@ -0,0 +1,27 @@
+package com.example.adventofcode.year2025.day12;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.IOException;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ChristmasTreeFarmTest {
+
+ private static Stream filepathsAndExpectedRegionsFittingAllPresents() {
+ return Stream.of(
+ //Arguments.of("AdventOfCodeData/2025/day12/example_input", 2),
+ Arguments.of("AdventOfCodeData/2025/day12/input", 474)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("filepathsAndExpectedRegionsFittingAllPresents")
+ void countRegionsFittingAllPresents(final String filename,
+ final long expectedRegionsFittingAllPresents) throws IOException {
+ assertEquals(expectedRegionsFittingAllPresents, ChristmasTreeFarm.countRegionsFittingAllPresents(filename));
+ }
+}
\ No newline at end of file