1
1
import static com .github .pareronia .aoc .AssertUtils .assertFalse ;
2
2
import static com .github .pareronia .aoc .AssertUtils .assertTrue ;
3
+ import static com .github .pareronia .aoc .StringOps .splitLines ;
3
4
import static java .util .Comparator .comparing ;
4
5
import static java .util .stream .Collectors .joining ;
5
6
import static java .util .stream .Collectors .summingInt ;
13
14
import java .util .List ;
14
15
import java .util .Set ;
15
16
import java .util .function .Function ;
16
- import java .util .regex .Pattern ;
17
17
import java .util .stream .Stream ;
18
18
19
19
import com .github .pareronia .aoc .geometry .Direction ;
20
20
import com .github .pareronia .aoc .geometry .Point ;
21
21
import com .github .pareronia .aoc .geometry .Position ;
22
- import com .github .pareronia .aocd .Aocd ;
23
- import com .github .pareronia .aocd .Puzzle ;
22
+ import com .github .pareronia .aoc .solution .Sample ;
23
+ import com .github .pareronia .aoc .solution .Samples ;
24
+ import com .github .pareronia .aoc .solution .SolutionBase ;
24
25
25
- public class AoC2016_22 extends AoCBase {
26
+ public class AoC2016_22
27
+ extends SolutionBase <AoC2016_22 .Cluster , Integer , Integer > {
26
28
27
- private static final Pattern REGEX = Pattern .compile (
28
- "^\\ /dev\\ /grid\\ /node-x([0-9]+)-y([0-9]+)\\ s+[0-9]+T" +
29
- "\\ s+([0-9]+)T\\ s+([0-9]+)T\\ s+[0-9]+%$" );
30
-
31
- private final List <Node > nodes ;
32
-
33
- private AoC2016_22 (final List <String > input , final boolean debug ) {
29
+ private AoC2016_22 (final boolean debug ) {
34
30
super (debug );
35
- this .nodes = input .stream ()
36
- .skip (2 )
37
- .flatMap (s -> REGEX .matcher (s ).results ())
38
- .map (m -> {
39
- final Integer x = Integer .valueOf (m .group (1 ));
40
- final Integer y = Integer .valueOf (m .group (2 ));
41
- final Integer used = Integer .valueOf (m .group (3 ));
42
- final Integer available = Integer .valueOf (m .group (4 ));
43
- return new Node (x , y , used , available );
44
- })
45
- .collect (toList ());
46
31
}
47
32
48
- public static AoC2016_22 create (final List < String > input ) {
49
- return new AoC2016_22 (input , false );
33
+ public static AoC2016_22 create () {
34
+ return new AoC2016_22 (false );
50
35
}
51
36
52
- public static AoC2016_22 createDebug (final List <String > input ) {
53
- return new AoC2016_22 (input , true );
54
- }
55
-
56
- private Set <Node > getUnusableNodes () {
57
- final Integer maxAvailable = this .nodes .stream ()
58
- .max (comparing (Node ::available ))
59
- .map (Node ::available ).orElseThrow ();
60
- return this .nodes .stream ()
61
- .filter (n -> n .used () > maxAvailable )
62
- .collect (toSet ());
63
- }
64
-
65
- private Node getEmptyNode () {
66
- final List <Node > emptyNodes = nodes .stream ()
67
- .filter (n -> n .used () == 0 )
68
- .collect (toList ());
69
- assertTrue (emptyNodes .size () == 1 , () -> "Expected 1 empty node" );
70
- return emptyNodes .get (0 );
71
- }
72
-
73
- private Integer getMaxX () {
74
- return this .nodes .stream ()
75
- .max (comparing (Node ::x ))
76
- .map (Node ::x ).orElseThrow ();
77
- }
78
-
79
- private Integer getMaxY () {
80
- return this .nodes .stream ()
81
- .max (comparing (Node ::y ))
82
- .map (Node ::y ).orElseThrow ();
37
+ public static AoC2016_22 createDebug () {
38
+ return new AoC2016_22 (true );
83
39
}
84
-
85
- private Node getGoalNode () {
86
- return nodes .stream ()
87
- .filter (n -> n .x () == getMaxX () && n .y () == 0 )
88
- .findFirst ().orElseThrow ();
89
- }
90
-
91
- private Node getAccessibleNode () {
92
- return nodes .stream ()
93
- .filter (n -> n .x () == 0 && n .y () == 0 )
94
- .findFirst ().orElseThrow ();
40
+
41
+ @ Override
42
+ protected Cluster parseInput (final List <String > inputs ) {
43
+ return new Cluster (inputs .stream ()
44
+ .skip (2 )
45
+ .map (Node ::fromInput )
46
+ .collect (toList ()));
95
47
}
96
48
97
49
@ Override
98
- public Integer solvePart1 () {
99
- return (int ) this .nodes .stream ()
50
+ public Integer solvePart1 (final Cluster cluster ) {
51
+ return (int ) cluster .nodes .stream ()
100
52
.filter (Node ::isNotEmpty )
101
- .flatMap (a -> this .nodes .stream ()
53
+ .flatMap (a -> cluster .nodes .stream ()
102
54
.filter (b -> !a .equals (b ))
103
55
.filter (b -> a .used () <= b .available ()))
104
56
.count ();
105
57
}
106
58
107
- private void visualize () {
108
- final Integer maxX = getMaxX ();
109
- final Integer maxY = getMaxY ();
110
- final List <Node > sorted = this .nodes .stream ()
59
+ private void visualize (final Cluster cluster ) {
60
+ final Integer maxX = cluster . getMaxX ();
61
+ final Integer maxY = cluster . getMaxY ();
62
+ final List <Node > sorted = cluster .nodes .stream ()
111
63
.sorted (comparing (n -> n .x () * maxY + n .y ()))
112
64
.collect (toList ());
113
65
final List <List <Node >> grid = Stream .iterate (0 , i -> i <= maxX , i -> i + 1 )
@@ -116,10 +68,10 @@ private void visualize() {
116
68
.takeWhile (n -> n .x () == i )
117
69
.collect (toList ()))
118
70
.collect (toList ());
119
- final Set <Node > unusableNodes = getUnusableNodes ();
120
- final Node emptyNode = getEmptyNode ();
121
- final Node goalNode = getGoalNode ();
122
- final Node accessibleNode = getAccessibleNode ();
71
+ final Set <Node > unusableNodes = cluster . getUnusableNodes ();
72
+ final Node emptyNode = cluster . getEmptyNode ();
73
+ final Node goalNode = cluster . getGoalNode ();
74
+ final Node accessibleNode = cluster . getAccessibleNode ();
123
75
for (final List <Node > row : grid ) {
124
76
final String line = row .stream ()
125
77
.map (n -> {
@@ -155,16 +107,16 @@ private Function<Path, Boolean> stopAt(
155
107
};
156
108
}
157
109
158
- private Integer solve2 () {
159
- visualize ();
160
- final Set <Position > unusableNodes = getUnusableNodes ().stream ()
110
+ private Integer solve2 (final Cluster cluster ) {
111
+ visualize (cluster );
112
+ final Set <Position > unusableNodes = cluster . getUnusableNodes ().stream ()
161
113
.map (this ::toPosition )
162
114
.collect (toSet ());
163
- final Position emptyNode = toPosition (getEmptyNode ());
164
- final Position accessibleNode = toPosition (getAccessibleNode ());
165
- final Integer maxX = getMaxX ();
166
- final Integer maxY = getMaxY ();
167
- final Position goalNode = toPosition (getGoalNode ());
115
+ final Position emptyNode = toPosition (cluster . getEmptyNode ());
116
+ final Position accessibleNode = toPosition (cluster . getAccessibleNode ());
117
+ final Integer maxX = cluster . getMaxX ();
118
+ final Integer maxY = cluster . getMaxY ();
119
+ final Position goalNode = toPosition (cluster . getGoalNode ());
168
120
log ("Unusable: " + unusableNodes );
169
121
log ("Empty: " + emptyNode );
170
122
log ("Accessible: " + accessibleNode );
@@ -191,9 +143,9 @@ private Integer solve2() {
191
143
return length + 1 ;
192
144
}
193
145
194
- private Integer solve2Cheat () {
195
- visualize ();
196
- final Set <Node > unusableNodes = getUnusableNodes ();
146
+ private Integer solve2Cheat (final Cluster cluster ) {
147
+ visualize (cluster );
148
+ final Set <Node > unusableNodes = cluster . getUnusableNodes ();
197
149
log (unusableNodes );
198
150
final Set <Integer > holeYs = unusableNodes .stream ()
199
151
.map (Node ::y )
@@ -206,52 +158,53 @@ private Integer solve2Cheat() {
206
158
assertFalse (unusableNodes .stream ()
207
159
.max (comparing (Node ::x ))
208
160
.map (Node ::x )
209
- .orElseThrow () != getMaxX (),
161
+ .orElseThrow () != cluster . getMaxX (),
210
162
() -> "Expected unusable row to touch side" );
211
163
final Integer holeX = unusableNodes .stream ()
212
164
.min (comparing (Node ::x ))
213
165
.map (Node ::x )
214
166
.orElseThrow ();
215
167
final Position hole = Position .of (holeX - 1 , holeY );
216
- final Position emptyNode = toPosition (getEmptyNode ());
168
+ final Position emptyNode = toPosition (cluster . getEmptyNode ());
217
169
final int part1 = emptyNode .manhattanDistance (hole );
218
- final Position goalNode = toPosition (getGoalNode ());
170
+ final Position goalNode = toPosition (cluster . getGoalNode ());
219
171
final int part2 = hole .manhattanDistance (
220
172
Position .of (goalNode .getX () - 1 , goalNode .getY ()));
221
173
final int part3 = 5 * (goalNode .getX () - 1 );
222
174
return part1 + part2 + part3 + 1 ;
223
175
}
224
176
225
177
@ Override
226
- public Integer solvePart2 () {
227
- return solve2Cheat ();
178
+ public Integer solvePart2 (final Cluster cluster ) {
179
+ return solve2Cheat (cluster );
228
180
}
229
-
230
- public static void main (final String [] args ) throws Exception {
231
- assert AoC2016_22 .createDebug (TEST ).solvePart1 () == 7 ;
232
- assert AoC2016_22 .createDebug (TEST ).solve2 () == 7 ;
233
181
234
- final Puzzle puzzle = Aocd .puzzle (2016 , 22 );
235
- final List <String > inputData = puzzle .getInputData ();
236
- puzzle .check (
237
- () -> lap ("Part 1" , AoC2016_22 .createDebug (inputData )::solvePart1 ),
238
- () -> lap ("Part 2" , AoC2016_22 .createDebug (inputData )::solvePart2 )
239
- );
182
+ @ Override
183
+ @ Samples ({
184
+ @ Sample (method = "part1" , input = TEST , expected = "7" )
185
+ })
186
+ public void samples () {
187
+ final AoC2016_22 test = AoC2016_22 .createDebug ();
188
+ assert test .solve2 (test .parseInput (splitLines (TEST ))) == 7 ;
189
+ }
190
+
191
+ public static void main (final String [] args ) throws Exception {
192
+ AoC2016_22 .createDebug ().run ();
240
193
}
241
194
242
- private static final List < String > TEST = splitLines (
243
- " root@ebhq-gridcenter# df -h\r \n " +
244
- " Filesystem Size Used Avail Use%\r \n " +
245
- " /dev/grid/node-x0-y0 10T 8T 2T 80%\r \n " +
246
- " /dev/grid/node-x0-y1 11T 6T 5T 54%\r \n " +
247
- " /dev/grid/node-x0-y2 32T 28T 4T 87%\r \n " +
248
- " /dev/grid/node-x1-y0 9T 7T 2T 77%\r \n " +
249
- " /dev/grid/node-x1-y1 8T 0T 8T 0%\r \n " +
250
- " /dev/grid/node-x1-y2 11T 7T 4T 63%\r \n " +
251
- " /dev/grid/node-x2-y0 10T 6T 4T 60%\r \n " +
252
- " /dev/grid/node-x2-y1 9T 8T 1T 88%\r \n " +
253
- " /dev/grid/node-x2-y2 9T 6T 3T 66%"
254
- ) ;
195
+ private static final String TEST = """
196
+ root@ebhq-gridcenter# df -h\r
197
+ Filesystem Size Used Avail Use%\r
198
+ /dev/grid/node-x0-y0 10T 8T 2T 80%\r
199
+ /dev/grid/node-x0-y1 11T 6T 5T 54%\r
200
+ /dev/grid/node-x0-y2 32T 28T 4T 87%\r
201
+ /dev/grid/node-x1-y0 9T 7T 2T 77%\r
202
+ /dev/grid/node-x1-y1 8T 0T 8T 0%\r
203
+ /dev/grid/node-x1-y2 11T 7T 4T 63%\r
204
+ /dev/grid/node-x2-y0 10T 6T 4T 60%\r
205
+ /dev/grid/node-x2-y1 9T 8T 1T 88%\r
206
+ /dev/grid/node-x2-y2 9T 6T 3T 66%
207
+ """ ;
255
208
256
209
private static final class PathFinder {
257
210
private final Position start ;
@@ -315,8 +268,63 @@ public boolean isAt(final Position position) {
315
268
}
316
269
317
270
record Node (int x , int y , int used , int available ) {
271
+ public static Node fromInput (final String line ) {
272
+ final String [] splits = line .split ("\\ s+" );
273
+ final String [] xy = splits [0 ].split ("-" );
274
+ return new Node (
275
+ Integer .parseInt (xy [1 ].substring (1 )),
276
+ Integer .parseInt (xy [2 ].substring (1 )),
277
+ Integer .parseInt (splits [2 ].substring (0 , splits [2 ].length () - 1 )),
278
+ Integer .parseInt (splits [3 ].substring (0 , splits [3 ].length () - 1 ))
279
+ );
280
+ }
281
+
318
282
public boolean isNotEmpty () {
319
283
return this .used != 0 ;
320
284
}
321
285
}
286
+
287
+ record Cluster (List <Node > nodes ) {
288
+
289
+ public Set <Node > getUnusableNodes () {
290
+ final Integer maxAvailable = this .nodes .stream ()
291
+ .max (comparing (Node ::available ))
292
+ .map (Node ::available ).orElseThrow ();
293
+ return this .nodes .stream ()
294
+ .filter (n -> n .used () > maxAvailable )
295
+ .collect (toSet ());
296
+ }
297
+
298
+ public Node getEmptyNode () {
299
+ final List <Node > emptyNodes = this .nodes .stream ()
300
+ .filter (n -> n .used () == 0 )
301
+ .collect (toList ());
302
+ assertTrue (emptyNodes .size () == 1 , () -> "Expected 1 empty node" );
303
+ return emptyNodes .get (0 );
304
+ }
305
+
306
+ public Integer getMaxX () {
307
+ return this .nodes .stream ()
308
+ .max (comparing (Node ::x ))
309
+ .map (Node ::x ).orElseThrow ();
310
+ }
311
+
312
+ public Integer getMaxY () {
313
+ return this .nodes .stream ()
314
+ .max (comparing (Node ::y ))
315
+ .map (Node ::y ).orElseThrow ();
316
+ }
317
+
318
+ public Node getGoalNode () {
319
+ return this .nodes .stream ()
320
+ .filter (n -> n .x () == getMaxX () && n .y () == 0 )
321
+ .findFirst ().orElseThrow ();
322
+ }
323
+
324
+ public Node getAccessibleNode () {
325
+ return this .nodes .stream ()
326
+ .filter (n -> n .x () == 0 && n .y () == 0 )
327
+ .findFirst ().orElseThrow ();
328
+ }
329
+ }
322
330
}
0 commit comments