Advent of Code 2022: Day 12

По двенадцатому дню — сказать особо нечего. Если дни 7 и 10 запомнились (первый — неожиданными сложностями, второй — интересной в решении загадкой), то день 12 — совершенно безликий какой-то.

Тут изначально понятно, что именно придется делать для решения, и также понятно, что делать это будут довольно скучно.

Ну да ладно, BFS — так BFS. Разве что — не «тру-ООП» — без хранения вершиной своего состояния. Работает — и хорошо.

Что полезного отсюда вынес — обратное преобразование матрицы в стрим элементов, позволяющее отказаться от вложенных циклов.

Простая вершина для представления точки на карте

class Vertex {
    public final int x;
    public final int y;
    public final int dist;
    public final char height;
    public Vertex(int x, int y, int dist, char height) {
        this.x = x;
        this.y = y;
        this.dist = dist;
        this.height = height;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Vertex that = (Vertex) o;
        return x == that.x && y == that.y;
    }
    @Override
    public int hashCode() {
        return x + y;
    }
    @Override
    public String toString() {
        return "Vertex{x=" + x + ", y=" + y + ", dist=" + dist + '}';
    }
}

Обход соседей и поиск пути

static Vertex findEndPoint(Vertex from, char[][] map) {
    Set<Vertex> visited = new HashSet<>();
    char endMark = 'E';
    char nextTraversableMarkAfter_z = '{';
    int[][] directions = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    int mapHeight = map.length;
    int mapWidth = map[0].length;

    Vertex start = new Vertex(from.x, from.y, from.dist, from.height);
    Queue<Vertex> queue = new ArrayDeque<>();
    queue.add(start);
    visited.add(start);

    while (!queue.isEmpty()) {
        Vertex current = queue.poll();
        for (int[] direction : directions) {
            int nextX = Math.max(current.x + direction[1], 0);
            int nextY = Math.max(current.y + direction[0], 0);
            if (nextX < mapWidth && nextY < mapHeight) {
                Vertex next = new Vertex(nextX, nextY, current.dist + 1, map[nextY][nextX]);
                if (!visited.contains(next) && (next.height - current.height <= 1 || current.height == 'S')) {
                    if (next.height == endMark) {
                        map[next.y][next.x] = nextTraversableMarkAfter_z;
                        continue;
                    }
                    if (next.height == nextTraversableMarkAfter_z) {
                        map[next.y][next.x] = endMark;
                        return next;
                    }
                    queue.add(next);
                    visited.add(next);
                }
            }
        }
    }
    return new Vertex(from.x, from.y, from.dist, '#');
}

Решения для обеих частей загадки

static void day12(String puzzleInputUri) throws IOException, InterruptedException {
    char[][] mapArray = client.send(request.uri((URI.create(puzzleInputUri))).build(), HttpResponse.BodyHandlers.ofLines())
        .body()
        .map(String::toCharArray)
        .toArray(char[][]::new);

    System.out.println("Answer 1: " + findEndPoint(new Vertex(0,20,0, 'S'), mapArray));

    Vertex answer2 = IntStream.range(0, mapArray.length)
        .mapToObj(y -> Map.entry(y, mapArray[y]))
        .flatMap(row -> IntStream.range(0, row.getValue().length)
            .mapToObj(x -> Stream.of(new Vertex(x, row.getKey(), 0, row.getValue()[x])))
        )
        .flatMap(Function.identity())
        .filter(vertex ->
            Set.of('a', 'S').contains(vertex.height)
            && (
                (vertex.x == 0 || vertex.x == mapArray[0].length - 1)
                || (vertex.y == 0 || vertex.y == mapArray.length - 1)
            )
        )
        .map(entrance -> findEndPoint(entrance, mapArray))
        .filter(v -> v.height != '#')
        .min(Comparator.comparingInt(v -> v.dist))
        .orElseThrow();
    System.out.println("Answer 2: " + answer2);
}

Для решения второй части — была мысль пойти от E в направлении ближайших a, но всё равно пришлось бы перебрать их все, чтобы сравнить длину пройденного пути.

Исходные данные: https://adventofcode.com/2022/day/12/input

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

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