Алгоритм AStar на Scala

Очень долго в последнее время работаю над созданием интерактивных диаграмм. Одна из задача в таких диаграммах — это правильная прорисовка соединений. Соединения должны обходить препятствия.

Решения всегда разные, но один из самых распространенных случаев — применение алгоритма AStar. Подробно я его описывать не буду. Ибо его описание есть на вики. А цель этой статьи описать более понятный и конкретный алгоритм работы для дальнейшей реализации на  Scala.

Предусловия:

  • начальная позиция никогда не является препятствием

Алгоритм:

  1. Проверка на возможность пути (если проверка не успешная то выходим)
    1. Если начальная и конечная позиции равны, то возвращаем путь из одной вершины
    2. Если конечная позиция является препятствием, то путь невозможен и поэтому выходим
  2. В открытый список добавляется первая точка c вычисленными параметрами
  3. Цикл, пока не достигнем целевой точки
    1. Ищем в открытом списке (необработанный список) вершину с наименьшим значением эвристической функции F
    2. Если не нашли то путь невозможен и поэтому выходим
    3. Если нашли, то раскрываем эту вершину циклом по всем ее соседям:
      1. Проверяем является ли вершина препятствием. Если да, то переходим к следующей соседней
      2. Проверяем находится ли вершина в закрытом списке. Если да, то переходим к следующей соседней
      3. Вычисляем параметры вершины
      4. Если ее нет в открытом списке, то добавляем ее туда
    4. Раскрытую вершину удаляем из открытого списка и добавляем в закрытый
  4. Достигли целевой точки поэтому собираем путь (по параметру parent) и возвращаем его

Абстрактная реализация на  Scala:

Для конкретной реализации необходимо:

  1. Реализовать класс вершины с примесью ASTarNode,  в которой:
    1. Переопределен метод isAStarObstacle (возвращает true, если вершина является препятствием)
    2. Переопределить методы hashCode и euals таким образом, чтобы они зависели от координат вершины
  2. Реализовать класс поля на котором ищется путь с примесью ASTarField, в котором:
    1. Переопределить методы получения начальной и конечной вершины пути: getSourceNode и getTargetNode
    2. Переопределить метод получения эвристической функции — getHeuristic
    3. Переопределить метод итерации по соседним вершинам

Вот пример реализации для сетки, в которой вершины являются квадратными, а соседними вершинами считаются клетки по четырем сторонам:

На последок, совсем ужатая версия AStar.