Skip to content

Commit b89a743

Browse files
committed
AoC 2022 Day 15 - java - faster
1 parent cccc46c commit b89a743

File tree

4 files changed

+171
-177
lines changed

4 files changed

+171
-177
lines changed

src/main/java/AoC2022_15.java

Lines changed: 78 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,117 @@
1-
import java.util.ArrayDeque;
21
import java.util.ArrayList;
3-
import java.util.Collections;
4-
import java.util.Deque;
52
import java.util.HashMap;
63
import java.util.HashSet;
74
import java.util.List;
85
import java.util.Map;
6+
import java.util.Map.Entry;
97
import java.util.Set;
108

9+
import com.github.pareronia.aoc.IterTools;
1110
import com.github.pareronia.aoc.RangeInclusive;
11+
import com.github.pareronia.aoc.StringOps;
1212
import com.github.pareronia.aoc.Utils;
1313
import com.github.pareronia.aoc.geometry.Position;
14-
import com.github.pareronia.aocd.Aocd;
15-
import com.github.pareronia.aocd.Puzzle;
14+
import com.github.pareronia.aoc.solution.SolutionBase;
1615

17-
public class AoC2022_15 extends AoCBase {
16+
public class AoC2022_15 extends SolutionBase<AoC2022_15.Input, Integer, Long> {
1817

19-
private final Set<Sensor> sensors;
20-
private final Map<Integer, Set<Integer>> beacons;
21-
22-
private AoC2022_15(final List<String> input, final boolean debug) {
18+
private AoC2022_15(final boolean debug) {
2319
super(debug);
24-
this.sensors = new HashSet<>();
25-
this.beacons = new HashMap<>();
26-
for (final String line : input) {
27-
final int[] nums = Utils.integerNumbers(line);
28-
final var sensor = new Sensor(nums[0], nums[1], nums[2], nums[3]);
29-
this.sensors.add(sensor);
30-
this.beacons.computeIfAbsent(nums[3], k -> new HashSet<>()).add(nums[2]);
31-
}
32-
log(this.sensors);
33-
log(this.beacons);
3420
}
3521

36-
public static final AoC2022_15 create(final List<String> input) {
37-
return new AoC2022_15(input, false);
22+
public static final AoC2022_15 create() {
23+
return new AoC2022_15(false);
3824
}
3925

40-
public static final AoC2022_15 createDebug(final List<String> input) {
41-
return new AoC2022_15(input, true);
26+
public static final AoC2022_15 createDebug() {
27+
return new AoC2022_15(true);
4228
}
4329

44-
private Deque<RangeInclusive<Integer>> getRanges(final int y) {
45-
final var ranges = new HashSet<RangeInclusive<Integer>>();
46-
for (final var sensor : this.sensors) {
47-
if (Math.abs(sensor.y - y) > sensor.distanceToBeacon) {
30+
@Override
31+
protected Input parseInput(final List<String> inputs) {
32+
final Map<Position, Integer> sensors = new HashMap<>();
33+
final Set<Position> beacons = new HashSet<>();
34+
for (final String line : inputs) {
35+
final int[] nums = Utils.integerNumbers(line);
36+
final Position s = Position.of(nums[0], nums[1]);
37+
final Position b = Position.of(nums[2], nums[3]);
38+
sensors.put(s, s.manhattanDistance(b));
39+
beacons.add(b);
40+
}
41+
return new Input(sensors, beacons);
42+
}
43+
44+
private int solve1(final Input input, final int y) {
45+
final var ranges = new ArrayList<RangeInclusive<Integer>>();
46+
for (final Entry<Position, Integer> entry : input.sensors.entrySet()) {
47+
final Position s = entry.getKey();
48+
final int md = entry.getValue();
49+
final int dy = Math.abs(s.getY() - y);
50+
if (dy > md) {
4851
continue;
4952
}
50-
ranges.add(sensor.xRangeAt(y));
53+
ranges.add(RangeInclusive.between(
54+
s.getX() - md + dy, s.getX() + md - dy));
5155
}
52-
return RangeMerger.mergeRanges(ranges);
53-
}
54-
55-
private int solve1(final int y) {
56-
final Set<Integer> beaconsXs = this.beacons.get(y);
57-
return (int) getRanges(y).stream()
58-
.mapToLong(r -> r.getMaximum() - r.getMinimum() + 1
59-
- beaconsXs.stream().filter(r::contains).count())
56+
return (int) RangeInclusive.mergeRanges(ranges).stream()
57+
.mapToLong(r ->
58+
r.getMaximum() - r.getMinimum() + 1
59+
- input.beacons.stream()
60+
.filter(b -> b.getY() == y && r.contains(b.getX()))
61+
.count())
6062
.sum();
6163
}
6264

63-
private long solve2(final int max) {
64-
for (int y = max; y >= 0; y--) {
65-
final var ranges = getRanges(y);
66-
for (int x = 0; x <= max; x++) {
67-
for (final var merged : ranges) {
68-
if (merged.isAfter(x)) {
69-
log(Position.of(x, y));
70-
return x * 4_000_000L + y;
71-
}
72-
x = merged.getMaximum() + 1;
73-
}
74-
}
65+
private long solve2(final Map<Position, Integer> sensors, final int max) {
66+
final Set<Integer> acoeffs = new HashSet<>();
67+
final Set<Integer> bcoeffs = new HashSet<>();
68+
for (final Entry<Position, Integer> entry : sensors.entrySet()) {
69+
final Position s = entry.getKey();
70+
final int md = entry.getValue();
71+
acoeffs.add(s.getY() - s.getX() + md + 1);
72+
acoeffs.add(s.getY() - s.getX() - md - 1);
73+
bcoeffs.add(s.getX() + s.getY() + md + 1);
74+
bcoeffs.add(s.getX() + s.getY() - md - 1);
7575
}
76-
throw new IllegalStateException("Unsolvable");
76+
return IterTools.product(acoeffs, bcoeffs).stream()
77+
.filter(ab -> ab.first() < ab.second())
78+
.filter(ab -> (ab.second() - ab.first()) % 2 == 0)
79+
.map(ab -> Position.of(
80+
(ab.second() - ab.first()) / 2,
81+
(ab.first() + ab.second()) / 2))
82+
.filter(p -> 0 < p.getX() && p.getX() < max)
83+
.filter(p -> 0 < p.getY() && p.getY() < max)
84+
.filter(p -> sensors.keySet().stream().allMatch(
85+
s -> p.manhattanDistance(s) > sensors.get(s)))
86+
.mapToLong(p -> 4_000_000L * p.getX() + p.getY())
87+
.findFirst().orElseThrow();
7788
}
7889

7990
@Override
80-
public Integer solvePart1() {
81-
return solve1(2_000_000);
91+
public Integer solvePart1(final Input input) {
92+
return solve1(input, 2_000_000);
8293
}
8394

8495
@Override
85-
public Long solvePart2() {
86-
return solve2(4_000_000);
96+
public Long solvePart2(final Input input) {
97+
return solve2(input.sensors, 4_000_000);
98+
}
99+
100+
record Input(Map<Position, Integer> sensors, Set<Position> beacons) {}
101+
102+
@Override
103+
public void samples() {
104+
final AoC2022_15 test = AoC2022_15.createDebug();
105+
final Input input = test.parseInput(StringOps.splitLines(TEST));
106+
assert test.solve1(input, 10) == 26;
107+
assert test.solve2(input.sensors, 20) == 56_000_011L;
87108
}
88109

89110
public static void main(final String[] args) throws Exception {
90-
assert AoC2022_15.createDebug(TEST).solve1(10) == 26;
91-
assert AoC2022_15.createDebug(TEST).solve2(20) == 56_000_011L;
92-
93-
final Puzzle puzzle = Aocd.puzzle(2022, 15);
94-
final List<String> inputData = puzzle.getInputData();
95-
puzzle.check(
96-
() -> lap("Part 1", AoC2022_15.create(inputData)::solvePart1),
97-
() -> lap("Part 2", AoC2022_15.create(inputData)::solvePart2)
98-
);
111+
AoC2022_15.create().run();
99112
}
100113

101-
private static final List<String> TEST = splitLines("""
114+
private static final String TEST = """
102115
Sensor at x=2, y=18: closest beacon is at x=-2, y=15
103116
Sensor at x=9, y=16: closest beacon is at x=10, y=16
104117
Sensor at x=13, y=2: closest beacon is at x=15, y=3
@@ -113,50 +126,5 @@ public static void main(final String[] args) throws Exception {
113126
Sensor at x=16, y=7: closest beacon is at x=15, y=3
114127
Sensor at x=14, y=3: closest beacon is at x=15, y=3
115128
Sensor at x=20, y=1: closest beacon is at x=15, y=3
116-
""");
117-
118-
private static final record Sensor(int x, int y, int distanceToBeacon) {
119-
public Sensor(final int x, final int y, final int beaconX, final int beaconY) {
120-
this(x, y, Math.abs(beaconX - x) + Math.abs(beaconY - y));
121-
}
122-
123-
public RangeInclusive<Integer> xRangeAt(final int y) {
124-
final int dy = Math.abs(this.y - y);
125-
assert dy <= distanceToBeacon;
126-
return RangeInclusive.between(
127-
x - distanceToBeacon + dy,
128-
x + distanceToBeacon - dy);
129-
}
130-
}
131-
132-
static final class RangeMerger {
133-
134-
public static Deque<RangeInclusive<Integer>> mergeRanges(final Set<RangeInclusive<Integer>> ranges) {
135-
final var m = new ArrayDeque<RangeInclusive<Integer>>();
136-
final var sorted = new ArrayList<>(ranges);
137-
Collections.sort(sorted, (r1, r2) -> {
138-
final int first = Integer.compare(r1.getMinimum(), r2.getMinimum());
139-
if (first == 0) {
140-
return Integer.compare(r1.getMaximum(), r2.getMaximum());
141-
}
142-
return first;
143-
});
144-
for (final var range : sorted) {
145-
if (m.isEmpty()) {
146-
m.addLast(range);
147-
continue;
148-
}
149-
final var last = m.peekLast();
150-
if (range.isOverlappedBy(last)) {
151-
m.removeLast();
152-
m.add(RangeInclusive.between(
153-
last.getMinimum(),
154-
Math.max(last.getMaximum(), range.getMaximum())));
155-
} else {
156-
m.addLast(range);
157-
}
158-
}
159-
return m;
160-
}
161-
}
129+
""";
162130
}

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package com.github.pareronia.aoc;
22

3+
import java.util.ArrayDeque;
4+
import java.util.ArrayList;
5+
import java.util.Collection;
6+
import java.util.Collections;
37
import java.util.Comparator;
8+
import java.util.List;
49
import java.util.Objects;
510

611
public final class RangeInclusive<T> {
@@ -40,6 +45,36 @@ public static <T> RangeInclusive<T> between(final T fromInclusive, final T toInc
4045
return new RangeInclusive<>(fromInclusive, toInclusive);
4146
}
4247

48+
public static List<RangeInclusive<Integer>> mergeRanges(
49+
final Collection<RangeInclusive<Integer>> ranges
50+
) {
51+
final var merged = new ArrayDeque<RangeInclusive<Integer>>();
52+
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+
});
60+
for (final var range : sorted) {
61+
if (merged.isEmpty()) {
62+
merged.addLast(range);
63+
continue;
64+
}
65+
final var last = merged.peekLast();
66+
if (range.isOverlappedBy(last)) {
67+
merged.removeLast();
68+
merged.add(RangeInclusive.between(
69+
last.getMinimum(),
70+
Math.max(last.getMaximum(), range.getMaximum())));
71+
} else {
72+
merged.addLast(range);
73+
}
74+
}
75+
return merged.stream().toList();
76+
}
77+
4378
public T getMinimum() {
4479
return minimum;
4580
}

src/test/java/AoC2022_15TestCase.java

Lines changed: 0 additions & 66 deletions
This file was deleted.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy