Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

UoKit.com Форумы _ UO Pilot _ Алгоритм А*

Автор: cirus 24.4.2017, 2:19

Алгоритм А*, 2.0
Код
// Алгоритм A*
load_array %map C:\Users\abc\Desktop\map1.txt        // массив карта
load_array %coord C:\Users\abc\Desktop\coord1.txt    // координаты точек

set $start A   // обозначение точки A в массиве
set $ending B  // обозначение точки B в массиве

// 0 - ходы только по вертикали и горизонтали, 1 - по диагонали тоже
set #angle 0

set #logmap 1     // выводить в лог карту
set #readmap 1    // выводить ходы в лог
set #skipstep 0   // 0 - не пропускать ходы, 1 - пропускать. Нужно, если ширина препятствия меньше ширины клетки, на которую можно сделать ход.
set #click 1       // делать ходы, клик по координатам точек.
set #send 0       // делать ходы, нажатия клавииш Up, Down, Left, Right
set #hint 1       // показывать количество проверенных точек
set #showpoint 0  // показывать проверенные точки (клик по ним)
set #wait 30      // пауза между нажатиями клавиш Up, Down, Left, Right

// остальное менять не нужно
// направление движения
init_arr %vector (1) -1 -1 0 1 1 1 0 -1     // x
init_arr %vector (2) 0 1 1 1 0 -1 -1 -1     // y

set #handle workwindow
if  workwindow = 0
    Log Не сделана привязка
    Log Скрипт остановлен
    stop_script
end_if
if  size(%map) = 0
    Log Массив с картой не найден.
    Log Скрипт остановлен
    stop_script
end_if
if  size(%coord) = 0
    Log Массив с координатами не найден.
    Log Скрипт остановлен
    stop_script
end_if

log clear
log mode compact
set size(%tmp)
set size(%open)
set size(%close)
set size(%curr)
set size(%resultarray)
set #width size(%map[])
set #height size(%map)
set %sizemap indexof (%map (0))
set #sizemap size(%sizemap) + 1

// Позиция конечной точки на карте
set %ending indexof (%map ($ending))
// Позиция начальной точки на карте
set %start indexof (%map ($start))
// если точка A или B не найдены
if  size(%ending) = 0 or size(%start) = 0
    if  size(%start) = 0
        log Точка A не найдена
    else
        log Точка B не найдена
    end_if
    log Скрипт остановлен
    end_script
end_if

// F,  G,  H,  Направление_движения,  Текущая клетка(X Y),  Текущая клетка(X:Y),  Родительская клетка (X Y)
init_arr %open (1) 0 0 0 0 %start [1 1] %start [1 2] %start [1 1]:%start [1 2] %start [1 1] %start [1 2]
while 1 = 1
    sort_array %open                    // сортировка по наименьшему F
    set #sizeclose size(%close) + 1
    init_arr %curr (1) %open [1 1] %open [1 2] %open [1 3] %open [1 4] %open [1 5] %open [1 6] %open [1 7] %open [1 8] %open [1 9]  // текущая клетка
    init_arr %close (#sizeclose) %open [1 1] %open [1 2] %open [1 3] %open [1 4] %open [1 5] %open [1 6] %open [1 7] %open [1 8] %open [1 9]  // добавить в закрытый список
    if  #showpoint = 1
        left %coord[%curr[1 5] %curr[1 6]]
        wait 1
    end_if
    delete_array %open -1     // удалить с открытого списка

    // для каждой из 8 клеток просчёт хода
    for #i 1 8
        if  #angle = 1 or mod(#i 2) = 1        // если ходы по диагонали допустимы или проверяется ход не по диагонали
            set #x %curr [1 5] + %vector [1 #i]
            set #y %curr [1 6] + %vector [2 #i]
            init_arr %tmp (1) #x : #y
            // поиск клетки в закрытом списке
            set %resultclose indexof (%close (%tmp[1]))
            if  #x > 0 and #y > 0 and #x <= #height and #y <= #width     // проверка не вышли ли за пределы массива
                if  (%map [#x #y] = 0 or %map [#x #y] = $ending) and size(%resultclose) = 0   // (если на клетку возможен ход или она является конечной) и не находится в закрытом списке
                    set %resultopen indexof (%open (%tmp[1]))  // Если клетка еще не в открытом списке, то добавляем ее туда.
                    if  size( %resultopen) = 0
                        set #sizeopen size(%open) + 1
                        if  mod(#i 2) = 1
                            set #g %curr [1 2] + 10                // стоимость G
                        else
                            set #g %curr [1 2] + 14
                        end_if
                        set #h (abs(eval(#x - %ending [1 1])) + abs(eval(#y - %ending [1 2]))) * 10      // стоимость H
                        set #f #g + #h                                                                   // стоимость F
                        init_arr %open (#sizeopen) #f #g #h #i #x #y %tmp[1] %curr [1 5] %curr [1 6]
                    else
                        // если клетка уже в открытом списке
                        set #tmp %resultopen [1 1]  // позиция клетки в открытом списке
                        set #g1 %open[#tmp 2]       // стоимость G клетки находящейся в открытом списке
                        if  mod(#i 2) = 1
                            set #g %curr [1 2] + 10     // стоимость G текущей клетки + стоимость хода на клетку находящуюся в открытом списке
                        else
                            set #g %curr [1 2] + 14
                        end_if
                        if  #g < #g1                // если стоимость хода дешевле
                            // меняем стоимость хода клетки находящейся в открытом списке
                            // меняем родителя клетки на текущую
                            set #h (abs(eval(#x - %ending [1 1])) + abs(eval(#y - %ending [1 2]))) * 10
                            set #f #g + #h
                            init_arr %open (#tmp) #f #g #h #i #x #y %tmp[1] %curr [1 5] %curr [1 6]
                        end_if
                    end_if
                end_if
            end_if
        end_if
    end_for

    // если конечная клетка добавлена в закрытый список или все точки проверены
    if  (%curr [1 5] = %ending [1 1] and %curr [1 6] = %ending [1 2]) or size(%open) = 0
        if  %curr [1 5] != %ending [1 1] and %curr [1 6] != %ending [1 2]
            log Вариантов хода нет
            end_script
        end_if

        // проходим весь путь от точки B до точки A и записываем путь в массив
        set size(%tmp)
        init_arr %tmp (1) %close [size(%close) 8] %close [size(%close) 9]
        while %tmp [size(%tmp) 1] != %start [1 1] or %tmp [size(%tmp) 2] != %start [1 2]
            set %pathAB indexof (%close (%tmp [size(%tmp) 1]:%tmp [size(%tmp) 2]))
            set #zz size(%tmp) + 1
            init_arr %tmp (#zz) %close [%pathAB [1 1] 8] %close [%pathAB [1 1] 9] %close [%pathAB [1 1] 4]
        end_while
        set #z 0
        set delimiter ' '
        for #i size(%tmp) 1 -1
            set #z #z + 1
            init_arr %resultarray (#z) %tmp [#i]
        end_for
        set delimiter
        // save_array %open C:\Users\abc\Desktop\open.txt      // открытый список
        // save_array %close C:\Users\abc\Desktop\close.txt    // закрытый список
        // save_array %resultarray C:\Users\abc\Desktop\resultarray.txt   // массив с индексами нужных точек находящихся в массиве %map
        log На поиск хода затрачено: timer мсек

        // Вывод карты
        if  #logmap = 1
            gosub logmap
        end_if

        // Вывод в лог ходов
        if  #readmap = 1
            gosub readmap
        end_if

        // Сделать ход
        if  #click = 1
            gosub hod
        end_if
        end_script
    end_if
    if  #hint = 1
        hint Проверено size(%close) из #sizemap точек.
    end_if
end_while
end_script

:logmap
    for #i 1 size(%map)
        log %map [#i]
    end_for
return

:readmap
    set workwindow #handle
    wait 500
    showwindow #handle
    wait 500

    set #tmpi size(%resultarray) - 1
    for #i 1 size(%resultarray)
        if  (#skipstep = 0) or (mod (#i 2) = 0 or #tmpi = #i)
            switch %resultarray [#i 3]
                case 1: log Вверх
                    if  #send = 1
                        send {Up}
                        wait #wait
                    end_if
                    break
                case 2: log Вверх-Вправо
                    if  #send = 1
                        send {Up}
                        wait #wait
                        send {Right}
                        wait #wait
                    end_if
                    break
                case 3: log Вправо
                    if  #send = 1
                        send {Right}
                        wait #wait
                    end_if
                    break
                case 4: log Вниз-Вправо
                    if  #send = 1
                        send {Down}
                        wait #wait
                        send {Right}
                        wait #wait
                    end_if
                    break
                case 5: log Вниз
                    if  #send = 1
                        send {Down}
                        wait #wait
                    end_if
                    break
                case 6: log Вниз-Влево
                    if  #send = 1
                        send {Down}
                        wait #wait
                        send {Left}
                        wait #wait
                    end_if
                    break
                case 7: log Влево
                    if  #send = 1
                        send {Left}
                        wait #wait
                    end_if
                    break
                case 8: log Вверх-Влево
                    if  #send = 1
                        send {Up}
                        wait #wait
                        send {Left}
                        wait #wait
                    end_if
                    break
            end_switch
        end_if
    end_for
return
:hod
    if  #showpoint = 1
        alarm
        set showscriptprocessing 1
        log Путь найден. Скрипт на паузе.
        log Для вывода пути снять скрипт с паузы.
        pause_script
        set showscriptprocessing 0
    end_if
    showwindow #handle
    wait 500
    if  #skipstep = 0
        for #i 2 size(%resultarray)     // делать все ходы
            kleft %coord[%resultarray [#i 1] %resultarray [#i 2]]
            // log  %coord[%resultarray [#i 1] %resultarray [#i 2]]
            wait 1
        end_for
    else
        for #i 3 size(%resultarray) 2   // пропускать не нужные ходы
            kleft %coord[%resultarray [#i 1] %resultarray [#i 2]]
            // log  %coord[%resultarray [#i 1] %resultarray [#i 2]]
            wait 1
        end_for
    end_if
return

Уменьшен код почти на 200 строк. Работает чуть медленнее первой версии.

Алгоритм А*, 1.0
Прикрепленный файл  АлгоритмA__версия1.zip ( 3,1 килобайт ) Кол-во скачиваний: 253

Описание алгоритма: http://masters.donntu.org/2013/fknt/buga/library/AStar.htm
Скрипт просчитывает самый короткий путь от точки A до точки B.
Для его работы нужен массив-карта, содержащий:
0 - точки, по которым можно перемещаться
1 - препятствия
A - точка A (англ.)
B - точка B (англ.)
Если для ходов используются клики, то ещё нужен массив с координатами всех точек.
Пример работы скрипта: https://youtu.be/dC-bBCIYuwE
В архиве картинки 2 лабиринтов и массив-карта к ним. Прикрепленный файл  A.zip ( 38,2 килобайт ) Кол-во скачиваний: 281

Автор: Mirage 24.4.2017, 20:17

Жухала dry.gif

biggrin.gif
Адаптировать такое на какую нибудь рогрпг аля дьяблы что бы искал кротчайший путь по карте до выхода их подземелья. Все проходящие игру на скоростью сдохнут от зависти.

Автор: Cockney 24.4.2017, 22:33

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

Автор: DarkMaster 25.4.2017, 1:15

сам когда-то задавался данной задачей, но что-то заглохло все.
В состоянии ли данная реализация выдать путь для некоторого набора вейпонитов?
Т.е. есть некоторый набор точек с указанием к каким точкам из них можно добраться.

Автор: cirus 25.4.2017, 2:43

Цитата
но для второй карты минута с лишним расчет.

Если на lua будет findcolor, скорее всего, все расчёты займут минимум времени. Можно функцию написать.
Цитата
В состоянии ли данная реализация выдать путь для некоторого набора вейпонитов?
Т.е. есть некоторый набор точек с указанием к каким точкам из них можно добраться.

Это скорее в сторону графов. Алгоритм Дейкстры.
Если доработать можно и этим искать путь, только на пилотовском языке устанешь ждать.
Самая большая проблема - это создание карты, а не поиск пути.



Автор: DarkMaster 25.4.2017, 5:54

Цитата
Это скорее в сторону графов. Алгоритм Дейкстры.

Графов да, но Дейкстры ли? Вроде тоже а-старом решается... По крайней мере принципиальных отличий не вижу. Вообще в моем понимании подобных алгоритм должен возвращать массив с координатами точек пути, а передвижение между ними не более чем пользовательская функция.

Автор: cirus 25.4.2017, 11:13

Цитата
Вообще в моем понимании подобных алгоритм должен возвращать массив с координатами точек пути, а передвижение между ними не более чем пользовательская функция.

Оно почти так и есть.
Массив %resultarray содержит индексы для массива с координатами (%coord) и направление движения.
Например, это массив %resultarray:
Код
2    2    5    
3    2    5    
3    3    3    
3    4    1    
2    4    

2 2 это начальная точка, 5 это направление движения (вниз).
2 4 конечная точка
Если используются клики, то:
left %coord [3 2] - первый ход
left %coord [3 3] - второй
left %coord [3 4] - третий
left %coord [2 4] - четвёртый

Сейчас скрипт останавливает поиск, если путь до конечной точки найден. Для нескольких точек нужно менять условие, или делать процедуру для поиска и искать сначала путь до одной точки, потом до другой.

Автор: WKnight 25.4.2017, 18:18

Красавчик.

Свитч полностью дублируется. Так имхо чуть короче.

Код
    for #i 1 size(%resultarray)
        //  #skipstep = 0                  // показывать все ходы
        //  mod (#i 2) = 0 or #tmpi = #i   // пропускать не нужные ходы
        if  (#skipstep = 0) or (mod (#i 2) = 0 or #tmpi = #i)
                switch %resultarray [#i 3]
                  ...
                end_switch            
        end_if
    end_for

Автор: cirus 26.4.2017, 2:52

Цитата
Свитч полностью дублируется. Так имхо чуть короче

Спасибо. Подправил.

Автор: Aimed 26.4.2017, 17:45

Вы же понимаете что вам для нормальной работы этого алгоритма в пилоте нужно так-же правила передвижения от ноде к ноде знать.

Мож для какого-нибудь 2Д лабиринта где только стенка и не стенка есть и сойдет, а все остальное... я не вижу как можно обобщить это и вшить в пилот. Тут для каждой игры свои правила будут.

Автор: DarkMaster 27.4.2017, 18:31

Цитата
Мож для какого-нибудь 2Д лабиринта где только стенка и не стенка есть и сойдет, а все остальное... я не вижу как можно обобщить это и вшить в пилот. Тут для каждой игры свои правила будут.

Ну вейпоинты не такая большая проблема на самом деле. У меня был (да и сейчас где-то валяется) алгоритм перемещения из коодинат x1, y1 в x2, y2 для вов. Игровые координаты дернуть не так сложно, как могло бы показаться.

Автор: cirus 28.4.2017, 3:16

Цитата
Т.е. есть некоторый набор точек с указанием к каким точкам из них можно добраться.

Возьмём тот же лабиринт, на котором есть 5 точек (A - начальная точка, остальные - B, C, D, E).
Нужно добраться из точки A до указанных, например, C и D.
Найти путь A-C-D, найти путь A-D-C. Выбрать тот, который короче. Так должно быть?

Автор: DarkMaster 28.4.2017, 5:53

Цитата
Найти путь A-C-D, найти путь A-D-C. Выбрать тот, который короче. Так должно быть?

Давай на более понятных тебе величинах smile.gif
Дано: орен, дарк елвен, глудин, глудио, дион, гиран, гиран харбор.
Из каждого города мы можем переместиться только в соседний(ходим пешком).
Нужно найти кротчайший путь из орена в глудин.

Автор: DarkMaster 28.4.2017, 6:15

Черные линии некоторая координатная сетка. Задача добраться из синего в красный. Предполагается, что цена пути помимо значения расстояния может дополнительны иметь модификатор (на некоторых локациях невозможно использовать ездовых животных, например), либо задан константой (частные случаи).


Эскизы прикрепленных изображений
Прикрепленное изображение

Автор: Aimed 28.4.2017, 16:32

Цитата(DarkMaster @ 27.4.2017, 17:31) *

Ну вейпоинты не такая большая проблема на самом деле. У меня был (да и сейчас где-то валяется) алгоритм перемещения из коодинат x1, y1 в x2, y2 для вов. Игровые координаты дернуть не так сложно, как могло бы показаться.


Не, я о другом. Я об обобщении и вшивании его в пилот как функции для поиска пути.
Проблема в том что есть слишком много разных факторов которые зависят от контекста.
Допустим возьмем УО.
Если на пути стоят персонажи, их все обходить или смотреть что по стамине и проходить через персонажа?
Если ты находишься на крыше у которой нет бортиков и с неё можно спрыгнуть?
Если на пути находится паутина, в которой ты застрянешь, но сама она непроходимой не является?

И так с каждой игрой.
Тут надо очень хорошо все нюансы игры знать, что-бы делать правильные расчеты кратчайшего пути через А*.
Например в той-же РанУО, при патфайндинге у мобов ( там тоже А* используется), между каждый тайлом используется весь алгоритм с проверками на шаг, а это довольно много кода, ибо нюансов вагон и тележка.


Пробежка по вей пойнтам х это тоже самое что запуск А* х раз. Зачем вам это как отдельная фича?
Имхо в ней есть смысл только в том случае, если у вас есть пачка вей пойнтов и все они не упорядочены.
Тоесть нужно получить общий кратчайший путь через все вей пойнты. Но в каком случае можно встретить такой сценарий, где у вас куча разных точек, разбросанных по карте и вам нужно по ним создать кратчайший маршрут?

Автор: DarkMaster 28.4.2017, 16:51

Цитата
Не, я о другом. Я об обобщении и вшивании его в пилот как функции для поиска пути.
Проблема в том что есть слишком много разных факторов которые зависят от контекста.
Допустим возьмем УО.
Если на пути стоят персонажи, их все обходить или смотреть что по стамине и проходить через персонажа?
Если ты находишься на крыше у которой нет бортиков и с неё можно спрыгнуть?
Если на пути находится паутина, в которой ты застрянешь, но сама она непроходимой не является?

И так с каждой игрой.
Тут надо очень хорошо все нюансы игры знать, что-бы делать правильные расчеты кратчайшего пути через А*.
Например в той-же РанУО, при патфайндинге у мобов ( там тоже А* используется), между каждый тайлом используется весь алгоритм с проверками на шаг, а это довольно много кода, ибо нюансов вагон и тележка.

Для этого должен быть разработан формат карты который будет учитывать данные детали. Т.е. в моем понимании это xyz координаты, модификатор стоимость пути, задание фиксированной стоимости. Карта должна создаваться непосредственно пользователем. Алгоритм же поиска не должен каждый раз вымучиваться на движке пилота.
Цитата
Пробежка по вей пойнтам х это тоже самое что запуск А* х раз.

Каким это образом? Это будет запуск 1 раз, просто логика должна понимать различную ценность маршрута и ограничения связей.
Цитата
Зачем вам это как отдельная фича?
Имхо в ней есть смысл только в том случае, если у вас есть пачка вей пойнтов и все они не упорядочены.
Тоесть нужно получить общий кратчайший путь через все вей пойнты. Но в каком случае можно встретить такой сценарий, где у вас куча разных точек, разбросанных по карте и вам нужно по ним создать кратчайший маршрут?

Навигация в любом полноценном 3д приложении.

Автор: pager 28.4.2017, 17:21

Цитата(Aimed @ 28.4.2017, 19:32) *

Не, я о другом. Я об обобщении и вшивании его в пилот как функции для поиска пути.
Проблема в том что есть слишком много разных факторов которые зависят от контекста.
Допустим возьмем УО.
Если на пути стоят персонажи, их все обходить или смотреть что по стамине и проходить через персонажа?
Если ты находишься на крыше у которой нет бортиков и с неё можно спрыгнуть?
Если на пути находится паутина, в которой ты застрянешь, но сама она непроходимой не является?

И так с каждой игрой.
Тут надо очень хорошо все нюансы игры знать, что-бы делать правильные расчеты кратчайшего пути через А*.
Например в той-же РанУО, при патфайндинге у мобов ( там тоже А* используется), между каждый тайлом используется весь алгоритм с проверками на шаг, а это довольно много кода, ибо нюансов вагон и тележка.
Пробежка по вей пойнтам х это тоже самое что запуск А* х раз. Зачем вам это как отдельная фича?
Имхо в ней есть смысл только в том случае, если у вас есть пачка вей пойнтов и все они не упорядочены.
Тоесть нужно получить общий кратчайший путь через все вей пойнты. Но в каком случае можно встретить такой сценарий, где у вас куча разных точек, разбросанных по карте и вам нужно по ним создать кратчайший маршрут?


На мой взгляд
Не думаю что в поиске пути правильно было бы учитывать динамические препятствия , получатель маршрута должен сам оценивать динамические препятствия и максимум что можно было бы сделать это системе поиска маршрута посылать уведомления что проложенный маршрут не подходит для использования и система его не учитывала в новом положении маршрута.

Автор: DarkMaster 28.4.2017, 17:29

Цитата
На мой взгляд
Не думаю что в поиске пути правильно было бы учитывать динамические препятствия , получатель маршрута должен сам оценивать динамические препятствия и максимум что можно было бы сделать это системе поиска маршрута посылать уведомления что проложенный маршрут не подходит для использования и система его не учитывала в новом положении маршрута.

Имхо решаться должно опять же пользователем при составлении карты и если так нужно динамическое изменение, то можно назначить фиксированное значение которое будет несопостовимо дорогим для прокладки данного маршрута, либо использовать зарезервированное значение для отмены данного маршрута. В любом случае это должно решаться картой. Нужна тебе динамика - динамически изменяй карту. Тут алгоритм поиска по карте, а не составления карты.

Автор: pager 28.4.2017, 18:17

Цитата(DarkMaster @ 28.4.2017, 20:29) *

Имхо решаться должно опять же пользователем при составлении карты и если так нужно динамическое изменение, то можно назначить фиксированное значение которое будет несопостовимо дорогим для прокладки данного маршрута, либо использовать зарезервированное значение для отмены данного маршрута. В любом случае это должно решаться картой. Нужна тебе динамика - динамически изменяй карту. Тут алгоритм поиска по карте, а не составления карты.

А я и не говорил о составление карты я под динамическими объектами имел в веду мобов тех же самых паутин и временных препятствий.Просто если система проложения маршрута начала бы делать проверки на тех же самых мобов то производительность системы вразы бы была меньше чем если бы эти проверки делал бы получатель маршрута

Автор: cirus 29.4.2017, 21:38

Пока черновой вариант. Путь считается только по расстоянию.

код
Код
// Алгоритм A*
load_array %map C:\Users\abc\Desktop\map.txt        // массив карта
load_array %coord C:\Users\abc\Desktop\coord.txt    // координаты точек

set $start 4   // начальная точка
set $ending 1  // конечная

set #click 0       // делать ходы, клик по координатам точек. Нужна привязка в paint.

if  size(%map) = 0
    Log Массив с картой не найден.
    Log Скрипт остановлен
    stop_script
end_if
if  size(%coord) = 0
    Log Массив с координатами не найден.
    Log Скрипт остановлен
    stop_script
end_if

log clear
log mode compact
set size(%tmp)
set size(%open)
set size(%close)
set size(%curr)
set size(%resultarray)

// Позиция конечной точки на карте
for #i 1 size(%map)
    if  %map[#i 1] =  $ending
        set %ending [1 1] $ending
        break
    end_if
end_for

// Позиция начальной точки на карте
set %start [1 1] 7
for #i 1 size(%map)
    if  %map[#i 1] =  $start
        set %start [1 1] $start
        break
    end_if
end_for

// F,  G,  H, Текущая клетка, Родительская клетка, Текущая клетка:Родительская клетка
init_arr %open (1) 0 0 0 %start [1 1] %start [1 1] %start [1 1]:%start [1 1]

while 1 = 1
    sort_array %open                    // сортировка по наименьшему F
    set #sizeclose size(%close) + 1
    init_arr %curr (1) %open [1 1] %open [1 2] %open [1 3] %open [1 4] %open [1 5] %open [1 6] // текущая клетка
    init_arr %close (#sizeclose) %open [1 1] %open [1 2] %open [1 3] %open [1 4] %open [1 5] %open [1 6]  // добавить в закрытый список
    delete_array %open -1     // удалить с открытого списка
    for #t 2 size(%map[1])    // проверяем все точки, в которые можно дойти из текущей
        set #tmp %map [%curr[1 4] #t]
        if  #tmp  != 0
            set #x %map [%curr[1 4] #t]      // текущая точка  // %curr[1 4] - родитель
            init_arr %tmp (1) #x : %curr [1 4]
            set #sizeopen size(%open) + 1
            set #g %curr [1 2] + round(point_distance(%coord[%curr [1 4] 1]  %coord[#x 1]))             // стоимость G
            set #h round(point_distance(%coord[$ending 1]  %coord[#x 1]))     // стоимость H
            set #f #g + #h          // стоимость F
            init_arr %tmp (1) #x : %curr [1 4]
            init_arr %open (#sizeopen) #f #g #h #x %curr [1 4] %tmp [1]
        else
            break
        end_if
    end_for

    if  size(%open) = 0 or %curr [1 4] = %ending [1 1]     // если открытый список пуст или конечная точка найдена
        set size(%tmp)
        set #tmp %curr [1 4]
        if  %curr [1 4] = %ending [1 1]                    // если конечная точка найдена
            set #z #z + 1
            set %tmp [#z] #tmp
            // идём от конечной точки к начальной
            while #tmp != $start
                for #i 1 size(%close)
                    if  %close [#i 4] = #tmp
                        set #zz #zz + 1
                        set #tmp %close[#i 5]
                        set #z #z + 1
                        set %tmp [#z] #tmp
                        break
                    end_if
                end_for
            end_while
            // переворачиваем путь
            set #z 0
            for #i size(%tmp) 1 -1
                set #z #z + 1
                set %resultarray [#z] %tmp[#i 1]
            end_for

            // вывод в лог пути
            for #i 1 size(%resultarray)
                log %resultarray [#i]      %coord[%resultarray [#i]]
                if  #click = 1
                    kleft %coord[%resultarray [#i]]
                end_if
            end_for
        else
            log Ходов нет
        end_if
        end_script
    end_if
end_while
end_script

В массиве %map содержатся все точки в виде:
<точка1> [до каких точек можно добраться из точки1]
<точка2> [до каких точек можно добраться из точки2]
В массиве %coord содержатся координаты всех точек. Координаты взяты с картинки открытой в Paint.
В архиве 2 тхт файла (%map и %coord) и картинка. Прикрепленный файл  Astar2.zip ( 7,66 килобайт ) Кол-во скачиваний: 194




Прикрепленные файлы
Прикрепленный файл  map.bmp ( 265,59 килобайт ) Кол-во скачиваний: 200

Автор: cirus 2.6.2017, 12:39

A* Lua
Код
--lua
log ("clear") log ("mode compact")

-- ходы по диагонали, 0 - нет, 1 - да
angle = 0

-- параметры findcolor
find = {29, 25, 522, 516, 10, 10, "(0-16777215)", "%arr"}


-- создание карты
function Create_Map(startX, startY, endX, endY, stepX, stepY, color, array)
    local z = 0
    local a = findcolor(startX, startY, endX, endY, stepX, stepY, "(0-16777215)", "%arr")
    if  #arr > 0 then
        local map = {}
        for i = 1, ((endY - startY) / stepY) + 1 do
            map[i] = {}
            for j = 1, ((endX - startX) / stepX) + 1 do
                z = z + 1
                if  tonumber(arr[z][3]) == 0 then
                    map[i][j] = {1, arr[z][1], arr[z][2]}
                elseif tonumber(arr[z][3]) == 255 then
                    map[i][j] = {"A", arr[z][1], arr[z][2]}
                elseif tonumber(arr[z][3]) == 16711680 then
                    map[i][j] = {"B", arr[z][1], arr[z][2]}
                else
                    map[i][j] = {0, arr[z][1], arr[z][2]}
                end
            end
        end
        return map
    end
end

    -- поиск в массиве A и B
function index_of_A_B(arr, text)
    local result = {}
    for i = 1, #arr  do
        for j = 1, #arr[i] do
            if arr[i][j][1] == text then
                table.insert(result, {i, j})
            end
        end
    end
    return result
end

    -- поиск в массиве
function index_of(arr, text)
    local result = {}
    for i = 1, #arr  do
        for j = 1, #arr[i] do
            if arr[i][j] == text then
                table.insert(result, {i, j})
            end
        end
    end
    return result
end

     -- A*
function A_star(map)
    local g, g1, h, f, tmp = 0, 0, 0, 0, 0
    local open, close, curr, index, resultclose, resultopen, vector = {}, {}, {}, {}, {}, {}, {}
    table.insert(vector, {-1, -1, 0, 1, 1, 1, 0, -1})
    table.insert(vector, {0, 1, 1, 1, 0, -1, -1, -1})

    table.insert(open, {0, 0, 0, 0, start [1][1], start [1][2], start [1][1]..":"..start [1][2], start [1][1], start [1][2]})
    while 1 == 1 do
        table.sort (open, function (a, b) return (a[1] < b[1]) end)
        curr [1] = {open [1][1], open [1][2], open [1][3], open [1][4], open [1][5], open [1][6], open [1][7], open [1][8], open [1][9]}
        table.insert(close, {open [1][1], open [1][2], open [1][3], open [1][4], open [1][5], open [1][6], open [1][7], open [1][8], open [1][9]})
        table.remove (open, 1)
        for i = 1, 8 do
            if angle == 1 or i%2 == 1 then
                local x, y = curr[1][5] + vector[1][i], curr[1][6] + vector[2][i]
                resultclose = index_of(close, x..":"..y)
                    if  x > 0 and y > 0 and x <= #map[i] and y <= #map then
                        if (map[x][y][1] == 0 or map[x][y][1] == "B") and #resultclose == 0 then
                            resultopen = index_of(open, x..":"..y)
                            if #resultopen == 0 then
                                if i%2 == 1 then g = curr[1][2] + 10 else g = curr[1][2] + 14 end
                                h = (math.abs(x - ending[1][1]) + math.abs(y - ending[1][2])) * 10
                                f = g + h
                                table.insert(open, {f, g, h, i, x, y, x..":"..y, curr[1][5], curr[1][6]})
                            else
                                tmp = resultopen [1][1]
                                g1 = open[tmp][2]
                                if i%2 == 1 then g = curr[1][2] + 10 else g = curr[1][2] + 14 end
                                if g < g1 then
                                    h = (math.abs(x - ending[1][1]) + math.abs(y - ending[1][2])) * 10
                                    f = g + h
                                    table.insert(open, {f, g, h, i, x, y, x..":"..y, curr[1][5], curr[1][6]})
                                end
                            end
                        end
                    end
            end
        end
        if  (curr [1][5] == ending [1][1] and curr [1][6] == ending [1][2]) or #open == 0 then
            break
        end
    end

    local pathAB, tmp, path = {}, {}, {}
    tmp [1] = {ending[1][1], ending[1][2]}
    table.insert(tmp, {close[#close][8], close[#close][9], close[#close][4]})

    while  tmp[#tmp][1] ~= start[1][1] or tmp[#tmp][2] ~= start[1][2] do
        pathAB = index_of(close, tmp[#tmp][1]..":"..tmp[#tmp][2])
        table.insert (tmp, {close[pathAB[1][1]][8], close[pathAB[1][1]][9], close[pathAB[1][1]][4]})
    end

    for i = #tmp, 1, -1 do
        table.insert (path, {map[tmp[i][1]][tmp[i][2]][2], map[tmp[i][1]][tmp[i][2]][3]})
    end
    return path
end



    -- создание карты
map = Create_Map(find[1],find[2],find[3],find[4],find[5],find[6],find[7],find[8])

    -- поиск точки A в массиве map
start = {}
start = index_of_A_B(map, "A")

    -- поиск точки B в массиве map
ending = {}
ending = index_of_A_B(map, "B")

    -- поиск пути
if #start > 0 and #ending > 0 then
    path = {}
    path = A_star(map)

      -- ход
    for i = 1, #path do
        kleft (path[i][1], path[i][2])
    end

elseif #start == 0 then
    log ("Точка A не найдена")
else
    log ("Точка B не найдена")
end

https://youtu.be/w4e9a3xjdwk
Прикрепленный файл  labirint.bmp ( 789,13 килобайт ) Кол-во скачиваний: 315

Автор: DarkMaster 3.6.2017, 5:39

Производительность тестил?

Автор: cirus 3.6.2017, 13:15

A* Lua, 2.0
Код
--lua
log ("clear") log ("mode compact")

-- ходы по диагонали, 0 - нет, 1 - да
angle = 0

-- параметры findcolor
find = {29, 25, 522, 516, 10, 10, "(0-16777215)", "%arr"}


-- создание карты
function Create_Map(startX, startY, endX, endY, stepX, stepY, color, array)
    local z = 0
    local a = findcolor(startX, startY, endX, endY, stepX, stepY, "(0-16777215)", "%arr")
    if  #arr > 0 then
        local map = {}
        for i = 1, ((endY - startY) / stepY) + 1 do
            map[i] = {}
            for j = 1, ((endX - startX) / stepX) + 1 do
                z = z + 1
                if  tonumber(arr[z][3]) == 0 then
                    map[i][j] = {1, arr[z][1], arr[z][2]}
                elseif tonumber(arr[z][3]) == 255 then
                    map[i][j] = {"A", arr[z][1], arr[z][2]}
                    -- точка A в массиве map
                    start = {}
                    start[1] = {i, j}
                elseif tonumber(arr[z][3]) == 16711680 then
                    map[i][j] = {"B", arr[z][1], arr[z][2]}
                     -- точка A в массиве map
                    ending = {}
                    ending[1] = {i, j}
                else
                    map[i][j] = {0, arr[z][1], arr[z][2]}
                end
            end
        end
        return map
    end
end


    -- поиск в массиве
function index_of(arr, text)
    local result = {}
    for i = 1, #arr  do
        if arr[i][7] == text then
            table.insert(result, {i, 7})
        end
    end
    return result
end

     -- A*
function A_star(map)
    local g, g1, h, f, tmp = 0, 0, 0, 0, 0
    local open, close, curr, index, resultclose, resultopen, vector = {}, {}, {}, {}, {}, {}, {}
    table.insert(vector, {-1, -1, 0, 1, 1, 1, 0, -1})
    table.insert(vector, {0, 1, 1, 1, 0, -1, -1, -1})

    table.insert(open, {0, 0, 0, 0, start [1][1], start [1][2], start [1][1]..":"..start [1][2], start [1][1], start [1][2]})
    while 1 == 1 do
        table.sort (open, function (a, b) return (a[1] < b[1]) end)
        curr [1] = {open [1][1], open [1][2], open [1][3], open [1][4], open [1][5], open [1][6], open [1][7], open [1][8], open [1][9]}
        table.insert(close, {open [1][1], open [1][2], open [1][3], open [1][4], open [1][5], open [1][6], open [1][7], open [1][8], open [1][9]})
        table.remove (open, 1)
        for i = 1, 8 do
            if angle == 1 or i%2 == 1 then
                local x, y = curr[1][5] + vector[1][i], curr[1][6] + vector[2][i]
                resultclose = index_of(close, x..":"..y)
                    if  x > 0 and y > 0 and x <= #map[i] and y <= #map then
                        if (map[x][y][1] == 0 or map[x][y][1] == "B") and #resultclose == 0 then
                            resultopen = index_of(open, x..":"..y)
                            if #resultopen == 0 then
                                if i%2 == 1 then g = curr[1][2] + 10 else g = curr[1][2] + 14 end
                                h = (math.abs(x - ending[1][1]) + math.abs(y - ending[1][2])) * 10
                                f = g + h
                                table.insert(open, {f, g, h, i, x, y, x..":"..y, curr[1][5], curr[1][6]})
                            else
                                tmp = resultopen [1][1]
                                g1 = open[tmp][2]
                                if i%2 == 1 then g = curr[1][2] + 10 else g = curr[1][2] + 14 end
                                if g < g1 then
                                    h = (math.abs(x - ending[1][1]) + math.abs(y - ending[1][2])) * 10
                                    f = g + h
                                    table.insert(open, {f, g, h, i, x, y, x..":"..y, curr[1][5], curr[1][6]})
                                end
                            end
                        end
                    end
            end
        end
        if  (curr [1][5] == ending [1][1] and curr [1][6] == ending [1][2]) or #open == 0 then
            break
        end
    end

    local pathAB, tmp, path = {}, {}, {}
    tmp [1] = {ending[1][1], ending[1][2]}
    table.insert(tmp, {close[#close][8], close[#close][9], close[#close][4]})

    while  tmp[#tmp][1] ~= start[1][1] or tmp[#tmp][2] ~= start[1][2] do
        pathAB = index_of(close, tmp[#tmp][1]..":"..tmp[#tmp][2])
        table.insert (tmp, {close[pathAB[1][1]][8], close[pathAB[1][1]][9], close[pathAB[1][1]][4]})
    end

    for i = #tmp, 1, -1 do
        table.insert (path, {map[tmp[i][1]][tmp[i][2]][2], map[tmp[i][1]][tmp[i][2]][3]})
    end
    return path
end

    -- создание карты
map = Create_Map(find[1],find[2],find[3],find[4],find[5],find[6],find[7],find[8])

    -- поиск пути
if #start > 0 and #ending > 0 then
    path = {}
    path = A_star(map)

      -- ход
    for i = 1, #path do
        kleft (path[i][1], path[i][2])
    end

elseif #start == 0 then
    log ("Точка A не найдена")
else
    log ("Точка B не найдена")
end

Подправил поиск в массиве. Теперь быстрее работает.
Цитата
Производительность тестил?

Как работает os.clock я не понял, точнее что за число он выдаёт. Так что точных данных нет.
На пилотовском языке на этот же лабиринт уходило примерно 20 секунд. На Lua меньше 1 секунды. Так что разница как минимум в 20 раз.

Встроенные функции пилота работают быстрее, чем те что написаны на lua самим? Например, пилотовский index_of быстрее выдаст результат, чем это?:
код
Код
    -- поиск в массиве
function index_of(arr, text)
    local result = {}
    for i = 1, #arr  do
        for j = 1, #arr[i] do
            if arr[i][j] == text then
                table.insert(result, {i, j})
            end
        end
    end
    return result
end

Автор: Cockney 3.6.2017, 13:54

Цитата
os.clock ()

Возвращает примерное количество времени в секундах, которое программа выполнялась на CPU.



Автор: cirus 3.6.2017, 14:09

Вроде понял.

Код
--lua
time_start = os.clock()
  -- какой-то код
time_end = os.clock() - time_start
   log (time_end)  -- время выполнения

Если так, то на создание карты и просчёт хода уходит 0.2 секунды.

Автор: cirus 3.6.2017, 18:06

A* Lua, 3.0
Код
--lua
time_start = os.clock()
log ("clear") log ("mode compact")

-- ходы по диагонали, 0 - нет, 1 - да
angle = 0

-- параметры findcolor
find = {5, 5, 620, 620, 1, 1, "(0-16777215)", "%arr"}          -- большой лабиринт
--find = {29, 25, 522, 516, 10, 10, "(0-16777215)", "%arr"}        -- маленький лабиринт


-- создание карты
function Create_Map(startX, startY, endX, endY, stepX, stepY, color, array)
    local z = 0
    local a = findcolor(startX, startY, endX, endY, stepX, stepY, "(0-16777215)", "%arr")
    if  #arr > 0 then
        local map = {}
        for i = 1, ((endY - startY) / stepY) + 1 do
            map[i] = {}
            for j = 1, ((endX - startX) / stepX) + 1 do
                z = z + 1
                if  tonumber(arr[z][3]) == 0 then
                    map[i][j] = {1, arr[z][1], arr[z][2]}
                elseif tonumber(arr[z][3]) == 255 then
                    map[i][j] = {"A", arr[z][1], arr[z][2]}
                    -- точка A в массиве map
                    start = {}
                    start[1] = {i, j}
                elseif tonumber(arr[z][3]) == 16711680 then
                    map[i][j] = {"B", arr[z][1], arr[z][2]}
                     -- точка A в массиве map
                    ending = {}
                    ending[1] = {i, j}
                else
                    map[i][j] = {0, arr[z][1], arr[z][2]}
                end
            end
        end
        return map
    end
end

     -- A*
function A_star(map)
    local g, g1, h, f, tmp = 0, 0, 0, 0, 0
    local open, close, curr, index, resultclose, resultopen, vector, close_map, open_map = {}, {}, {}, {}, {}, {}, {}, {}, {}

    for i = 1, #map do
        close_map [i] = {}
    end

    for i = 1, #map do
        open_map [i] = {}
        for j = 1, #map [i] do
            open_map[i][j] = {}
        end
    end

    table.insert(vector, {-1, -1, 0, 1, 1, 1, 0, -1})
    table.insert(vector, {0, 1, 1, 1, 0, -1, -1, -1})
    table.insert(open, {0, 0, 0, 0, start [1][1], start [1][2], start [1][1]..":"..start [1][2], start [1][1], start [1][2]})
    while 1 == 1 do
        table.sort (open, function (a, b) return (a[1] < b[1]) end)
        curr [1] = {open [1][1], open [1][2], open [1][3], open [1][4], open [1][5], open [1][6], open [1][7], open [1][8], open [1][9]}
        table.insert(close, {open [1][1], open [1][2], open [1][3], open [1][4], open [1][5], open [1][6], open [1][7], open [1][8], open [1][9]})
        close_map [open [1][5]][open [1][6]] = 1
        table.remove (open, 1)
        for i = 1, 8 do
            if angle == 1 or i%2 == 1 then
                local x, y = curr[1][5] + vector[1][i], curr[1][6] + vector[2][i]
                    if  x > 0 and y > 0 and x <= #map[i] and y <= #map then
                        if (map[x][y][1] == 0 or map[x][y][1] == "B") and close_map[x][y] == nil then
                            if open_map[x][y][1] == nil then
                                if i%2 == 1 then g = curr[1][2] + 10 else g = curr[1][2] + 14 end
                                h = (math.abs(x - ending[1][1]) + math.abs(y - ending[1][2])) * 10
                                f = g + h
                                table.insert(open, {f, g, h, i, x, y, x..":"..y, curr[1][5], curr[1][6]})
                                open_map[x][y] = {i, curr[1][5], curr[1][6]}
                            else
                                g1 = open_map[x][y][2]
                                if i%2 == 1 then g = curr[1][2] + 10 else g = curr[1][2] + 14 end
                                if g < g1 then
                                    h = (math.abs(x - ending[1][1]) + math.abs(y - ending[1][2])) * 10
                                    f = g + h
                                    table.insert(open, {f, g, h, i, x, y, x..":"..y, curr[1][5], curr[1][6]})
                                    open_map[x][y] = {i, curr[1][5], curr[1][6]}
                                end
                            end
                        end
                    end
            end
        end
        if  (curr [1][5] == ending [1][1] and curr [1][6] == ending [1][2]) or #open == 0 then
            break
        end
    end

    local pathAB, tmp, path = {}, {}, {}
    tmp [1] = {ending[1][1], ending[1][2]}
    table.insert(tmp, {close[#close][8], close[#close][9], close[#close][4]})

    while  tmp[#tmp][1] ~= start[1][1] or tmp[#tmp][2] ~= start[1][2] do
        table.insert (tmp,{open_map[tmp[#tmp][1]][tmp[#tmp][2]][2],open_map[tmp[#tmp][1]][tmp[#tmp][2]][3], open_map[tmp[#tmp][1]][tmp[#tmp][2]][1]})
    end

    for i = #tmp, 1, -1 do
        table.insert (path, {map[tmp[i][1]][tmp[i][2]][2], map[tmp[i][1]][tmp[i][2]][3], map[tmp[i][1]][tmp[i][2]][1]})
    end
    return path
end

    -- создание карты
map = Create_Map(find[1],find[2],find[3],find[4],find[5],find[6],find[7],find[8])
time_create_map = os.clock() - time_start
log ("Создание карты:".." "..time_create_map)
    -- поиск пути
if #start > 0 and #ending > 0 then
    path = {}
    path = A_star(map)
    time_create_path = os.clock() - time_start - time_create_map
    log ("Поиск пути:".." "..time_create_path)

      -- ход
    for i = 1, #path do
        kleft (path[i][1], path[i][2])
    end
    time_paint_path = os.clock() - time_start - time_create_path  - time_create_map
    log ("Рисование маршрута:".." "..time_paint_path)
    log ("Всего затрачено времени:".." "..time_create_map + time_create_path + time_paint_path)
elseif #start == 0 then
    log ("Точка A не найдена")
else
    log ("Точка B не найдена")
end

Чуть подправил. Теперь быстрее работает. На маленький лабиринт уходит 50 мсек.
Тест на лабиринте 601*601 пиксель : https://youtu.be/9aQd-s86yFk
Прикрепленное изображение

Автор: DarkMaster 5.6.2017, 23:19

Цитата
Тест на лабиринте 601*601 пиксель

Жесть!
А как ты генерировал эти лабиринты?

Цитата
Встроенные функции пилота работают быстрее, чем те что написаны на lua самим? Например, пилотовский index_of быстрее выдаст результат, чем это?

Это в первую очередь зависит от качества реализации функции. Естественно, что встроенная функция при трушной реализации будет шустрее ибо более низкоуровнево. На асме это будет еще быстрее, но можно же написать и коряво, тогда будет медленнее.

Автор: Cockney 6.6.2017, 1:01

На асме невозможно написать коряво,ибо кто пишет на асме не могут писать коряво. А если написано коряво, то это написано не на асме.

Да и как бы асм тут причем ? Тем более в контексте пилота и луа.

Автор: cirus 6.6.2017, 2:06

Цитата
А как ты генерировал эти лабиринты?

В поисковике скачать картинки лабиринта smile.gif
В алгоритмы построения лабиринтов не вникал, не было надобности.

Автор: dron4938 24.3.2018, 16:28

думал на ссд будет быстрее, оказалось ниочинь)
Маршрут рисует на 2сек быстрее

Изображение

Автор: cirus 24.3.2018, 17:09

Диск не влияет. Скорее всего только процессор, хотя тоже не понятно.
Создаётся и рисуется карта одинаково по времени, а вот поиск пути отличается:
i7 2600k 4.4 Ггц (разгон с 3.4 Ггц), ddr3 1600, win7 = 13 секунд.
i7 7700k 4.2 Ггц, dd4 2133, win10 = 4 секунды.

Автор: FREEON 29.3.2018, 18:17

Прикольно...а можно ли подобрать путь к выходам?

https://pp.userapi.com/c840138/v840138958/93b29/bQx9dXpJQyM.jpg

http://www.policyalmanac.org/games/aStarTutorial_rus.htm - ОПИСАНИЕ НЕ ДОСТУПНО!

Автор: cirus 30.3.2018, 12:37

В bmp или png картинку лабиринта скиньте.

Автор: FREEON 30.3.2018, 15:31

Цитата(cirus @ 30.3.2018, 12:37) *

В bmp или png картинку лабиринта скиньте.

Да лан это не важно...мне более интересно разобраться в алгоритме и настройках (чо-куда). Ссылка которая закреплена в шапке она не работает, а более подробной информации нет. Не могли бы вы ее продублировать на форуме либо где еще.

Автор: cirus 31.3.2018, 1:34

Цитата
Ссылка которая закреплена в шапке она не работает

Обновил. http://masters.donntu.org/2013/fknt/buga/library/AStar.htm

Автор: nykep 1.3.2023, 0:39

пытался для игры сделать поиск пути и получилась прикольная штука, он не ищет кратчайший путь, просто пробегает по дорожке, пока примитивно но всё же

Код
--lua
local startX, startY = 960, 520 --начало пути
local nextX, nextY = 960, 520   --начало пути
local ending = 0
local fat = 5  -- "толщина дорожки"
local pcolor = 0 -- цвет дорожки (как запихнуть в переменную набор цветов или диапазон я не знаю)
move (startX, startY)
wait (50)
kleft_down (startX, startY)
repeat
    local path = findcolor(startX - fat, startY - fat, startX + fat, startY + fat, pcolor, '%p', 2, -1, 0)
    if path then
        for i = 1, path do
            dist = math.sqrt((nextX - p[i][1])^2 + (nextY - p[i][2])^2)
            if i == 1 then
                max = dist
            end
            if max <= dist then
                max = dist
                nextX2 = p[i][1]
                nextY2 = p[i][2]
            end
        end
        nextX, nextY = startX, startY
        startX, startY = nextX2, nextY2
        ending = math.sqrt((startX - nextX)^2 + (startY - nextY)^2)
        move (startX, startY)
    end
until ending < max * 0.4 --должен дойти до конца дорожки

[+]

Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)