c语言实现A*寻路算法

2021-01-31  本文已影响0人  一路向后

1.算法简介

   A算法与最好优先贪婪算法一样都通过计算一个值来判断探索的方向。对于节点N,计算公式如下:F(N)=G(N)+H(N) 其中G(N)就是Dijkstra算法中计算的,从起点到当前节点N的移动消耗,而H(N),在只允许上下左右移动的前提下,就是最好优先贪婪算法中当前节点N到目标节点E的曼哈顿距离。因此,当节点间移动消耗非常小时,G对F的影响也会微乎其微,A算法就退化为最好优先贪婪算法;当节点间移动消耗非常大以至于H对F的影响微乎其微时,A*算法就退化为Dijkstra算法。

2.源码实现

#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>
#include <math.h>   

int map[20][20] =    
{   
    { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 },
    { 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
    { 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};

//节点结构体   
typedef struct Node   
{
    int f, g, h;
    int row;        //该节点所在行
    int col;        //该节点所在列
    int direction;      //parent节点要移动的方向就能到达本节点
    struct Node *parent;
} Node, *PNode;

//OPEN CLOSED 表结构体
typedef struct Stack
{
    PNode npoint;
    struct Stack *next;
} Stack, *PStack;

int directionIndex = 0;     //方向
int direction[256];     //方向数组
int rows = 20;          //地图行数
int cols = 20;          //地图列数
int G_OFFSET = 1;       //每个图块G值的增加值
int destinationRow;     //目标所在行
int destinationCol;     //目标所在列
int canMoveIndex = 0;       //可以通行的地图图块索引
int tileSize = 1;       //图块大小

PStack Open = NULL;
PStack Closed = NULL;

//选取OPEN表上f值最小的节点,返回该节点地址
Node *getNodeFromOpen()
{
    PStack temp = Open->next, min = Open->next, minp = Open;
    PNode minx;

    if(temp == NULL)
        return NULL;

    while(temp->next != NULL)
    {
        if((temp->next->npoint->f) < (min->npoint->f))
        {
            min = temp->next;
            minp = temp;
        }

        temp = temp->next;
    }

    minx = min->npoint;
    temp = minp->next;
    minp->next = minp->next->next;

    free(temp);

    return minx;
}

//判断节点是否相等,相等,不相等   
short Equal(PNode a, PNode b)
{
    if((a->row == b->row) && (a->col == b->col))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

//判断节点是否属于OPEN表,是则返回节点地址,否则返回空地址
PNode BelongInOpen(PNode u)
{
    PStack temp = Open->next;

    if(temp == NULL)
        return NULL;

    while(temp != NULL)
    {
        if(Equal(u, temp->npoint))
        {
            return temp->npoint;
        }
        else
        {
            temp = temp->next;
        }
    }

    return NULL;
}

//判断节点是否属于CLOSED表,是则返回节点地址,否则返回空地址
PNode BelongInClosed(PNode u)
{
    PStack temp = Closed->next;

    if(temp == NULL)
        return NULL;

    while(temp != NULL)
    {
        if(Equal(u, temp->npoint))
        {
            return temp->npoint;
        }
        else
        {
            temp = temp->next;
        }
    }

    return NULL;
}

//把节点放入OPEN表中
void PutintoOpen(Node *u)
{
    Stack *temp;
    temp =(Stack *)malloc(sizeof(Stack));
    temp->npoint = u;
    temp->next = Open->next;
    Open->next = temp;
}

//把节点放入CLOSED表中
void PutintoClosed(Node *u)
{
    Stack *temp;
    temp =(Stack *)malloc(sizeof(Stack));
    temp->npoint = u;
    temp->next = Closed->next;
    Closed->next = temp;
}

//得到该图块的H值
int getH(int row, int col)   
{   
    return (abs(destinationRow - row) + abs(destinationCol - col));
}

//得到该位置所在地图行
int getRowPosition(int y)
{
    return (y / tileSize);
}

//得到该位置所在地图列   
int getColPosition(int x)
{
    return (x / tileSize);
}

//检测该图块是否可通行
short isCanMove(int col, int row)
{
    if(col < 0 || col >= cols)
        return 0;
    if(row < 0 || row >= rows)
        return 0;

    return map[col][row] == canMoveIndex;
}

/*检查坐标是否在open列表里*/
PNode checkOpen(int row, int col)
{
    PStack temp = Open->next;

    if(temp == NULL)
        return NULL;

    while(temp != NULL)
    {
        if((temp->npoint->row == row) && (temp->npoint->col == col))
        {
            return temp->npoint;
        }
        else
        {
            temp = temp->next;
        }
    }

    return NULL;
}

/*检查坐标是否在closed列表里*/
short isInClose(int row, int col)
{
    PStack temp = Closed->next;

    if(temp == NULL)
        return 0;

    while(temp != NULL)
    {
        if((temp->npoint->row == row) && (temp->npoint->col == col))
        {
            return 1;
        }
        else
        {
            temp = temp->next;
        }
    }

    return 0;
}

/*一步处理逻辑*/
void creatSeccessionNode(PNode bestNode, int row, int col)
{
    int g = bestNode->g + G_OFFSET;

    if(!isInClose(row, col))
    {
        PNode oldNode = NULL;

        //如果在open列表里则更新, 如果不在则添加至open列表
        if((oldNode=checkOpen(row, col)) != NULL)
        {
            if(oldNode->g < g)
            {
                oldNode->parent = bestNode;
                oldNode->g = g;
                oldNode->f = g + oldNode->h;
            }
        }
        else
        {
            PNode node = (PNode)malloc(sizeof(Node));
            node->parent = bestNode;
            node->g = g;
            node->h = getH(row, col);
            node->f = node->g + node->h;
            node->row = row;
            node->col = col;
            directionIndex++;
            node->direction = directionIndex;
            PutintoOpen(node);
        }
    }
}
       
/**  
 * 根据传入的节点生成子节点  
 * @param bestNode  
 * @param destinationRow  
 * @param destinationCol  
 */   
void seachSeccessionNode(PNode bestNode)   
{
    int row, col;

    //上部节点
    if(isCanMove(row = bestNode->row - 1, col = bestNode->col))
    {
        creatSeccessionNode(bestNode, row, col);
    }

    //下部节点
    if(isCanMove(row = bestNode->row + 1, col = bestNode->col))
    {
        creatSeccessionNode(bestNode, row, col);
    }

    //左部节点
    if(isCanMove(row = bestNode->row, col = bestNode->col - 1))
    {
        creatSeccessionNode(bestNode, row, col);
    }

    //右部节点
    if(isCanMove(row = bestNode->row, col = bestNode->col + 1))
    {
        creatSeccessionNode(bestNode, row, col);
    }

    //传入节点放入close列表
    PutintoClosed(bestNode);
}

//遍历open链表
void printfOpenData()
{
    PStack temp = Open->next;
    PNode p_node;

    if(temp == NULL)
        return;

    while(temp != NULL)
        {
        PStack head = temp;
        temp = temp->next;
        p_node = head->npoint;
        printf("Open库数据! [%d,%d]\n", p_node->col, p_node->row);
        free(p_node);
        free(head);
        Open->next = temp;
    }

    printf("\nOpen库数据 数据全部清除\n");

    return;
}

//遍历close链表
void printfClosedData()
{
    PStack temp = Closed->next;
    PNode p_node;

    if(temp == NULL)
        return;

    while(temp != NULL)
        {
        PStack head = temp;
        temp = temp->next;
        p_node = head->npoint;
        printf("Closed库数据! [%d,%d]\n", p_node->col, p_node->row);
        free(p_node);
        free(head);
        Closed->next = temp;
    }

    printf("\nClosed库数据 数据全部清除\n");

    return;
}

void getPath(int startX, int StartY, int destinationX, int destinationY)
{
    PNode startNode = (PNode)malloc(sizeof(Node));
    PNode bestNode  = NULL;
    int index = 0;

    destinationRow = getRowPosition(destinationY);
    destinationCol = getColPosition(destinationX);
       
    startNode->parent= NULL;
    startNode->row = getRowPosition(StartY);
    startNode->col = getColPosition(startX);
    startNode->g = 0;
    startNode->h = getH(startNode->row, startNode->col);
    startNode->f = startNode->g + startNode->h;
    startNode->direction = 0;

    PutintoOpen(startNode);
       
    while(1)
    {
        //从OPEN表中取出f值最小的节点
        bestNode = getNodeFromOpen();

        if(bestNode == NULL)
        {
            printf("未找到路径\n");
            return;
        }
        else if(bestNode->row == destinationRow && bestNode->col == destinationCol)
        {
            //寻路结束,打印相关信息
            PNode _Node = bestNode;
            int nodeSum = 0;
            int nodeIndex =0;

            printf("程序运行次数=%d\n", index);

            while(_Node->parent != NULL)
            {
                printf("x:%d  y:%d  direction = %d \n", _Node->col, _Node->row, _Node->direction);
                _Node = _Node->parent;
                nodeSum += 1;
            }

            printf("节点数量=%d\n", nodeSum);

            _Node = bestNode;
            nodeIndex = nodeSum-1;

            while(_Node->parent != NULL && nodeIndex>=0)
            {
                PNode _NodeParent = _Node->parent;

                printf("x:%d  y:%d  direction = %d \n", _Node->col, _Node->row, _Node->direction);

                if(_NodeParent->col - _Node->col == 0 && _NodeParent->row - _Node->row == +1)
                {
                    direction[nodeIndex] = 1;
                }
                else if(_NodeParent->col - _Node->col == 0 && _NodeParent->row - _Node->row == -1)
                {
                    direction[nodeIndex] = 2;
                }
                else if(_NodeParent->col - _Node->col == +1 && _NodeParent->row - _Node->row == 0)
                {
                    direction[nodeIndex] = 3;
                }
                else if(_NodeParent->col - _Node->col == -1 && _NodeParent->row - _Node->row == 0)
                {
                    direction[nodeIndex] = 4;
                }
                else
                {
                    direction[nodeIndex] = 0;
                }

                nodeIndex -= 1;
                _Node = _Node->parent;
            }

            for(nodeIndex=0; nodeIndex<nodeSum; nodeIndex++)
            {
                printf("direction[%d]=%d\n",nodeIndex,direction[nodeIndex]);
            }

            return;
        }

        index++;
        seachSeccessionNode(bestNode);
    }
}

//主函数
void main()   
{
    //初始操作,建立open和closed表
    Open = (PStack)malloc(sizeof(Stack));
    Open->next = NULL;
    Closed = (PStack)malloc(sizeof(Stack));
    Closed->next = NULL;

    getPath(0, 0, 19, 19);

    printfOpenData();
    printfClosedData();

    free(Open);
    free(Closed);
}

3.编译源码

$ gcc -o example example.c

4.运行及结果

$ ./example
程序运行次数=53
x:19  y:19  direction = 84 
x:18  y:19  direction = 82 
x:17  y:19  direction = 81 
x:16  y:19  direction = 80 
x:15  y:19  direction = 79 
x:14  y:19  direction = 76 
x:14  y:18  direction = 75 
x:14  y:17  direction = 69 
x:14  y:16  direction = 68 
x:13  y:16  direction = 66 
x:12  y:16  direction = 64 
x:11  y:16  direction = 62 
x:10  y:16  direction = 60 
x:9  y:16  direction = 58 
x:8  y:16  direction = 56 
x:7  y:16  direction = 54 
x:6  y:16  direction = 52 
x:5  y:16  direction = 50 
x:4  y:16  direction = 48 
x:3  y:16  direction = 46 
x:2  y:16  direction = 43 
x:2  y:15  direction = 28 
x:2  y:14  direction = 26 
x:2  y:13  direction = 24 
x:2  y:12  direction = 22 
x:2  y:11  direction = 20 
x:2  y:10  direction = 18 
x:2  y:9  direction = 16 
x:2  y:8  direction = 14 
x:2  y:7  direction = 12 
x:2  y:6  direction = 10 
x:2  y:5  direction = 8 
x:2  y:4  direction = 7 
x:2  y:3  direction = 6 
x:2  y:2  direction = 5 
x:2  y:1  direction = 4 
x:1  y:1  direction = 3 
x:1  y:0  direction = 2 
节点数量=38
x:19  y:19  direction = 84 
x:18  y:19  direction = 82 
x:17  y:19  direction = 81 
x:16  y:19  direction = 80 
x:15  y:19  direction = 79 
x:14  y:19  direction = 76 
x:14  y:18  direction = 75 
x:14  y:17  direction = 69 
x:14  y:16  direction = 68 
x:13  y:16  direction = 66 
x:12  y:16  direction = 64 
x:11  y:16  direction = 62 
x:10  y:16  direction = 60 
x:9  y:16  direction = 58 
x:8  y:16  direction = 56 
x:7  y:16  direction = 54 
x:6  y:16  direction = 52 
x:5  y:16  direction = 50 
x:4  y:16  direction = 48 
x:3  y:16  direction = 46 
x:2  y:16  direction = 43 
x:2  y:15  direction = 28 
x:2  y:14  direction = 26 
x:2  y:13  direction = 24 
x:2  y:12  direction = 22 
x:2  y:11  direction = 20 
x:2  y:10  direction = 18 
x:2  y:9  direction = 16 
x:2  y:8  direction = 14 
x:2  y:7  direction = 12 
x:2  y:6  direction = 10 
x:2  y:5  direction = 8 
x:2  y:4  direction = 7 
x:2  y:3  direction = 6 
x:2  y:2  direction = 5 
x:2  y:1  direction = 4 
x:1  y:1  direction = 3 
x:1  y:0  direction = 2 
direction[0]=4
direction[1]=2
direction[2]=4
direction[3]=2
direction[4]=2
direction[5]=2
direction[6]=2
direction[7]=2
direction[8]=2
direction[9]=2
direction[10]=2
direction[11]=2
direction[12]=2
direction[13]=2
direction[14]=2
direction[15]=2
direction[16]=2
direction[17]=2
direction[18]=4
direction[19]=4
direction[20]=4
direction[21]=4
direction[22]=4
direction[23]=4
direction[24]=4
direction[25]=4
direction[26]=4
direction[27]=4
direction[28]=4
direction[29]=4
direction[30]=2
direction[31]=2
direction[32]=2
direction[33]=4
direction[34]=4
direction[35]=4
direction[36]=4
direction[37]=4
Open库数据! [18,18]
Open库数据! [13,19]
Open库数据! [13,18]
Open库数据! [16,15]
Open库数据! [15,17]
Open库数据! [13,17]
Open库数据! [12,17]
Open库数据! [11,17]
Open库数据! [10,17]
Open库数据! [9,17]
Open库数据! [8,17]
Open库数据! [7,17]
Open库数据! [6,17]
Open库数据! [5,17]
Open库数据! [4,17]
Open库数据! [3,17]
Open库数据! [1,16]
Open库数据! [2,17]
Open库数据! [14,13]
Open库数据! [1,14]
Open库数据! [1,13]
Open库数据! [1,12]
Open库数据! [1,11]
Open库数据! [1,10]
Open库数据! [1,9]
Open库数据! [1,8]
Open库数据! [1,7]
Open库数据! [1,6]
Open库数据! [1,5]
Open库数据! [1,4]
Open库数据! [0,1]

Open库数据 数据全部清除
Closed库数据! [18,19]
Closed库数据! [17,19]
Closed库数据! [16,19]
Closed库数据! [15,19]
Closed库数据! [14,19]
Closed库数据! [14,18]
Closed库数据! [14,17]
Closed库数据! [16,17]
Closed库数据! [16,16]
Closed库数据! [15,16]
Closed库数据! [14,16]
Closed库数据! [13,16]
Closed库数据! [12,16]
Closed库数据! [11,16]
Closed库数据! [10,16]
Closed库数据! [9,16]
Closed库数据! [8,16]
Closed库数据! [7,16]
Closed库数据! [6,16]
Closed库数据! [5,16]
Closed库数据! [4,16]
Closed库数据! [3,16]
Closed库数据! [2,16]
Closed库数据! [2,15]
Closed库数据! [14,14]
Closed库数据! [13,14]
Closed库数据! [12,14]
Closed库数据! [11,14]
Closed库数据! [10,14]
Closed库数据! [9,14]
Closed库数据! [8,14]
Closed库数据! [7,14]
Closed库数据! [6,14]
Closed库数据! [5,14]
Closed库数据! [4,14]
Closed库数据! [3,14]
Closed库数据! [2,14]
Closed库数据! [2,13]
Closed库数据! [2,12]
Closed库数据! [2,11]
Closed库数据! [2,10]
Closed库数据! [2,9]
Closed库数据! [2,8]
Closed库数据! [2,7]
Closed库数据! [2,6]
Closed库数据! [2,5]
Closed库数据! [2,4]
Closed库数据! [2,3]
Closed库数据! [2,2]
Closed库数据! [2,1]
Closed库数据! [1,1]
Closed库数据! [1,0]
Closed库数据! [0,0]

Closed库数据 数据全部清除
上一篇下一篇

猜你喜欢

热点阅读