Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AdventOfCodeData
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
<artifactId>jgrapht-core</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>tools.aqua</groupId>
<artifactId>z3-turnkey</artifactId>
<version>4.14.1</version>
</dependency>
</dependencies>

<build>
Expand Down
180 changes: 180 additions & 0 deletions src/main/java/com/example/adventofcode/year2025/day10/Factory.java
Original file line number Diff line number Diff line change
@@ -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<String> 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<List<Integer>> buttons = parseButtons(elements);

steps += countMinButtonPressesForIndicatorLights(targetLights, buttons);
}

return steps;
}

public static long countMinButtonPressesForJoltageLevelCounters(final String filename) throws IOException {
List<String> lines = readLines(filename);
long steps = 0;
for (String line : lines) {
String[] elements = line.split(" ");
List<List<Integer>> buttons = parseButtons(elements);
List<Integer> joltages = parseJoltages(elements);

steps += solveLinearProblem(buttons, joltages);
}

return steps;
}

private static List<List<Integer>> parseButtons(String[] elements) {
List<List<Integer>> 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<Integer> 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<List<Integer>> buttons) {
Deque<State> p = new ArrayDeque<>();
p.addLast(new State(parseInitialState(targetLights), 0));

while (!p.isEmpty()) {
State current = p.pollFirst();
for (List<Integer> 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<Integer> 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<List<Integer>> buttons, List<Integer> 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<List<Integer>> 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<List<Integer>> buttons, List<Integer> joltages, IntExpr[] buttonVariables, Context ctx, Optimize opt) {
for (int joltageNumber = 0; joltageNumber < joltages.size(); joltageNumber++) {
List<IntExpr> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<Integer> indexes){}

public static long countRegionsFittingAllPresents(final String filename) throws IOException {
List<String> lines = readLines(filename);
List<Area> 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<Area> parseAreas(List<String> lines) {
List<Area> 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<Integer> integerIndexes = Arrays.stream(indexes)
.map(Integer::valueOf)
.toList();

areas.add(new Area(new Dimensions(dimensionX, dimensionY), integerIndexes));
}
}
return areas;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,36 @@

class MovieTheaterTest {

private static Stream<Arguments> filepathsAndConnectionsAndExpectedLargestArea() {
private static Stream<Arguments> filepathsAndExpectedLargestArea() {
return Stream.of(
Arguments.of("AdventOfCodeData/2025/day09/example_input", 50),
Arguments.of("AdventOfCodeData/2025/day09/input", 4769758290L)
);
}

@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<Arguments> filepathsAndConnectionsAndExpectedLargestAreaInsideOfPolygon() {
private static Stream<Arguments> filepathsAndExpectedLargestAreaInsideOfPolygon() {
return Stream.of(
Arguments.of("AdventOfCodeData/2025/day09/example_input", 24),
Arguments.of("AdventOfCodeData/2025/day09/input", 1588990708L)
);
}

@ParameterizedTest
@MethodSource("filepathsAndConnectionsAndExpectedLargestAreaInsideOfPolygon")
@MethodSource("filepathsAndExpectedLargestAreaInsideOfPolygon")
void calculateLargestAreaInsideOfPolygon(final String filename,
final long expectedLargestArea) throws IOException {
assertEquals(expectedLargestArea, MovieTheater.calculateLargestAreaInsideOfPolygon(filename));
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Arguments> 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<Arguments> 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,29 @@

class ReactorTest {

private static Stream<Arguments> filepathsAndConnectionsAndExpectedWaysOut() {
private static Stream<Arguments> filepathsAndExpectedWaysOut() {
return Stream.of(
Arguments.of("AdventOfCodeData/2025/day11/example_input", 5),
Arguments.of("AdventOfCodeData/2025/day11/input", 615)
);
}

@ParameterizedTest
@MethodSource("filepathsAndConnectionsAndExpectedWaysOut")
@MethodSource("filepathsAndExpectedWaysOut")
void countWaysOut(final String filename,
final long expectedWaysOut) throws IOException {
assertEquals(expectedWaysOut, Reactor.countWaysOut(filename));
}

private static Stream<Arguments> filepathsAndConnectionsAndExpectedWaysOutThroughNodesSubset() {
private static Stream<Arguments> filepathsAndExpectedWaysOutThroughNodesSubset() {
return Stream.of(
Arguments.of("AdventOfCodeData/2025/day11/example_input2", 2),
Arguments.of("AdventOfCodeData/2025/day11/input", 303012373210128L)
);
}

@ParameterizedTest
@MethodSource("filepathsAndConnectionsAndExpectedWaysOutThroughNodesSubset")
@MethodSource("filepathsAndExpectedWaysOutThroughNodesSubset")
void countWaysOutThroughNodesSubset(final String filename,
final long expectedWaysOut) throws IOException {
assertEquals(expectedWaysOut, Reactor.countWaysOutThroughNodesSubset(filename));
Expand Down
Loading
Loading