Skip to content

Commit 97e296f

Browse files
committed
AoC 2025 Day 5 - java
1 parent 277b785 commit 97e296f

File tree

3 files changed

+130
-59
lines changed

3 files changed

+130
-59
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
1010
| ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
1111
| python3 | [](src/main/python/AoC2025_01.py) | [](src/main/python/AoC2025_02.py) | [](src/main/python/AoC2025_03.py) | [](src/main/python/AoC2025_04.py) | [](src/main/python/AoC2025_05.py) | | | | | | | |
12-
| java | [](src/main/java/AoC2025_01.java) | [](src/main/java/AoC2025_02.java) | [](src/main/java/AoC2025_03.java) | [](src/main/java/AoC2025_04.java) | | | | | | | | |
12+
| java | [](src/main/java/AoC2025_01.java) | [](src/main/java/AoC2025_02.java) | [](src/main/java/AoC2025_03.java) | [](src/main/java/AoC2025_04.java) | [](src/main/java/AoC2025_05.java) | | | | | | | |
1313
| bash | [](src/main/bash/AoC2025_01.sh) | [](src/main/bash/AoC2025_02.sh) | [](src/main/bash/AoC2025_03.sh) | [](src/main/bash/AoC2025_04.sh) | [](src/main/bash/AoC2025_05.sh) | | | | | | | |
1414
<!-- @END:ImplementationsTable:2025@ -->
1515

src/main/java/AoC2025_05.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import com.github.pareronia.aoc.RangeInclusive;
2+
import com.github.pareronia.aoc.StringOps;
3+
import com.github.pareronia.aoc.StringOps.StringSplit;
4+
import com.github.pareronia.aoc.solution.Sample;
5+
import com.github.pareronia.aoc.solution.Samples;
6+
import com.github.pareronia.aoc.solution.SolutionBase;
7+
8+
import java.util.HashSet;
9+
import java.util.List;
10+
import java.util.Set;
11+
12+
@SuppressWarnings({"PMD.ClassNamingConventions", "PMD.NoPackage"})
13+
public final class AoC2025_05 extends SolutionBase<AoC2025_05.Database, Long, Long> {
14+
15+
private AoC2025_05(final boolean debug) {
16+
super(debug);
17+
}
18+
19+
public static AoC2025_05 create() {
20+
return new AoC2025_05(false);
21+
}
22+
23+
public static AoC2025_05 createDebug() {
24+
return new AoC2025_05(true);
25+
}
26+
27+
@Override
28+
protected Database parseInput(final List<String> inputs) {
29+
return Database.fromInput(inputs);
30+
}
31+
32+
@Override
33+
public Long solvePart1(final Database database) {
34+
return database.availableIds().stream()
35+
.filter(pid -> database.idRanges().stream().anyMatch(rng -> rng.contains(pid)))
36+
.count();
37+
}
38+
39+
@Override
40+
public Long solvePart2(final Database database) {
41+
return RangeInclusive.mergeRanges(database.idRanges()).stream()
42+
.mapToLong(rng -> rng.getMaximum() - rng.getMinimum() + 1)
43+
.sum();
44+
}
45+
46+
@Samples({
47+
@Sample(method = "part1", input = TEST, expected = "3"),
48+
@Sample(method = "part2", input = TEST, expected = "14"),
49+
})
50+
public static void main(final String[] args) throws Exception {
51+
create().run();
52+
}
53+
54+
private static final String TEST =
55+
"""
56+
3-5
57+
10-14
58+
16-20
59+
12-18
60+
61+
1
62+
5
63+
8
64+
11
65+
17
66+
32
67+
""";
68+
69+
record Database(Set<RangeInclusive<Long>> idRanges, List<Long> availableIds) {
70+
71+
public static Database fromInput(final List<String> inputs) {
72+
final List<List<String>> blocks = StringOps.toBlocks(inputs);
73+
final Set<RangeInclusive<Long>> idRanges = new HashSet<>();
74+
for (final String line : blocks.getFirst()) {
75+
final StringSplit split = StringOps.splitOnce(line, "-");
76+
idRanges.add(
77+
RangeInclusive.between(
78+
Long.parseLong(split.left()), Long.parseLong(split.right())));
79+
}
80+
final List<Long> availableIds = blocks.getLast().stream().map(Long::parseLong).toList();
81+
return new Database(idRanges, availableIds);
82+
}
83+
}
84+
}

src/main/java/com/github/pareronia/aoc/RangeInclusive.java

Lines changed: 45 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ public int compare(final Object obj1, final Object obj2) {
2323
private final T minimum;
2424
private final T maximum;
2525
private final Comparator<T> comparator;
26-
private String toString;
27-
28-
@SuppressWarnings({ "unchecked" })
26+
private String asString;
27+
28+
@SuppressWarnings({"unchecked", "PMD.AvoidLiteralsInIfCondition"})
2929
private RangeInclusive(final T element1, final T element2) {
3030
AssertUtils.assertTrue(
3131
element1 != null && element2 != null,
32-
() -> String.format("Elements in a range must not be null: element1=%s, element2=%s",
33-
element1, element2));
32+
() ->
33+
String.format(
34+
"Elements in a range must not be null: element1=%s, element2=%s",
35+
element1, element2));
3436
this.comparator = ComparableComparator.INSTANCE;
3537
if (comparator.compare(element1, element2) < 1) {
3638
this.minimum = element1;
@@ -45,18 +47,21 @@ public static <T> RangeInclusive<T> between(final T fromInclusive, final T toInc
4547
return new RangeInclusive<>(fromInclusive, toInclusive);
4648
}
4749

48-
public static List<RangeInclusive<Integer>> mergeRanges(
49-
final Collection<RangeInclusive<Integer>> ranges
50-
) {
51-
final var merged = new ArrayDeque<RangeInclusive<Integer>>();
50+
@SuppressWarnings({"PMD.UseExplicitTypes", "PMD.LawOfDemeter"})
51+
public static <T> List<RangeInclusive<T>> mergeRanges(
52+
final Collection<RangeInclusive<T>> ranges) {
53+
final var comparator = ComparableComparator.INSTANCE;
54+
final var merged = new ArrayDeque<RangeInclusive<T>>();
5255
final var sorted = new ArrayList<>(ranges);
53-
Collections.sort(sorted, (r1, r2) -> {
54-
final int first = Integer.compare(r1.getMinimum(), r2.getMinimum());
55-
if (first == 0) {
56-
return Integer.compare(r1.getMaximum(), r2.getMaximum());
57-
}
58-
return first;
59-
});
56+
Collections.sort(
57+
sorted,
58+
(r1, r2) -> {
59+
final int first = comparator.compare(r1.getMinimum(), r2.getMinimum());
60+
if (first == 0) {
61+
return comparator.compare(r1.getMaximum(), r2.getMaximum());
62+
}
63+
return first;
64+
});
6065
for (final var range : sorted) {
6166
if (merged.isEmpty()) {
6267
merged.addLast(range);
@@ -65,9 +70,11 @@ public static List<RangeInclusive<Integer>> mergeRanges(
6570
final var last = merged.peekLast();
6671
if (range.isOverlappedBy(last)) {
6772
merged.removeLast();
68-
merged.add(RangeInclusive.between(
69-
last.getMinimum(),
70-
Math.max(last.getMaximum(), range.getMaximum())));
73+
final T newMaximum =
74+
comparator.compare(last.getMaximum(), range.getMaximum()) > 0
75+
? last.getMaximum()
76+
: range.getMaximum();
77+
merged.add(between(last.getMinimum(), newMaximum));
7178
} else {
7279
merged.addLast(range);
7380
}
@@ -84,41 +91,27 @@ public T getMaximum() {
8491
}
8592

8693
public boolean isBefore(final T element) {
87-
if (element == null) {
88-
return false;
89-
}
90-
return comparator.compare(element, maximum) > 0;
94+
return element != null && comparator.compare(element, maximum) > 0;
9195
}
9296

9397
public boolean isAfter(final T element) {
94-
if (element == null) {
95-
return false;
96-
}
97-
return comparator.compare(element, minimum) < 0;
98+
return element != null && comparator.compare(element, minimum) < 0;
9899
}
99100

100101
public boolean contains(final T element) {
101-
if (element == null) {
102-
return false;
103-
}
104-
return comparator.compare(element, minimum) > -1
102+
return element != null
103+
&& comparator.compare(element, minimum) > -1
105104
&& comparator.compare(element, maximum) < 1;
106105
}
107106

108107
public boolean containsRange(final RangeInclusive<T> otherRange) {
109-
if (otherRange == null) {
110-
return false;
111-
}
112-
return contains(otherRange.minimum) && contains(otherRange.maximum);
108+
return otherRange != null && contains(otherRange.minimum) && contains(otherRange.maximum);
113109
}
114110

115111
public boolean isOverlappedBy(final RangeInclusive<T> otherRange) {
116-
if (otherRange == null) {
117-
return false;
118-
}
119-
return otherRange.contains(minimum)
120-
|| otherRange.contains(maximum)
121-
|| contains(otherRange.minimum);
112+
return otherRange != null && otherRange.contains(minimum)
113+
|| otherRange.contains(maximum)
114+
|| contains(otherRange.minimum);
122115
}
123116

124117
@Override
@@ -128,25 +121,19 @@ public int hashCode() {
128121

129122
@Override
130123
public boolean equals(final Object obj) {
131-
if (this == obj) {
132-
return true;
133-
}
134-
if (obj == null) {
135-
return false;
136-
}
137-
if (getClass() != obj.getClass()) {
138-
return false;
139-
}
140-
final RangeInclusive<?> other = (RangeInclusive<?>) obj;
141-
return Objects.equals(maximum, other.maximum)
142-
&& Objects.equals(minimum, other.minimum);
124+
return switch (obj) {
125+
case final RangeInclusive<?> other when other != null ->
126+
Objects.equals(maximum, other.maximum)
127+
&& Objects.equals(minimum, other.minimum);
128+
default -> false;
129+
};
143130
}
144-
131+
145132
@Override
146133
public String toString() {
147-
if (toString == null) {
148-
toString = String.format("[%s..%s]", minimum, maximum);
134+
if (asString == null) {
135+
asString = String.format("[%s..%s]", minimum, maximum);
149136
}
150-
return toString;
137+
return asString;
151138
}
152-
}
139+
}

0 commit comments

Comments
 (0)