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