0%

Erlang实现A星寻路

概述

Erlang实现A星寻路

需求

Erlang实现A星寻路

图示

img.png

img_1.png

相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
-module(a_sweep_trace2).
-export([
generate_route/8
, generate_route/7
, generate_route/5
, expand_points/2
]).

%% 八个方向 走九宫格
-define(EXPAND_POINTS_8(X, Y), [
{X - 1, Y}, {X - 1, Y - 1},
{X, Y - 1}, {X + 1, Y - 1},
{X + 1, Y}, {X + 1, Y + 1},
{X, Y + 1}, {X - 1, Y + 1}
]).

%% 六个方向 走六宫格(地图为蜂窝六边形)
-define(EXPAND_POINTS_6(X, Y), [
{X, Y + 1}, {X + 1, Y},
{X + 1, Y - 1}, {X, Y - 1},
{X - 1, Y}, {X - 1, Y + 1}
]).

%% 四个方向 走十字
-define(EXPAND_POINTS_4(X, Y), [
{X - 1, Y}, {X + 1, Y},
{X, Y + 1}, {X, Y - 1}
]).

%% 障碍物
-define(BLOCKS, [{3, 4}, {4, 3}, {3, 2}, {1, 2}]).


%% 扩展查找的点
-record(point, {
x = 0 :: integer(), %% 当前点位X
y = 0 :: integer(), %% 当前点位Y
g = 0 :: integer(), %% G = 走过的步数
h = 0 :: integer(), %% H = 曼哈顿距离
f = 0 :: integer(), %% F = G + H
p_x = 0 :: integer(), %% 前一个位置
p_y = 0 :: integer() %% 前一个位置
}).

expand_points(X, Y) ->
?EXPAND_POINTS_4(X, Y).

-define(DEBUG(F), io:format("##[~w~w:~w] " ++ F ++ "~n", [self(), ?MODULE, ?LINE])).
-define(DEBUG(F, A), io:format("##[~w~w:~w] " ++ F ++ "~n", [self(), ?MODULE, ?LINE | A])).

%% a_sweep_trace2:generate_route(1,3,5,2,1).

%% [{2,3},{3,3},{4,3},{4,2},{5,2}] 没有墙
%% [{2,3},{2,2},{2,1},{3,1},{4,1},{4,2},{5,2}] 有墙
generate_route(StartX, StartY, TargetX, TargetY, SceneId) ->
generate_route(StartX, StartY, TargetX, TargetY, SceneId, 50, 50).

generate_route(StartX, StartY, TargetX, TargetY, SceneId, MaxTraceRangeX, MaxTraceRangeY) ->
generate_route(StartX, StartY, TargetX, TargetY, SceneId, MaxTraceRangeX, MaxTraceRangeY, 0).

generate_route(StartX, StartY, TargetX, TargetY, SceneId, MaxTraceRangeX, MaxTraceRangeY, Flying) ->
%% 初始化开始点坐标
CostH = calculate_cost(StartX, StartY, TargetX, TargetY),
CostG = 0,
OpenPoints = [#point{x = StartX, y = StartY, g = CostG, h = CostH, f = CostG + CostH}],
%% 目标点坐标
case search(OpenPoints, [], #point{x = TargetX, y = TargetY}, SceneId, MaxTraceRangeX, MaxTraceRangeY, Flying) of
false ->
false;
{Point, ResultOpenPoints, ResultClosePoints} ->
Solution = generate_solution(Point, [], ResultClosePoints ++ ResultOpenPoints),
PosList = [{LX, LY} || #point{x = LX, y = LY} <- Solution],
PosList
end.

%% 查找目标点
%% param: OpenPoints 扩充列表,closePoints 已遍历列表,TargetPoint 目标点
search([], _ClosePoints, _TargetPoint, _SceneId, _MaxTraceRangeX, _MaxTraceRangeY, _Flying) ->
false;
search(OpenPoints, ClosePoints, TargetPoint, SceneId, MaxTraceRangeX, MaxTraceRangeY, Flying) ->
?DEBUG("OpenPoints:~p end", [OpenPoints]),
[Point | NewOpenPoints] = lists:keysort(#point.f, OpenPoints),
NewClosePoints = [Point | ClosePoints],
case is_target(Point, TargetPoint) of
true ->
{Point, NewOpenPoints, NewClosePoints};
false ->
%% 未找到,
AddOpenPoints = expand(NewOpenPoints, NewClosePoints, Point, TargetPoint, SceneId, MaxTraceRangeX, MaxTraceRangeY, Flying),
%% 坐标点扩充
search(AddOpenPoints ++ NewOpenPoints, NewClosePoints, TargetPoint, SceneId, MaxTraceRangeX, MaxTraceRangeY, Flying)
end.

%% 坐标点扩充
expand(OpenPoints, ClosePoints, #point{x = X, y = Y, g = G}, #point{x = TargetX, y = TargetY}, SceneId, MaxTraceRangeX, MaxTraceRangeY, Flying) ->
CostG = G + 1,
lists:foldl(fun({X1, Y1}, AccIn) ->
case is_blocked(X1, Y1, TargetX, TargetY, SceneId, MaxTraceRangeX, MaxTraceRangeY, Flying) of
true ->
AccIn;
false ->
case lists:any(fun(#point{x = OpentX, y = OpentY}) ->
(OpentX == X1 andalso OpentY == Y1) end, OpenPoints) of
false ->
case lists:any(fun(#point{x = CloseX, y = CloseY}) ->
(CloseX == X1 andalso CloseY == Y1) end, ClosePoints) of
false ->
CostH = calculate_cost(X1, Y1, TargetX, TargetY),
[#point{x = X1, y = Y1, g = CostG, h = CostH, f = CostH + CostG, p_x = X, p_y = Y} | AccIn];
true ->
%% 已在 close 列表 中不加
AccIn
end;
true ->
%% 已在 opent 列表 中不加
AccIn
end
end
end, [], ?EXPAND_POINTS_4(X, Y)).

%% 生成结果列表
generate_solution(Point = #point{p_x = X, p_y = Y}, SolutionPoints, Points) ->
%% 找到终点前面的格子A 找到格子A前面的格子B 找到格子B前面的格子C
PPoints = [Info || Info = #point{x = X2, y = Y2} <- Points, X2 == X, Y2 == Y],
case PPoints of
[] ->
SolutionPoints;
[One] ->
generate_solution(One, [Point | SolutionPoints], lists:delete(One, Points));
[NewPoint | _] = Tmp ->
%% 一般来说不会走到这里来
?DEBUG("Tmp:~p end", [Tmp]),
generate_solution(NewPoint, [Point | SolutionPoints], lists:delete(NewPoint, Points))
end.

%% 权重计算
calculate_cost(X1, Y1, X2, Y2) ->
((X1 - X2) * (X1 - X2) + (Y1 - Y2) * (Y1 - Y2)).

%% 是否到达目标点了,到达目标周围点就可以了
is_target(#point{x = StartX, y = StartY}, #point{x = TargetX, y = TargetY}) ->
StartX == TargetX andalso StartY == TargetY.
%% abs(StartX - TargetX) =< 1 andalso abs(StartY - TargetY) =<1.


%% 检查是否是边界或者障碍物 {3,3}, {5,2}
is_blocked(X, Y, TargetX, TargetY, _SceneId, MaxTraceRangeX, MaxTraceRangeY, _Flying) ->
case abs(TargetX - X) > MaxTraceRangeX orelse
abs(TargetY - Y) > MaxTraceRangeY of
true ->
%% 在查找范围外,作为阻档处理
true;
false ->
%% 在查找范围内
lists:member({X, Y}, ?BLOCKS)
end.