Advent of Code 2023: Day 3: Gear Ratios

В начале была лень… Стойкое нежелание возиться с границами массивов. Оно, и только оно толкнуло меня на скользкую дорожку замены матрицы примитивов на 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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *