AStar A* 算法的Erlang实现

%% @author rolong@vip.qq.com
%% 本代码来自 瑞仙的Erlang开发博客
%% http://blog.csdn.net/zhongruixian

-module(astar1).

-export([
         find_path/2
         ,test/2
        ]).

-record(state, {
          open_trees %% 开启列表
          ,close_sets %% 关闭列表
          ,parents_trees %% 父节点列表
          ,xy %% 目标点
         }).

-record(point, {
          xy = {0, 0}
          ,g = 0
          ,h = 0
          ,f = 0
         }).

%% 1为向上,按顺时针依次为1~8
-define(DIRS, [1,2,3,4,5,6,7,8]).


%%' API
test(XY1, XY2) ->
    Path = find_path(XY1, XY2),
    io:format("Path:~n~p~n", [Path]),
    show_path(Path).

find_path(XY1, XY2) ->
    State = #state{
               open_trees = gb_trees:empty()
               ,close_sets = gb_sets:new()
               ,xy = XY2
               ,parents_trees = gb_trees:empty()
              },
    StartPoint = #point{xy = XY1},
    OpenTrees = gb_trees:enter(XY1, {0, 0, 0}, State#state.open_trees),
    State1 = State#state{open_trees = OpenTrees},
    find_next_point(StartPoint, State1).
%%.

%%' priv

-spec walkable({X, Y}) -> true | false when
      X :: integer(),
      Y :: integer().

%%'walkable函数根据具体点阵实现
%% 示例如下:

walkable({X, Y}) ->
    {Row, Col} = map(size),
    case X >= 0 andalso Y >= 0 andalso X < Row andalso Y < Col of
        true ->
            Block = map(block),
            Nth = X + Y * Row + 1,
            case string:substr(Block, Nth, 1) of
                "1" -> true;
                "0" -> false
            end;
        false -> false
    end;
walkable(_) -> false.

map(size) ->
    {20, 10};
map(block) ->
    %01234567890123456789
    "11111111111111111111" % 0
    "11111111111111111111" % 1
    "11111111111111111111" % 2
    "11111111000111111111" % 3
    "11111111000111111111" % 4
    "11111111000111111111" % 5
    "11111111000111111111" % 6
    "11111111000111111111" % 7
    "11111111000111111111" % 8
    "11111111111111111111" % 9
    .
%%.

find_next_point(#point{xy = XY}, #state{xy = XY} = State) ->
    get_path(XY, State#state.parents_trees, [XY]);

find_next_point(#point{xy = XY} = CurPoint, State) ->
    State1 = open2close(CurPoint, State),
    %% 将周围点添加到开放列表中
    AroundPoints = find_around_points(CurPoint, State1),
    State2 = add_open_trees(AroundPoints, XY, State1),
    case find_min_f_point(State2) of
        none ->
            io:format("Error coord:~w~n", [CurPoint#point.xy]),
            [];
        NextPoint ->
            find_next_point(NextPoint, State2)
    end.

find_min_f_point(State) ->
    Iter = gb_trees:iterator(State#state.open_trees),
    find_min_f_point(Iter, -1, none).

find_min_f_point(Iter, F, Point) ->
    case gb_trees:next(Iter) of
        none -> Point;
        {XY, {G1, H1, F1}, Iter1} ->
            case F1 < F orelse F =:= -1 of
                true ->
                    Point1 = #point{
                                xy = XY
                                ,g = G1
                                ,h = H1
                                ,f = F1
                               },
                    find_min_f_point(Iter1, F1, Point1);
                false ->
                    find_min_f_point(Iter1, F, Point)
            end
    end.

xy2point({CurX, CurY}, ParentPoint, {DstX, DstY}) ->
    #point{
       xy = {X, Y}
       ,g = G
      } = ParentPoint,
    AddG = if
               CurX =:= X -> 10;
               CurY =:= Y -> 10;
               true -> 14
           end,
    CurH = (erlang:abs(CurX - DstX) + erlang:abs(CurY - DstY)) * 10,
    CurG = G + AddG,
    #point{
       xy = {CurX, CurY}
       ,g = CurG
       ,h = CurH
       ,f = CurG + CurH
      }.

%% 找出周围点
find_around_points(Point, State) ->
    #state{
       close_sets = CloseSets
      } = State,
    #point{
       xy = {X, Y}
      } = Point,
    F = fun(Dir, Acc) ->
                XY1 = get_next_coord(Dir, X, Y),
                case walkable(XY1) of
                    true ->
                        case gb_sets:is_element(XY1, CloseSets) of
                            true -> Acc;
                            false ->
                                Point1  = xy2point(XY1, Point, State#state.xy),
                                [Point1 | Acc]
                        end;
                    false -> Acc
                end
        end,
    lists:foldl(F, [], ?DIRS).

add_open_trees([Point | Tail], ParentXY, State) ->
    case gb_trees:lookup(Point#point.xy, State#state.open_trees) of
        {_XY, {G, _H, _F}} ->
            case Point#point.g < G of
                true ->
                    State1 = add_open_trees1(Point, ParentXY, State),
                    add_open_trees(Tail, ParentXY, State1);
                false ->
                    add_open_trees(Tail, ParentXY, State)
            end;
        none ->
            State1 = add_open_trees1(Point, ParentXY, State),
            add_open_trees(Tail, ParentXY, State1)
    end;
add_open_trees([], _ParentXY, State) ->
    State.


add_open_trees1(Point, ParentXY, State) ->
    #point{xy = XY, g = G, h = H, f = F} = Point,
    OpenTrees1 = gb_trees:enter(XY, {G, H, F}, State#state.open_trees),
    ParentsTrees1 = gb_trees:enter(XY, ParentXY, State#state.parents_trees),
    State#state{
      open_trees = OpenTrees1
      ,parents_trees = ParentsTrees1
     }.

open2close(Point, State) ->
    OpenTrees = gb_trees:delete(Point#point.xy, State#state.open_trees),
    CloseSets = gb_sets:add(Point#point.xy, State#state.close_sets),
    State#state{
      open_trees = OpenTrees
      ,close_sets = CloseSets
     }.

get_next_coord(1,X,Y)->
	{X,Y-1};
get_next_coord(2,X,Y)->
	{X+1,Y-1};
get_next_coord(3,X,Y)->
	{X+1,Y};
get_next_coord(4,X,Y)->
	{X+1,Y+1};
get_next_coord(5,X,Y)->
	{X,Y+1};
get_next_coord(6,X,Y)->
	{X-1,Y+1};
get_next_coord(7,X,Y)->
	{X-1,Y};
get_next_coord(8,X,Y)->
	{X-1,Y-1}.

get_path(XY, ParentsTrees, Acc) ->
    case gb_trees:lookup(XY, ParentsTrees) of
        none -> Acc;
        {value, XY1} ->
            get_path(XY1, ParentsTrees, [XY1 | Acc])
    end.

%%'用于测试打印
show_path(XYs) ->
    Block = map(block),
    {Row, Col} = map(size),
    Len = Row * Col,
    show_path(XYs, Row, Col, Len, Block).

show_path([{X, Y} | XYs], Row, Col, Len, Block) ->
    LeftLen = X + Y * Row,
    RightLen = Len - LeftLen - 1,
    Left = string:left(Block, LeftLen),
    Right = string:right(Block, RightLen),
    Block1 = Left ++ "*" ++ Right,
    show_path(XYs, Row, Col, Len, Block1);
show_path([], Row, _Col, Len, Block) ->
    show_path1(Row, Len, Block, "").

show_path1(_Row, Len, _Block, Acc) when Len =< 0 ->
    io:format("~n~s~n", [Acc]);
show_path1(Row, Len, Block, Acc) ->
    Len1 = Len - Row,
    Left = string:left(Block, Row),
    Block1 = string:right(Block, Len1),
    Acc1 = Acc ++ Left ++ "\n",
    show_path1(Row, Len1, Block1, Acc1).
%%.
%%.

%%% vim: set foldmethod=marker filetype=erlang foldmarker=%%',%%.:


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,以下是A*算法的Python实现代码: ```python import heapq class Node: def __init__(self, x, y, reachable=True): self.x = x self.y = y self.reachable = reachable self.parent = None self.g = 0 self.h = 0 self.f = 0 def __lt__(self, other): return self.f < other.f class AStar: def __init__(self, grid): self.open = [] heapq.heapify(self.open) self.close = set() self.grid = grid def find_path(self, start, end): start_node = Node(*start) end_node = Node(*end) heapq.heappush(self.open, start_node) while len(self.open) > 0: current_node = heapq.heappop(self.open) self.close.add(current_node) if current_node.x == end_node.x and current_node.y == end_node.y: path = [] while current_node.parent: path.append((current_node.x, current_node.y)) current_node = current_node.parent path.append((start_node.x, start_node.y)) return path[::-1] neighbors = self.get_neighbors(current_node) for neighbor in neighbors: if neighbor not in self.close: if neighbor not in self.open: neighbor.g = current_node.g + 1 neighbor.h = self.get_distance(neighbor, end_node) neighbor.f = neighbor.g + neighbor.h neighbor.parent = current_node heapq.heappush(self.open, neighbor) else: new_g = current_node.g + 1 if new_g < neighbor.g: neighbor.g = new_g neighbor.f = neighbor.g + neighbor.h neighbor.parent = current_node return None def get_distance(self, node, end): return abs(node.x - end.x) + abs(node.y - end.y) def get_neighbors(self, node): neighbors = [] for x, y in [(0, 1), (0, -1), (1, 0), (-1, 0)]: new_x = node.x + x new_y = node.y + y if new_x < 0 or new_x >= len(self.grid) or new_y < 0 or new_y >= len(self.grid[0]): continue if not self.grid[new_x][new_y].reachable: continue neighbors.append(Node(new_x, new_y)) return neighbors ``` 这里定义了一个`Node`类来表示网格中的一个节点,包含了节点的坐标、是否可以到达、父节点、G值、H值和F值。其中,G值表示从起始点到当前节点的实际距离,H值表示从当前节点到终点的估计距离,F值是G值和H值的和,用来决定下一个要扩展的节点。在实现中,使用了一个`open`列表来存储待扩展的节点,并按F值从小到大排序;使用一个`close`集合来存储已经扩展过的节点,避免重复扩展。在每次扩展时,更新节点的G值、H值和F值,并将其加入到`open`列表中。最后,如果找到了终点,则从终点开始回溯,得到路径。 具体使用方法可以参考以下代码: ```python grid = [[Node(x, y) for y in range(10)] for x in range(10)] grid[2][3].reachable = False grid[3][3].reachable = False start = (0, 0) end = (9, 9) astar = AStar(grid) path = astar.find_path(start, end) if path: print(path) else: print("No path found.") ``` 这里构造了一个10x10的网格,并将第(2,3)和(3,3)个节点设置为不可达。然后,定义了起始点和终点,并创建了一个`AStar`对象。最后,调用`find_path`方法来查找路径,如果找到了路径,则打印出来,否则打印"No path found."。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值