Skip to content

Commit 759bbb3

Browse files
committed
AoC 2025 Day 10 Part 2 - java [no solver]
1 parent 3a28bc4 commit 759bbb3

File tree

4 files changed

+141
-90
lines changed

4 files changed

+141
-90
lines changed

src/main/java/AoC2025_10.java

Lines changed: 97 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
import static com.github.pareronia.aoc.IntegerSequence.Range.range;
2-
import static com.github.pareronia.aoc.Utils.toAString;
3-
import static com.github.pareronia.aoc.itertools.IterTools.combinations;
42

5-
import static java.util.stream.Collectors.joining;
3+
import static java.util.stream.Collectors.toMap;
64

7-
import com.github.pareronia.aoc.IntegerSequence.Range;
85
import com.github.pareronia.aoc.Utils;
6+
import com.github.pareronia.aoc.itertools.IterTools;
97
import com.github.pareronia.aoc.solution.Sample;
108
import com.github.pareronia.aoc.solution.Samples;
119
import com.github.pareronia.aoc.solution.SolutionBase;
1210

13-
import org.ojalgo.optimisation.Expression;
14-
import org.ojalgo.optimisation.ExpressionsBasedModel;
15-
import org.ojalgo.optimisation.Optimisation.Result;
16-
import org.ojalgo.optimisation.Variable;
17-
1811
import java.util.ArrayList;
1912
import java.util.Arrays;
13+
import java.util.HashMap;
2014
import java.util.List;
21-
import java.util.function.ToLongFunction;
15+
import java.util.Map;
16+
import java.util.Map.Entry;
2217

2318
@SuppressWarnings({"PMD.ClassNamingConventions", "PMD.NoPackage"})
24-
public final class AoC2025_10 extends SolutionBase<List<AoC2025_10.Machine>, Long, Long> {
19+
public final class AoC2025_10 extends SolutionBase<List<AoC2025_10.Machine>, Integer, Integer> {
2520

2621
private AoC2025_10(final boolean debug) {
2722
super(debug);
@@ -37,63 +32,17 @@ public static AoC2025_10 createDebug() {
3732

3833
@Override
3934
protected List<Machine> parseInput(final List<String> inputs) {
40-
return inputs.stream().map(Machine::fromInput).toList();
35+
return inputs.parallelStream().map(Machine::fromInput).toList();
4136
}
4237

4338
@Override
44-
public Long solvePart1(final List<Machine> machines) {
45-
final ToLongFunction<Machine> buttonPressesForLights =
46-
machine ->
47-
Range.between(1, machine.presses().size())
48-
.intStream()
49-
.filter(
50-
n ->
51-
combinations(machine.presses().size(), n).stream()
52-
.anyMatch(machine::pressesMatchLights))
53-
.findFirst()
54-
.orElseThrow();
55-
return machines.stream().mapToLong(buttonPressesForLights).sum();
39+
public Integer solvePart1(final List<Machine> machines) {
40+
return machines.stream().mapToInt(Machine::buttonPressesForLights).sum();
5641
}
5742

5843
@Override
59-
@SuppressWarnings({
60-
"PMD.AssignmentInOperand",
61-
"PMD.AvoidInstantiatingObjectsInLoops",
62-
"PMD.AvoidLiteralsInIfCondition"
63-
})
64-
public Long solvePart2(final List<Machine> machines) {
65-
long ans = 0L;
66-
for (final Machine machine : machines) {
67-
final int vars = machine.presses().size();
68-
final int targets = machine.joltages().length;
69-
final double[][] a = new double[targets][vars];
70-
range(vars).stream()
71-
.forEach(
72-
c -> Arrays.stream(machine.presses().get(c)).forEach(p -> a[p][c] = 1));
73-
final ExpressionsBasedModel model = new ExpressionsBasedModel();
74-
final List<Variable> variables =
75-
range(vars).stream()
76-
.map(c -> model.newVariable("x" + c).lower(0).integer().weight(1))
77-
.toList();
78-
range(targets).stream()
79-
.forEach(
80-
r -> {
81-
final Expression constraint =
82-
model.newExpression("c" + r).level(machine.joltages()[r]);
83-
for (int c = 0; c < vars; c++) {
84-
if (a[r][c] != 0d) {
85-
constraint.set(variables.get(c), a[r][c]);
86-
}
87-
}
88-
});
89-
final Result result = model.minimise();
90-
ans +=
91-
range(vars)
92-
.intStream()
93-
.mapToLong(c -> Math.round(result.get(c).doubleValue()))
94-
.sum();
95-
}
96-
return ans;
44+
public Integer solvePart2(final List<Machine> machines) {
45+
return machines.stream().mapToInt(Machine::buttonPressesForJoltages).sum();
9746
}
9847

9948
@Samples({
@@ -111,14 +60,21 @@ public static void main(final String[] args) throws Exception {
11160
[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}
11261
""";
11362

114-
record Machine(String lights, int[] joltages, List<int[]> presses) {
63+
private static List<Integer> parity(final List<Integer> lst) {
64+
return lst.stream().map(i -> i % 2).toList();
65+
}
66+
67+
record Machine(
68+
List<Integer> lights,
69+
List<Integer> joltages,
70+
Map<List<Integer>, Map<List<Integer>, Integer>> patterns) {
11571

11672
public static Machine fromInput(final String input) {
11773
final String[] splits = input.split(" ");
118-
final String lights =
74+
final List<Integer> lights =
11975
Utils.asCharacterStream(splits[0].substring(1, splits[0].length() - 1))
120-
.map(ch -> (char) ('0' + ".#".indexOf(ch)))
121-
.collect(toAString());
76+
.map(ch -> ch == '#' ? 1 : 0)
77+
.toList();
12278
final List<int[]> presses = new ArrayList<>();
12379
for (int i = 1; i < splits.length - 1; i++) {
12480
presses.add(
@@ -127,35 +83,88 @@ public static Machine fromInput(final String input) {
12783
.toArray());
12884
}
12985
final String sj = splits[splits.length - 1];
130-
final int[] joltages =
86+
final List<Integer> joltages =
13187
Arrays.stream(sj.substring(1, sj.length() - 1).split(","))
132-
.mapToInt(Integer::parseInt)
133-
.toArray();
134-
return new Machine(lights, joltages, presses);
88+
.map(Integer::valueOf)
89+
.toList();
90+
return new Machine(lights, joltages, createPatterns(lights, joltages, presses));
13591
}
13692

137-
private int pressAsBinary(final int idx) {
138-
return Arrays.stream(this.presses.get(idx))
139-
.map(i -> 1 << (this.lights.length() - 1 - i))
140-
.sum();
93+
private static Map<List<Integer>, Map<List<Integer>, Integer>> createPatterns(
94+
final List<Integer> lights,
95+
final List<Integer> joltages,
96+
final List<int[]> presses) {
97+
final int numButtons = presses.size();
98+
final int numVariables = joltages.size();
99+
assert lights.size() == numVariables;
100+
final Map<List<Integer>, Map<List<Integer>, Integer>> patterns =
101+
IterTools.product(List.of(0, 1).iterator(), numVariables).stream()
102+
.collect(toMap(p -> p, p -> new HashMap<>()));
103+
for (int n = 0; n <= numButtons; n++) {
104+
final Iterable<int[]> combos = IterTools.combinations(numButtons, n).iterable();
105+
for (final int[] combo : combos) {
106+
final int[] pattern = new int[joltages.size()];
107+
for (final int idx : combo) {
108+
for (final int p : presses.get(idx)) {
109+
pattern[p] += 1;
110+
}
111+
}
112+
final List<Integer> key = Arrays.stream(pattern).boxed().toList();
113+
final List<Integer> parityPattern = parity(key);
114+
if (!patterns.get(parityPattern).containsKey(key)) {
115+
patterns.get(parityPattern).put(key, n);
116+
}
117+
}
118+
}
119+
return patterns;
141120
}
142121

143-
public boolean pressesMatchLights(final int... idxs) {
144-
return Arrays.stream(idxs).map(this::pressAsBinary).reduce(0, (acc, c) -> acc ^ c)
145-
== Integer.parseInt(this.lights, 2);
122+
public int buttonPressesForLights() {
123+
return this.patterns.get(this.lights()).values().stream()
124+
.mapToInt(Integer::intValue)
125+
.min()
126+
.orElseThrow();
146127
}
147128

148-
@Override
149-
public String toString() {
150-
final StringBuilder builder = new StringBuilder(100);
151-
builder.append("Machine [lights=")
152-
.append(lights)
153-
.append(", joltages=")
154-
.append(Arrays.toString(joltages))
155-
.append(", presses=[")
156-
.append(presses.stream().map(Arrays::toString).collect(joining(", ")))
157-
.append("]]");
158-
return builder.toString();
129+
public int buttonPressesForJoltages() {
130+
class DFS {
131+
private final Map<List<Integer>, Integer> cache;
132+
133+
public DFS() {
134+
this.cache = new HashMap<>();
135+
}
136+
137+
public int dfs(final List<Integer> joltages) {
138+
if (this.cache.containsKey(joltages)) {
139+
return this.cache.get(joltages);
140+
}
141+
int ans;
142+
if (joltages.stream().allMatch(j -> j == 0)) {
143+
ans = 0;
144+
} else {
145+
ans = 10_000_000;
146+
final List<Integer> parityPattern = parity(joltages);
147+
for (final Entry<List<Integer>, Integer> e :
148+
Machine.this.patterns.get(parityPattern).entrySet()) {
149+
final List<Integer> pattern = e.getKey();
150+
if (!range(pattern.size())
151+
.intStream()
152+
.allMatch(i -> pattern.get(i) <= joltages.get(i))) {
153+
continue;
154+
}
155+
final List<Integer> newJoltages =
156+
range(pattern.size()).stream()
157+
.map(i -> (joltages.get(i) - pattern.get(i)) / 2)
158+
.toList();
159+
ans = Math.min(ans, 2 * dfs(newJoltages) + e.getValue());
160+
}
161+
}
162+
this.cache.put(joltages, ans);
163+
return ans;
164+
}
165+
}
166+
167+
return new DFS().dfs(this.joltages());
159168
}
160169
}
161170
}

src/main/java/com/github/pareronia/aoc/itertools/IterTools.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
@SuppressWarnings({"PMD.CouplingBetweenObjects", "PMD.TooManyMethods"})
1616
public final class IterTools {
1717

18-
private IterTools() {}
18+
private IterTools() {}
1919

2020
// TODO potentially huge storage cost -> make iterative version
2121
public static <T> Stream<List<T>> permutations(final Iterable<T> iterable) {
@@ -95,6 +95,12 @@ public static <T, U> IterToolsIterator<ProductPair<T, U>> product(
9595
return product(first.iterator(), second.iterator());
9696
}
9797

98+
public static <T> IterToolsIterator<List<T>> product(
99+
final Iterator<T> iterator, final int repeat) {
100+
101+
return asIterToolsIterator(Product.product(iterator, repeat));
102+
}
103+
98104
public static <T, U> IterToolsIterator<ProductPair<T, U>> product(
99105
final Iterator<T> first, final Iterator<U> second) {
100106

src/main/java/com/github/pareronia/aoc/itertools/Product.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.github.pareronia.aoc.Utils;
44

5+
import java.util.ArrayList;
56
import java.util.Iterator;
67
import java.util.List;
78

@@ -28,4 +29,24 @@ public ProductPair<T, U> next() {
2829
}
2930
};
3031
}
32+
33+
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
34+
public static <T> Iterator<List<T>> product(final Iterator<T> iterator, final int repeat) {
35+
final List<T> items = Utils.stream(iterator).toList();
36+
List<List<T>> ans = new ArrayList<>();
37+
ans.add(List.of());
38+
for (int i = 0; i < repeat; i++) {
39+
final List<List<T>> newAns = new ArrayList<>();
40+
for (final T item : items) {
41+
for (final List<T> tmp : ans) {
42+
final List<T> lst = new ArrayList<>();
43+
lst.addAll(tmp);
44+
lst.add(item);
45+
newAns.add(lst);
46+
}
47+
}
48+
ans = newAns;
49+
}
50+
return ans.iterator();
51+
}
3152
}

src/test/java/com/github/pareronia/aoc/itertools/IterToolsTestCase.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,23 @@
1515

1616
public class IterToolsTestCase {
1717

18+
@Test
19+
public void product() {
20+
final List<List<Integer>> ans = IterTools.product(List.of(0, 1).iterator(), 3).stream().toList();
21+
assertThat(ans).containsExactlyInAnyOrder(
22+
List.of(0, 0, 0),
23+
List.of(0, 0, 1),
24+
List.of(0, 1, 0),
25+
List.of(0, 1, 1),
26+
List.of(1, 0, 0),
27+
List.of(1, 0, 1),
28+
List.of(1, 1, 0),
29+
List.of(1, 1, 1)
30+
);
31+
}
32+
1833
@Test
19-
public void product() {
34+
public void productPair() {
2035
final Iterator<ProductPair<Integer, Integer>> product1
2136
= IterTools.product(List.of(0, 1), Set.of(0, 1));
2237
final List<ProductPair<Integer, Integer>> ans = new ArrayList<>();

0 commit comments

Comments
 (0)