Опубликован: 12.02.2014 | Уровень: для всех | Доступ: платный
Лекция 8:

Графы

< Лекция 7 || Лекция 8: 1234 || Лекция 9 >

8.6. Лабиринт

В настоящем параграфе строится лабиринт. Стенки лабиринта генерируются случайным образом. В качестве места для размещения лабиринта используется клеточное поле размером 23 \times 38 (23 ряда и 38 столбцов). Размер поля подобран так, чтобы его было удобно отображать в окне консоли (рис. 8.5). Программа ищет путь из левого верхнего угла поля в правый нижний угол. Крайние столбцы слева и справа, а также самый нижний ряд поля всегда свободны, так что по этим клеткам можно перемещаться. Этим с избытком обеспечивается существование пути из начальной клетки в конечную. Для всех остальных клеток при генерации лабиринта шанс стать "стенкой", т. е. клеткой, по которой перемещаться нельзя, равен одному из трех.

 Лабиринт, сгенерированный случайным образом, и проход по нему

Рис. 8.5. Лабиринт, сгенерированный случайным образом, и проход по нему

Для поиска пути выхода из лабиринта используется "жадный" алгоритм. Первым продлевается путь, последняя вершина которого находится ближе всего к конечной клетке относительно обычного расстояния, без учета стенок. Для ускорения поиска используется следующее ограничение: пути не продлеваются в клетки, из которых уже строились продолжения путей. В результате путь находится быстро, но, в общем случае, этот путь не является кратчайшим.

Для клеток, по которым можно перемещаться, используется серый цвет, клетки стенок лабиринта желтого цвета. Путь прокрашивается синим цветом. Перед выводом каждой клетки пути делается небольшая пауза6Пример приведен для версии 7.5. В версии 7.4 вместо выражения not(A in B) должно быть not(list::isMember(A, B)). Вместо A и B в листингах стоят разные переменные..

    open core, console, console_native, list, math, programControl

domains
    state = empty; wall; pathcell.
    square = sq(unsigned16, unsigned16).

class facts
    m : unsigned16 := 23.
    n : unsigned16 := getConsoleWidth() div 2 - 2.

class facts
    cell: (unsigned16, unsigned16, state).

class predicates
    create: ().
    print: ().
    f: (unsigned) -> state.
    attr: (state) -> unsigned16.
    printCell: (square, state).
clauses
    create():-      % генерация лабиринта
        I = std::fromTo(0, m - 1), J = std::fromTo(0, n - 1),
            X = if (J = 0; J = n - 1; I = m - 1), ! then 1 
                else random(3) end if,
            assert(cell(I, J, f(X))),
        fail;
        succeed().

    f(0) = wall:- !.
    f(_) = empty.

    attr(wall) = bit::bitLeft(14, 4):- !.
    attr(empty) = bit::bitLeft(7, 4):- !.
    attr(_) = bit::bitLeft(9, 4).

    print():-      % печать лабиринта
        cell(I, J, State),
            printCell(sq(I, J), State),
        fail;
        succeed().

    printCell(sq(I, J), State):-
        setTextAttribute(attr(State)),
        setLocation(coord(2 * J + 2, I + 1)),
        write("  ").      % два пробельных символа

class predicates
    search: (square, square) -> square*.
    path: (square**, square*, square) -> square* determ.
    next: (square) -> square nondeterm.
    isEmpty: (square) determ.
    d: (square*, square) -> real.
clauses
    search(Start, Goal) = reverse(Path):-
        Path = path([[Start]], [], Goal),
        !.
    search(Start, _) = [Start].

    path([[Goal | Path] | _], _, Goal) = [Goal | Path]:- !.
    path([[Cell | Path] | PathList], CList, Goal) = 
            path(NewPathList, [Cell | CList], Goal):-
        NextPaths = [[NextCell, Cell | Path] || NextCell = next(Cell),
                            isEmpty(NextCell), not(NextCell in CList)],
        if NextPaths <> [] then
            NextPathList = append(NextPaths, PathList),
            BestPath = minimumBy(
                {(P1, P2) = compare(d(P1, Goal), d(P2, Goal))},
                NextPathList),
            NewPathList = [BestPath | remove(NextPathList, BestPath)]
        else
            NewPathList = PathList
        end if.
    
    next(sq(I, J)) = sq(I, J + 1):- J < n - 1.
    next(sq(I, J)) = sq(I + 1, J):- I < m - 1.
    next(sq(I, J)) = sq(I - 1, J):- I > 0.
    next(sq(I, J)) = sq(I, J - 1):- J > 0.

    isEmpty(sq(I, J)):-
        cell(I, J, empty),
        !.

    d([sq(I1, J1) | _], sq(I2, J2)) = abs(I2 - I1)^2 + abs(J2 - J1)^2:-
        !.
    d(_, _) = 0.

    run():-
        setConsoleTitle("Лабиринт"),
        create(),
        print(),
        Path = search(sq(0, 0), sq(m - 1, n - 1)),
        forAll(Path, {(Cell):- printCell(Cell, pathcell), sleep(50)}),
        _ = readLine().
Пример 8.8. Поиск пути в лабиринте

Предикат getConsoleWidth возвращает ширину окна консоли. Предикат abs находит абсолютную величину числа.

Вместо обычного расстояния можно использовать манхэттенское расстояние. Для этого нужно определить предикат d/2 следующим образом:

d([sq(I1, J1) | _], sq(I2, J2)) = abs(I2 - I1) + abs(J2 - J1):- !.
    d(_, _) = 0.

Упражнения

  1. Найдите пути, проходящие только через разрешенное множество вершин.
  2. Найдите пути между заданными вершинами, не проходящие через запрещенное множество вершин.
  3. Найдите пути между заданными вершинами, состоящие из заданного количества ребер.
  4. Реализуйте алгоритм поиска путей в ширину для графа, представленного в программе в виде фактов, каждый из которых хранит вершину и список пар, содержащих смежную вершину и вес ребра, который их соединяет.
  5. Найдите эйлеров цикл в графе.
  6. Используйте эвристический поиск в глубину для решения задачи о коммивояжере — для поиска гамильтоновых циклов в графе минимального суммарного веса.
  7. Определите предикат, который возвращает элементы матрицы смежности графа.
  8. Сгенерируйте полный граф с заданным количеством вершин и случайными весами ребер.
  9. Используйте алгоритм "первый лучший" для поиска кратчайшего по стоимости пути между фигурами, стоящими на клетчатом поле, клетки которого имеют разный вес (от 1 до 4). Вес клеток генерируется случайным образом. Как поле, так и пути должны отображаться в окне консоли.
< Лекция 7 || Лекция 8: 1234 || Лекция 9 >
Жаныл Айкын
Жаныл Айкын
Rustam Inatov
Rustam Inatov

Доброго времени суток, подскажите пожалуйста, visual prolog examples, pie, vip7.5 - это все, где я могу скачать? (в смысле) может быть на сайте есть какой-то архив? Увы я не нашел его.

Подскажите, пожалуйста.

С уважением, Рустам.

Айдана Ахметова
Айдана Ахметова
Россия
Дмитрий Куянов
Дмитрий Куянов
Россия, Омск, ОмГТУ