Skip to content

Commit ded6072

Browse files
committed
AoC 2016 Day 22 - java - refactor
1 parent 884fe34 commit ded6072

File tree

1 file changed

+124
-116
lines changed

1 file changed

+124
-116
lines changed

src/main/java/AoC2016_22.java

Lines changed: 124 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import static com.github.pareronia.aoc.AssertUtils.assertFalse;
22
import static com.github.pareronia.aoc.AssertUtils.assertTrue;
3+
import static com.github.pareronia.aoc.StringOps.splitLines;
34
import static java.util.Comparator.comparing;
45
import static java.util.stream.Collectors.joining;
56
import static java.util.stream.Collectors.summingInt;
@@ -13,101 +14,52 @@
1314
import java.util.List;
1415
import java.util.Set;
1516
import java.util.function.Function;
16-
import java.util.regex.Pattern;
1717
import java.util.stream.Stream;
1818

1919
import com.github.pareronia.aoc.geometry.Direction;
2020
import com.github.pareronia.aoc.geometry.Point;
2121
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;
2425

25-
public class AoC2016_22 extends AoCBase {
26+
public class AoC2016_22
27+
extends SolutionBase<AoC2016_22.Cluster, Integer, Integer> {
2628

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) {
3430
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());
4631
}
4732

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);
5035
}
5136

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);
8339
}
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()));
9547
}
9648

9749
@Override
98-
public Integer solvePart1() {
99-
return (int) this.nodes.stream()
50+
public Integer solvePart1(final Cluster cluster) {
51+
return (int) cluster.nodes.stream()
10052
.filter(Node::isNotEmpty)
101-
.flatMap(a -> this.nodes.stream()
53+
.flatMap(a -> cluster.nodes.stream()
10254
.filter(b -> !a.equals(b))
10355
.filter(b -> a.used() <= b.available()))
10456
.count();
10557
}
10658

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()
11163
.sorted(comparing(n -> n.x() * maxY + n.y()))
11264
.collect(toList());
11365
final List<List<Node>> grid = Stream.iterate(0, i -> i <= maxX, i -> i + 1)
@@ -116,10 +68,10 @@ private void visualize() {
11668
.takeWhile(n -> n.x() == i)
11769
.collect(toList()))
11870
.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();
12375
for (final List<Node> row : grid) {
12476
final String line = row.stream()
12577
.map(n -> {
@@ -155,16 +107,16 @@ private Function<Path, Boolean> stopAt(
155107
};
156108
}
157109

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()
161113
.map(this::toPosition)
162114
.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());
168120
log("Unusable: "+ unusableNodes);
169121
log("Empty: " + emptyNode);
170122
log("Accessible: " + accessibleNode);
@@ -191,9 +143,9 @@ private Integer solve2() {
191143
return length + 1;
192144
}
193145

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();
197149
log(unusableNodes);
198150
final Set<Integer> holeYs = unusableNodes.stream()
199151
.map(Node::y)
@@ -206,52 +158,53 @@ private Integer solve2Cheat() {
206158
assertFalse(unusableNodes.stream()
207159
.max(comparing(Node::x))
208160
.map(Node::x)
209-
.orElseThrow() != getMaxX(),
161+
.orElseThrow() != cluster.getMaxX(),
210162
() -> "Expected unusable row to touch side");
211163
final Integer holeX = unusableNodes.stream()
212164
.min(comparing(Node::x))
213165
.map(Node::x)
214166
.orElseThrow();
215167
final Position hole = Position.of(holeX - 1, holeY);
216-
final Position emptyNode = toPosition(getEmptyNode());
168+
final Position emptyNode = toPosition(cluster.getEmptyNode());
217169
final int part1 = emptyNode.manhattanDistance(hole);
218-
final Position goalNode = toPosition(getGoalNode());
170+
final Position goalNode = toPosition(cluster.getGoalNode());
219171
final int part2 = hole.manhattanDistance(
220172
Position.of(goalNode.getX() - 1, goalNode.getY()));
221173
final int part3 = 5 * (goalNode.getX() - 1);
222174
return part1 + part2 + part3 + 1;
223175
}
224176

225177
@Override
226-
public Integer solvePart2() {
227-
return solve2Cheat();
178+
public Integer solvePart2(final Cluster cluster) {
179+
return solve2Cheat(cluster);
228180
}
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;
233181

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();
240193
}
241194

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+
""";
255208

256209
private static final class PathFinder {
257210
private final Position start;
@@ -315,8 +268,63 @@ public boolean isAt(final Position position) {
315268
}
316269

317270
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+
318282
public boolean isNotEmpty() {
319283
return this.used != 0;
320284
}
321285
}
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+
}
322330
}

0 commit comments

Comments
 (0)