В начале была лень… Стойкое нежелание возиться с границами массивов. Оно, и только оно толкнуло меня на скользкую дорожку замены матрицы примитивов на List<List<>>
.
Следующим шагом на пути окунания в пучины многословия – стало создание контейнеров для упаковки данных со “схемы двигателя”. Вот они:
public record Coord(Integer row, Integer column){};
public record Cell(Coord coord, Integer codePoint){
Set<Coord> border() {return Set.of(
new Coord(coord.row(), coord.column() - 1), // left
new Coord(coord.row() - 1, coord.column() - 1), // top-left
new Coord(coord.row() - 1, coord.column()), // top
new Coord(coord.row() - 1, coord.column() + 1), // top-right
new Coord(coord.row(), coord.column() + 1), // right
new Coord(coord.row() + 1, coord.column() + 1), // bot-right
new Coord(coord.row() + 1, coord.column()), // bottom
new Coord(coord.row() + 1, coord.column() - 1) // bot-left
);}
};
public record Part(List<Cell> cells){
public Part {
cells = List.copyOf(cells);
}
Set<Coord> border() {
return cells.stream()
.flatMap(c -> c.border().stream())
.filter(bCoord -> cells.stream()
.noneMatch(c -> bCoord.equals(c.coord())))
.collect(Collectors.toSet());
}
Integer value() {
return Integer.parseInt(cells.stream()
.map(c -> Character.toChars(c.codePoint()))
.map(String::new).collect(Collectors.joining()));
}
};
У меня было три штуки рекордов, несколько методов с регулярно вычисляющей одно и то же логикой внутри них и россыпь операций по упаковке простого содержимого в сложные формы. Не то, чтобы всё это было нужно в решении задачи, но раз начал коллекционировать объекты, то иди в своём увлечении до конца.
static void day3(String puzzleInputUri) throws IOException, InterruptedException {
Integer emptyCell = (int) '.';
Integer gearCell = (int) '*';
List<List<Integer>> scheme = client.send(request.uri((URI.create(puzzleInputUri))).build(),
HttpResponse.BodyHandlers.ofLines()).body()
.map(line -> line.chars().boxed().collect(Collectors.toCollection(ArrayList::new)))
.peek(chars -> chars.addFirst(emptyCell))
.peek(chars -> chars.addLast(emptyCell))
.collect(Collectors.toCollection(ArrayList::new));
scheme.addFirst(Collections.nCopies(scheme.getFirst().size(), emptyCell));
scheme.addLast(Collections.nCopies(scheme.getFirst().size(), emptyCell));
List<Part> parts = new ArrayList<>();
List<Cell> gears = new ArrayList<>();
List<Cell> partDigit = new ArrayList<>();
boolean prevIsDigit = false;
for (int row = 1; row < scheme.size() - 1; row++) {
for (int column = 1; column < scheme.get(row).size() - 1; column++) {
Integer cellValue = scheme.get(row).get(column);
if (Character.isDigit(cellValue)) {
partDigit.add(new Cell(new Coord(row, column), cellValue));
prevIsDigit = true;
} else if (prevIsDigit) {
parts.add(new Part(partDigit));
partDigit.clear();
prevIsDigit = false;
}
if (gearCell.equals(cellValue)) {
gears.add(new Cell(new Coord(row, column), cellValue));
}
}
}
var validParts = parts.stream()
.filter(part -> part.border().stream().anyMatch(
c -> !emptyCell.equals(scheme.get(c.row()).get(c.column()))
)).toList();
var answer1 = validParts.stream()
.mapToInt(part -> part.value())
.sum();
System.out.println(answer1);
var answer2 = gears.stream()
.map(gear -> Map.entry(gear,
validParts.stream().filter(part -> part.border().contains(gear.coord())).toList()
))
.filter(gearParts -> gearParts.getValue().size() == 2)
.map(Map.Entry::getValue)
.mapToInt(gearParts -> gearParts.getFirst().value() * gearParts.getLast().value())
.sum();
System.out.println(answer2);
}
Это очень опасное предприятие закончилось благополучно! Длинная подготовка вылилась в достаточно короткие решения. Пришло удивительное, вселенское ощущение правильности всего того, что я делал.
static void printScheme(List<List<Integer>> scheme) {
scheme.forEach(row -> {
StringBuilder sb = new StringBuilder();
row.forEach(sb::appendCodePoint);
System.out.println(sb);
});
}
https://adventofcode.com/2023/day/3