AStar 算法(erlang版本)

来源:互联网 发布:java二级考试 编辑:程序博客网 时间:2024/06/06 02:08

A* 算法的erlang实现,下面的代码在get_neighbour_coord函数里面使用到了判断坐标是否可走的map:is_movable_coord 函数,需要自己去实现并替换。

以下是源码:

%%=========================astar.erl=====================================

-module(astar).

%% ====================================================================
%% API functions
%% ====================================================================
-export([find_path_by_astar/5]).

-record(pathcoord, {g=0,f=0,now_pos={},parent_coord={}}).

%%=========================================================
%%        A*寻路算法 written by pjc
%%=========================================================
%% 假设下面是一张小地图
%%  y ----------------------------------------------------------------------------------
%%  * |(0,9)  | (1,9)  | (2,9) | (3,9) | (4,9) | (5,9)  | (6,9) | (7,9) | (8,9) | (9,9) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,8)  | (1,8)  | (2,8) | (3,8) | (4,8) | (5,8)  | (6,8) | (7,8) | (8,8) | (9,8) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,7)  | (1,7)  | (2,7) | (3,7) | (4,7) | (5,7)  | (6,7) | (7,7) | (8,7) | (9,7) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,6)  | (1,6)  | (2,6) | (3,6) | (4,6) | (5,6)  | (6,6) | (7,6) | (8,6) | (9,6) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,5)  | (1,5)  | (2,5) | (3,5) | (4,5) | (5,5)  | (6,5) | (7,5) | (8,5) | (9,5) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,4)  | (1,4)  | (2,4) | (3,4) | (4,4) | (5,4)  | (6,4) | (7,4) | (8,4) | (9,4) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,3)  | (1,3)  | (2,3) | (3,3) | (4,3) | (5,3)  | (6,3) | (7,3) | (8,3) | (9,3) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,2)  | (1,2)  | (2,2) | (3,2) | (4,2) | (5,2)  | (6,2) | (7,2) | (8,2) | (9,2) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,1)  | (1,1)  | (2,1) | (3,1) | (4,1) | (5,1)  | (6,1) | (7,1) | (8,1) | (9,1) |
%%  * |-------|--------|-------|-------|-------|--------|-------|-------|-------|-------|
%%  * |(0,0)  | (1,0)  | (2,0) | (3,0) | (4,0) | (5,0)  | (6,0) | (7,0) | (8,0) | (9,0) |
%%  0 ----------------------------------------------------------------------------------> x

find_path_by_astar(SceneId,Src_x,Src_y,Des_x,Des_y) ->     
    CheckDict = dict:new(),
    UncheckDict = dict:new(),
    SrcPathCoord =#pathcoord{g=0,f=0,now_pos={Src_x,Src_y}},
    NewUncheckDict = dict:store({Src_x,Src_y}, SrcPathCoord, UncheckDict),
    find_path_by_astar(SceneId,{Src_x,Src_y},{Des_x,Des_y},
                        CheckDict, NewUncheckDict,[SrcPathCoord]).

find_path_by_astar(SceneId,SrcCoord,DesCoord,CheckDict,UncheckDict,UncheckList) ->
    Unlen = erlang:length(UncheckList),
    if
        Unlen =< 0    -> []; %%结束(未找到路径)
        Unlen > 0    ->
            CurrPathCoord = lists:last(UncheckList),
            {CurX,CurY} = CurrPathCoord#pathcoord.now_pos,
            UncheckList1=lists:delete(CurrPathCoord, UncheckList),
            UncheckDict1 = dict:erase({CurX,CurY}, UncheckDict),
            CheckDict1 = dict:store({CurX,CurY}, CurrPathCoord, CheckDict),
            
            case {CurX,CurY} =:= DesCoord of
                true-> generate_final_path(DesCoord,CheckDict1,[]);%%找到目标点
                _    ->
                    NeiList1 = get_neighbour_coord(SceneId,CurX,CurY),
                    
                    %%过滤已经在checkdict中坐标,转为pathcoord
                    NeiList2 = [{X,Y}|| {X,Y} <- NeiList1,dict:find({X,Y}, CheckDict1)==error],
                    NeiList3 = covert_to_path_coord(CurrPathCoord,DesCoord,NeiList2,[]),

                    %%判断是否在uncheckdict和 UncheckList中,比较G+F值的大小,如果更优,则加入uncheck list
                    Exist = [ PathCoord|| PathCoord <- NeiList3,
                                          dict:find(PathCoord#pathcoord.now_pos, UncheckDict1)=/=error],
                    {UncheckList2,UncheckDict2} = filter_exist_coord(Exist,UncheckList1, UncheckDict1),
                    
                    %%把新的坐标加到uncheck list和uncheck dict,然后排序uncheck list(逆序)
                    UncheckDict3 = add_new_coord(NeiList3, UncheckDict2),
                    UncheckList3 = lists:append(NeiList3, UncheckList2),
                    UncheckList4 = lists:sort(fun(Coord1,Coord2) ->
                            if Coord1#pathcoord.f =< Coord2#pathcoord.f -> false;
                                true -> true
                            end end, UncheckList3),
                     find_path_by_astar(SceneId,SrcCoord, DesCoord,CheckDict1, UncheckDict3,UncheckList4)    
            end
    end.

generate_final_path(CurCoord,CheckDict,Path) ->
    case erlang:tuple_size(CurCoord) =< 0 of
        true-> Path;
        _     ->
            {ok,Value} = dict:find(CurCoord, CheckDict),
            Path1 = lists:append([CurCoord], Path),
            Parent = Value#pathcoord.parent_coord,
            generate_final_path(Parent,CheckDict,Path1)
    end.

covert_to_path_coord(ParentPathCoord,{DesX,DesY}=DesCoord,InList,OutList) ->
    case erlang:length(InList)>0 of
            false -> OutList;
        _ ->
            [{CurX,CurY}|LeftCoord]=InList,
            {Px,Py}=ParentPathCoord#pathcoord.now_pos,
            Tmp_g = ParentPathCoord#pathcoord.g + cal_a_star_g(CurX,CurY,Px,Py),
            Tmp_f = cal_a_star_h(CurX,CurY,DesX,DesY)+Tmp_g,
            NewPathCoord = #pathcoord{g=Tmp_g,f=Tmp_f,now_pos={CurX,CurY},parent_coord={Px,Py}},
            OutList1 = lists:append([NewPathCoord],OutList),
            covert_to_path_coord(ParentPathCoord,DesCoord,LeftCoord,OutList1)
    end.

%% 把NewCoord加到Dict中
add_new_coord(NewCoordList,Dict) ->
    case erlang:length(NewCoordList)>0 of
        false -> Dict;
        _ ->
            [Coord|LeftCoord]=NewCoordList,
            add_new_coord(LeftCoord, dict:store(Coord#pathcoord.now_pos, Coord, Dict))
    end.

filter_exist_coord(ExistCoord,UncheckList,UncheckDict) ->
    case erlang:length(ExistCoord)>0 of
        false ->{UncheckList,UncheckDict};
        _ ->
            [Coord|LeftCoord]=ExistCoord,
            {pathcoord,G,F,Pos,Parent} = Coord,
            
            {Ret,UncheckDict3} =
                case dict:find(Pos, UncheckDict) of
                    {ok,{pathcoord,G1,F1,Pos1,Parent1}} ->
                        if F < F1 ->
                                UncheckDict1=dict:erase(Pos1, UncheckDict),
                                {true,dict:store(Pos, Coord, UncheckDict1)};
                            true -> {false,UncheckDict}
                        end;
                    _ -> {false,UncheckDict}
                end,
            UncheckList1=
                case Ret of
                    true -> lists:append([Coord], UncheckList);
                    _ -> UncheckList
                end,
            filter_exist_coord(LeftCoord,UncheckList1,UncheckDict3)
    end.

%%根据9宫格算出{Coord_x,Coord_y}的相邻坐标中的可走坐标,返回数组[{x,y}]
get_neighbour_coord(SceneId,Coord_x,Coord_y) ->
    Sudoku=[{-1,1},{0,1},{1,1},{-1,0},{1,0},{-1,-1},{0,-1},{1,-1}],
    List = [{Coord_x+X, Coord_y+Y} || {X,Y} <- Sudoku],
    [{X,Y} || {X,Y} <- List,X>=0,Y>=0,map:is_movable_coord(SceneId,X, Y)==true].

%%当前点到目标点都的h值
cal_a_star_h(Src_x,Src_y,Des_x,Des_y) ->
    (abs((Src_x - Des_x))+abs((Src_y - Des_y)))*10.

%%相邻点的g值
cal_a_star_g(Src_x,Src_y,Des_x,Des_y) ->
    if Src_x==Des_x,abs(Src_y-Des_y)=:=1
          orelse Src_y==Des_y,abs(Src_x-Des_x)=:=1 -> 10;%%直线距离10
        true -> 14 %%斜线距离14
    end.
0 0