static void day17(String puzzleInputUri) throws IOException, InterruptedException {
boolean[][][] shapes = new boolean[][][] {
{ {true, true, true, true} }, // ___
{ {false, true, false}, {true, true, true}, {false, true, false} }, // +
{ {false, false, true}, {false, false, true}, {true, true, true} }, // _|
{ {true}, {true}, {true}, {true} }, // |
{ {true, true}, {true, true} } // #
};
List<Move> moves = client.send(request.uri((URI.create(puzzleInputUri))).build(), HttpResponse.BodyHandlers.ofLines())
.body()
.map(String::toCharArray)
.flatMap(chars -> CharBuffer.wrap(chars).chars().mapToObj(i -> (char) i))
.map(Move::of)
.collect(Collectors.toList());
int level = 0;
int ins = 0;
Map<Integer, boolean[]> rows = new HashMap<>();
Map<Triple, Map.Entry<Long, Integer>> visited = new HashMap<>();
long additionalLevel = 0;
// long totalRocks = 2022L;
long totalRocks = 1_000_000_000_000L;
for (long rock = 0; rock < totalRocks; rock++) {
int currentShapeIdx = (int) (rock % shapes.length);
boolean[][] shape = shapes[currentShapeIdx];
int x = 2;
int y = level + 3;
while (true) {
Move direction = moves.get(ins++ % moves.size());
if ((Move.LEFT == direction && x > 0) && hasMove(shape, x - 1, y, rows)) {
x--;
} else if ((Move.RIGHT == direction && x < 7 - shape[0].length)
&& hasMove(shape, x + 1, y, rows)) {
x++;
}
if (hasMove(shape, x, y - 1, rows) && y > 0) {
y--;
} else {
for (int yIdx = 0; yIdx < shape.length; yIdx++) {
int currentY = y + (shape.length - yIdx);
boolean[] shapeRow = shape[yIdx];
boolean[] fieldRow = rows.computeIfAbsent(currentY, it -> new boolean[7]);
for (int xIdx = 0; xIdx < shapeRow.length; xIdx++) {
fieldRow[x + xIdx] |= shapeRow[xIdx];
}
}
break;
}
}
var levels = rows.keySet().stream().mapToInt(Integer::intValue).sorted().toArray();
level = levels[levels.length - 1];
List<Integer> window = new ArrayList<>(Collections.nCopies(7, -1));
for (int i = 0; i < 7; i++) {
for (int j = levels.length - 1; j >= 0; j--) {
if (rows.get(levels[j])[i]) {
window.add(i, level - levels[j]);
break;
}
}
}
var visitedRow = new Triple((ins - 1) % moves.size(), currentShapeIdx, window);
if (visited.containsKey(visitedRow)) {
var previous = visited.get(visitedRow);
long repeat = (totalRocks - rock) / (rock - previous.getKey());
rock += (rock - previous.getKey()) * repeat;
additionalLevel += repeat * (level - previous.getValue());
}
visited.put(visitedRow, Map.entry(rock, level));
}
System.out.println(level + additionalLevel);
}