1
- import java .util .ArrayDeque ;
2
1
import java .util .ArrayList ;
3
- import java .util .Collections ;
4
- import java .util .Deque ;
5
2
import java .util .HashMap ;
6
3
import java .util .HashSet ;
7
4
import java .util .List ;
8
5
import java .util .Map ;
6
+ import java .util .Map .Entry ;
9
7
import java .util .Set ;
10
8
9
+ import com .github .pareronia .aoc .IterTools ;
11
10
import com .github .pareronia .aoc .RangeInclusive ;
11
+ import com .github .pareronia .aoc .StringOps ;
12
12
import com .github .pareronia .aoc .Utils ;
13
13
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 ;
16
15
17
- public class AoC2022_15 extends AoCBase {
16
+ public class AoC2022_15 extends SolutionBase < AoC2022_15 . Input , Integer , Long > {
18
17
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 ) {
23
19
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 );
34
20
}
35
21
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 );
38
24
}
39
25
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 );
42
28
}
43
29
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 ) {
48
51
continue ;
49
52
}
50
- ranges .add (sensor .xRangeAt (y ));
53
+ ranges .add (RangeInclusive .between (
54
+ s .getX () - md + dy , s .getX () + md - dy ));
51
55
}
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 ())
60
62
.sum ();
61
63
}
62
64
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 );
75
75
}
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 ();
77
88
}
78
89
79
90
@ 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 );
82
93
}
83
94
84
95
@ 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 ;
87
108
}
88
109
89
110
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 ();
99
112
}
100
113
101
- private static final List < String > TEST = splitLines ( """
114
+ private static final String TEST = """
102
115
Sensor at x=2, y=18: closest beacon is at x=-2, y=15
103
116
Sensor at x=9, y=16: closest beacon is at x=10, y=16
104
117
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 {
113
126
Sensor at x=16, y=7: closest beacon is at x=15, y=3
114
127
Sensor at x=14, y=3: closest beacon is at x=15, y=3
115
128
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
+ """ ;
162
130
}
0 commit comments