Волновой алгоритм — это алгоритм поиска пути, который использует волновое распространение для определения кратчайшего пути от начальной вершины до целевой вершины. Название алгоритму дано не случайно, поведение алгоритма соответствует распространению волны, волна огибает препятствия, постепенно заполняя все пространство Всем привет! Меня зовут Нурислам (aka tonitaga), я участник School21 и сегодня я бы вам хотел рассказать о Волновом алгоритме. Лабиринт — структура, состоящая из запутанных путей к выходу.   Лабиринт хранится в файле в виде двух матриц — первая матрица говорит о наличии от текущей клетки стены справа, вторая матрица говорит о наличии от текущей клетки стены снизу. (1 - стена есть, 0 - стены нет) Хранение и использование лабиринта в виде двух матриц позволяет нам выделять меньше памяти, так как если поместить всю информацию в один массив, размер увеличится до 20×20, что составляет 400 клеток, а первый случай хранит всего 200 клеток. Однако есть и минусы, такой вид хранение увеличивает размер кода, так как мы работает одновременно с двумя массивами.  Пещера хранится в файле в виде матрицы, каждая клетка говорит о том, является ли она стеной или пустотой. (1 - стена, 0 - пустота) Про генерацию пещер можно прочитать в моей статье по ссылке:Генерация пещер by tonitaga  Волна будет распространяться по окрестности фон Неймана  Окрестность фон Неймана — совокупность четырёх клеток на квадратном паркете, имеющих общую сторону с данной клеткой.  Конечно, вместо окрестности фон Неймана, можно было использовать окрестность Мура, однако в случае с лабиринтом это невозможно, остановимся на первой окрестности. Волна, как и в жизни, может распространиться только в ту сторону, где нет препятствия Последней волной будет считаться та, которая достигла конечной точки, либо которая заполнила все пространство. Нахождение кратчайшего пути в пещере В первую очередь нам нужно создать матрицу (массив) длин, где будем хранить расстояния от клетки старта до клетки финиша CaveWrapper — это структура, которая хранит в себе данные пещеры и позволяет работать с одномерным массивом как с матрицей, структура знает в валидном ли состоянии пещера и  знает её размеры  Во вторую очередь нам нужно создать массивы распространения волны. Волна будет распространяться постепенно, поэтому нам надо хранить текущее распространение волны и предыдущее распространение, текущая волна будет распространяться в зависимости от предыдущего распространения. Также нам нужен будет счетчик для того чтобы знать сколько раз волна распространилась Для правильной работы алгоритма, мы в первую очередь должны инициализировать переменные начальными значениями Матрица длин (length_map) — это самое главное в этом алгоритме, по нему мы и будет находить кратчайший путь. У этой матрицы будет всего два типа значений: -1 (Если волна еще не была в этой клетке) и "неотрицательное число" - расстояние от начальной точки до текущей. Клетка старта в матрице длин будет помечена как 0, так как расстояние от начальной клетки до начальной и есть 0. От этой клетки и начнется распространение волны. old_wave будет в себе хранить точку старта, так как последующие волны будут распространяться именно от этой точки. Повторюсь, новая волна (wave) будет генерироваться от старой волны (old_wave).  Старая волна хранит в себе лишь последовательность точек на плоскости, от которых и нужно пускать новую волну Зная массив точек распространения (old_wave), будет пытаться распространять волну дальше, используя окрестность фон Неймана SidePoint это структура,  которая будет хранить в себе координаты соседних клеток и их значение  Зная точки предыдущей волны мы во вложенном цикле определяем соседние точки и их значения, переменная value отвечает за наличие препятствия в данном направлении, и если препятствия нет, то последующим этапом проверяем, была ли волна уже в этой точке, и если волны там еще не было, то мы распространяем новую волну в данном направлении, после каждого распространения мы проверяем не достигли мы конца. Для этого в функцию дополнительным параметром передается точка финиша (Point to) После того как все возможные распространения были сделаны и волна не достигла конечной точки, мы в старую волну передаем новые точки распространения. (19 строчка).  Волновой алгоритм находит все пути сразу, но нам нужен именно кратчайший путь Для того чтобы получить его, там нужно итеративно вернуться из точки финиша в точку старта, для того чтобы это сделать будем использовать матрицу длин. Зная значения в матрице длин, будем постепенно возвращаться, чтобы понять куда нам идти (вверх, вниз, вправо, влево), нужно всего лишь сравнить значение текущей клетки и клетки направления, и если клетка направления имеет число меньше чем текущее на 1, то это и есть наш путь. Путь будем искать пока не дойдем до начальной точки (вспомним, что начальную точку в матрице длин мы инициализировали нулем) Можно заметить, что это не единственный кратчайший путь, но мы выберем один из многих   Нахождение кратчайшего пути в лабиринте В целом реализация отличается лишь тем, что при обработке пещеры мы работаем с одним массивом, а в случае лабиринта с двумя массивами. В остальном алгоритм ничем не отличается. В главную функцию будет приходить не пещера а лабиринт тип данных у которого MazeWrapper MazeWrapper это структура, которая хранит в себе данные лабиринта и позволяет обращаться в одномерным массивам как к матрицам, все это благодаря перегруженному оператору круглых скобочек Из-за того что лабиринт хранит два массива, в операторе круглых скобок добавлен bool параметр, чтобы извне можно было выбирать один из двух массивов.  Отличие будет при определении соседей для распространения новой волны и при нахождении кратчайшего пути (все это связано с тем как эти данные мы храним с структуре) В моем случае лабиринт генерируется используя Алгоритм Эллера, это говорит о том, что в лабиринте от любых двух точек существует всего один путь, его мы и будем искать. Полезная статья по алгоритму ЭллераАлгоритм Эллера Можно заметить что слева от клетки со значением 20 есть клетка 19, на первый взгляд кажется что можно повернуть в эту сторону, однако там стенка, поэтому перед каждый поворотом нужно проверять нет ли стенки там, куда мы поворачиваем. Если этого не сделать то, путь будет проходить сквозь стены лабиринта, что является неверным решением задачи.  Хочу поделиться своим github'ом, буду признателен если вы оформите подписку:GitHub Статья, написанная мной, по генерации пещер:Генерация пещер Полезная статья по генерации лабиринта:Алгоритм Эллера Если возникли вопросы по реализации алгоритма, пишите в телеграм:@tonitaga