A*/Dijkstra算法简单实现(Pascal)

发布于 2025-01-01 04:42:38 字数 5319 浏览 2 评论 0原文

我正在尝试使用这篇文章链接。但我无法弄清楚我的代码出了什么问题(它找到了不正确的路径)。

在此处输入图像描述

而不是空的 begin ... end;应该是这一步:

如果它已经在打开列表中,请检查该路径是否指向该路径 平方更好,使用 G 成本作为衡量标准。较低的 G 成本意味着 这是一条更好的道路。如果是这样,请将正方形的父级更改为 当前方格,并重新计算该方格的G和F分数。

但我认为这并不重要,因为没有对角线运动。

uses
    crt;

const
    MAXX = 20;
    MAXY = 25;

type
    TArr = array [0..MAXY, 0..MAXX] of integer;
    
    TCell = record
        x: integer;
        y: integer;
    end;
    
    TListCell = record
        x: integer;
        y: integer;
        G: integer;
        parent: TCell;
    end;
    
    TListArr = array [1..10000] of TListCell;
    
    TList = record
        arr: TListArr;
        len: integer;
    end;

var
    i, j, minind, ind, c: integer;
    start, finish: TCell;
    current: TListCell;
    field: TArr;
    opened, closed: TList;

procedure ShowField;
var
    i, j: integer;
begin
    textcolor(15);
    for i := 0 to MAXX do
    begin
        for j := 0 to MAXY do
        begin
            case field[j, i] of
                99: textcolor(8);  // not walkable
                71: textcolor(14); // walkable
                11: textcolor(10); // start
                21: textcolor(12); // finish
                15: textcolor(2);  // path
                14: textcolor(5);
                16: textcolor(6);
            end;
            write(field[j, i], ' ');
        end;
        writeln;
    end;
    textcolor(15);
end; 


procedure AddClosed(a: TListCell);
begin
    closed.arr[closed.len + 1] := a;
    inc(closed.len);
end;


procedure AddOpened(x, y, G: integer);
begin
    opened.arr[opened.len + 1].x := x;
    opened.arr[opened.len + 1].y := y;
    opened.arr[opened.len + 1].G := G;
    inc(opened.len);
end;

procedure DelOpened(n: integer);
var
    i: integer;
begin
    AddClosed(opened.arr[n]);
    for i := n to opened.len - 1 do
        opened.arr[i] := opened.arr[i + 1];
    dec(opened.len);
end;


procedure SetParent(var a: TListCell; parx, pary: integer);
begin
    a.parent.x := parx;
    a.parent.y := pary;
end;


function GetMin(var a: TList): integer;
var
    i, min, mini: integer;
begin
    min := MaxInt;
    mini := 0;
    for i := 1 to a.len do
        if a.arr[i].G < min then
        begin
            min := a.arr[i].G;
            mini := i;
        end;
    
    GetMin := mini;
end;


function FindCell(a: TList; x, y: integer): integer;
var
    i: integer;
begin
    FindCell := 0;
    for i := 1 to a.len do
        if (a.arr[i].x = x) and (a.arr[i].y = y) then
        begin
            FindCell := i;
            break;
        end;
end;


procedure ProcessNeighbourCell(x, y: integer);
begin
    if (field[current.x + x, current.y + y] <> 99) then    // if walkable
        if (FindCell(closed, current.x + x, current.y + y) <= 0) then // and not visited before
            if (FindCell(opened, current.x + x, current.y + y) <= 0) then // and not added to list already
            begin
                AddOpened(current.x + x, current.y + y, current.G + 10);
                SetParent(opened.arr[opened.len], current.x, current.y);
                //  field[opened.arr[opened.len].x, opened.arr[opened.len].y]:=16;
            end
                else
            begin
                
            end;
end;


begin
    randomize;
    for i := 0 to MAXX do
        for j := 0 to MAXY do
            field[j, i] := 99;
    
    for i := 1 to MAXX - 1 do
        for j := 1 to MAXY - 1 do
            if random(5) mod 5 = 0 then
                field[j, i] := 99
            else field[j, i] := 71;
  
    // start and finish positions coordinates
    start.x := 5;
    start.y := 3;
    finish.x := 19;
    finish.y := 16;
    field[start.x, start.y] := 11;
    field[finish.x, finish.y] := 21;
    
    ShowField;
    
    writeln;
    
    opened.len := 0;
    closed.len := 0;
    AddOpened(start.x, start.y, 0);
    SetParent(opened.arr[opened.len], -1, -1);
    current.x := start.x;
    current.y := start.y;
    
    repeat
        minind := GetMin(opened);
        current.x := opened.arr[minind].x;
        current.y := opened.arr[minind].y;
        current.G := opened.arr[minind].G; 
        DelOpened(minind); 
        
        ProcessNeighbourCell(1, 0);  // look at the cell to the right
        ProcessNeighbourCell(-1, 0); // look at the cell to the left
        ProcessNeighbourCell(0, 1);  // look at the cell above
        ProcessNeighbourCell(0, -1); // look at the cell below
      
        if (FindCell(opened, finish.x, finish.y) > 0) then
            break;
    until opened.len = 0;
    
    // count and mark path
    c := 0;
    while ((current.x <> start.x) or (current.y <> start.y)) do
    begin
        field[current.x, current.y] := 15;
        ind := FindCell(closed, current.x, current.y);
        current.x := closed.arr[ind].parent.x;
        current.y := closed.arr[ind].parent.y;
        inc(c);
    end;
    
    
    ShowField;
    writeln(c);
    readln;
end.

编辑 2012 年 2 月 1 日:更新了代码,还修复了路径标记(应该有 or 代替 and),看起来现在可以工作了:)

I'm trying to implement A* path finding algorithm (now it's Dijkstra's algorithm i.e without heuristic) using this article Link. But I can't figure out what's wrong in my code (it finds incorrect path).

enter image description here

instead of the empty begin ... end; it should be this step:

If it is on the open list already, check to see if this path to that
square is better, using G cost as the measure. A lower G cost means
that this is a better path. If so, change the parent of the square to
the current square, and recalculate the G and F scores of the square.

but I think it is not important because there is no diagonal movement.

uses
    crt;

const
    MAXX = 20;
    MAXY = 25;

type
    TArr = array [0..MAXY, 0..MAXX] of integer;
    
    TCell = record
        x: integer;
        y: integer;
    end;
    
    TListCell = record
        x: integer;
        y: integer;
        G: integer;
        parent: TCell;
    end;
    
    TListArr = array [1..10000] of TListCell;
    
    TList = record
        arr: TListArr;
        len: integer;
    end;

var
    i, j, minind, ind, c: integer;
    start, finish: TCell;
    current: TListCell;
    field: TArr;
    opened, closed: TList;

procedure ShowField;
var
    i, j: integer;
begin
    textcolor(15);
    for i := 0 to MAXX do
    begin
        for j := 0 to MAXY do
        begin
            case field[j, i] of
                99: textcolor(8);  // not walkable
                71: textcolor(14); // walkable
                11: textcolor(10); // start
                21: textcolor(12); // finish
                15: textcolor(2);  // path
                14: textcolor(5);
                16: textcolor(6);
            end;
            write(field[j, i], ' ');
        end;
        writeln;
    end;
    textcolor(15);
end; 


procedure AddClosed(a: TListCell);
begin
    closed.arr[closed.len + 1] := a;
    inc(closed.len);
end;


procedure AddOpened(x, y, G: integer);
begin
    opened.arr[opened.len + 1].x := x;
    opened.arr[opened.len + 1].y := y;
    opened.arr[opened.len + 1].G := G;
    inc(opened.len);
end;

procedure DelOpened(n: integer);
var
    i: integer;
begin
    AddClosed(opened.arr[n]);
    for i := n to opened.len - 1 do
        opened.arr[i] := opened.arr[i + 1];
    dec(opened.len);
end;


procedure SetParent(var a: TListCell; parx, pary: integer);
begin
    a.parent.x := parx;
    a.parent.y := pary;
end;


function GetMin(var a: TList): integer;
var
    i, min, mini: integer;
begin
    min := MaxInt;
    mini := 0;
    for i := 1 to a.len do
        if a.arr[i].G < min then
        begin
            min := a.arr[i].G;
            mini := i;
        end;
    
    GetMin := mini;
end;


function FindCell(a: TList; x, y: integer): integer;
var
    i: integer;
begin
    FindCell := 0;
    for i := 1 to a.len do
        if (a.arr[i].x = x) and (a.arr[i].y = y) then
        begin
            FindCell := i;
            break;
        end;
end;


procedure ProcessNeighbourCell(x, y: integer);
begin
    if (field[current.x + x, current.y + y] <> 99) then    // if walkable
        if (FindCell(closed, current.x + x, current.y + y) <= 0) then // and not visited before
            if (FindCell(opened, current.x + x, current.y + y) <= 0) then // and not added to list already
            begin
                AddOpened(current.x + x, current.y + y, current.G + 10);
                SetParent(opened.arr[opened.len], current.x, current.y);
                //  field[opened.arr[opened.len].x, opened.arr[opened.len].y]:=16;
            end
                else
            begin
                
            end;
end;


begin
    randomize;
    for i := 0 to MAXX do
        for j := 0 to MAXY do
            field[j, i] := 99;
    
    for i := 1 to MAXX - 1 do
        for j := 1 to MAXY - 1 do
            if random(5) mod 5 = 0 then
                field[j, i] := 99
            else field[j, i] := 71;
  
    // start and finish positions coordinates
    start.x := 5;
    start.y := 3;
    finish.x := 19;
    finish.y := 16;
    field[start.x, start.y] := 11;
    field[finish.x, finish.y] := 21;
    
    ShowField;
    
    writeln;
    
    opened.len := 0;
    closed.len := 0;
    AddOpened(start.x, start.y, 0);
    SetParent(opened.arr[opened.len], -1, -1);
    current.x := start.x;
    current.y := start.y;
    
    repeat
        minind := GetMin(opened);
        current.x := opened.arr[minind].x;
        current.y := opened.arr[minind].y;
        current.G := opened.arr[minind].G; 
        DelOpened(minind); 
        
        ProcessNeighbourCell(1, 0);  // look at the cell to the right
        ProcessNeighbourCell(-1, 0); // look at the cell to the left
        ProcessNeighbourCell(0, 1);  // look at the cell above
        ProcessNeighbourCell(0, -1); // look at the cell below
      
        if (FindCell(opened, finish.x, finish.y) > 0) then
            break;
    until opened.len = 0;
    
    // count and mark path
    c := 0;
    while ((current.x <> start.x) or (current.y <> start.y)) do
    begin
        field[current.x, current.y] := 15;
        ind := FindCell(closed, current.x, current.y);
        current.x := closed.arr[ind].parent.x;
        current.y := closed.arr[ind].parent.y;
        inc(c);
    end;
    
    
    ShowField;
    writeln(c);
    readln;
end.

Edit Feb 1 '12: updated code, also fixed path marking (there should be or instead and), looks like it works now :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吐个泡泡 2025-01-08 04:42:38

您应该重写程序以使用循环而不是剪切和粘贴来访问每个邻居。如果这样做,您将避免出现如下错误:(

if (field[current.x, current.y - 1] <> 99) then
    if (FindCell(closed, current.x, current.y - 1) <= 0) then
        if (FindCell(opened, current.x + 1, current.y) <= 0) then

请参阅最后一行中不一致的 current.x + 1, current.y。)


关于循环,我正在考虑类似的事情this(伪Python):

neighbor_offsets = [(0, 1), (0, -1), (1, 0), (-1, 0)]
for offset in neighbor_offsets:
    neighbor = current + offset
    if is_walkable(neighbor) and not is_visited(neighbor):
        # Open 'neighbor' with 'current' as parent:
        open(neighbor, current)

        # Perhaps check if the goal is reached:
        if neighbor == finish:
            goal_reached = True
            break

如果您不编写循环而只是重构,

ProcessCell(x+1, y); 
ProcessCell(x-1, y); 
ProcessCell(x, y-1); 
ProcessCell(x, y-1);

那么这也是一个很大的改进。

You should rewrite the program to use a loop instead of cut-and-paste to visit each neighbor. If you do that you will avoid bugs like the following:

if (field[current.x, current.y - 1] <> 99) then
    if (FindCell(closed, current.x, current.y - 1) <= 0) then
        if (FindCell(opened, current.x + 1, current.y) <= 0) then

(See the inconsistent current.x + 1, current.y in the last line.)


With respect to the loop, I was thinking of something like this (pseudo-Python):

neighbor_offsets = [(0, 1), (0, -1), (1, 0), (-1, 0)]
for offset in neighbor_offsets:
    neighbor = current + offset
    if is_walkable(neighbor) and not is_visited(neighbor):
        # Open 'neighbor' with 'current' as parent:
        open(neighbor, current)

        # Perhaps check if the goal is reached:
        if neighbor == finish:
            goal_reached = True
            break

If you don't write a loop but just refactor to

ProcessCell(x+1, y); 
ProcessCell(x-1, y); 
ProcessCell(x, y-1); 
ProcessCell(x, y-1);

then that's a great improvement too.

↙温凉少女 2025-01-08 04:42:38

您发布了很多代码,您是否尝试过缩小失败的范围?

您是否将您的代码与维基百科上的伪代码进行了比较?

另请记住,dijkstra 只是 A*,启发式为 0。

编辑:

您链接的文章(我现在意识到这与我用来学习 A* 的文章完全相同,有趣)包含插图步骤。我建议您重新创建该地图/网格并在其上运行您的实现。然后逐步浏览图像:

  1. 八个初始邻居是否已添加到打开列表中?他们有正确的父母吗?
  2. 是否根据启发式选择了正确的开放节点作为下一个要扫描的节点?
  3. 关闭节点列表是否正确?
  4. 等等...

Youre posting quite a lot of code, have you tried narrow it down where it fails?

Have you compared your code with the pseudocode on wikipedia?

Also remember that dijkstra is just A* with a heuristic of 0.

Edit:

The article you linked (which I now realize is the very same I used to learn the A*, funny) contains illustrated steps. I would suggest that you recreate that map/grid and run your implementation on it. Then step through the images:

  1. Are the eight initial neighbors added to the open list? Do they have the correct parent?
  2. Is the correct open node picked as next to be scanned according to the heuristic?
  3. Is the list of closed nodes correct?
  4. And so on...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文