最近搞MTK斯凯冒泡平台的游戏开发,碰到了自动寻路的问题,很多程序员都知道A*算法,既简单有使用!
所以我也选择了A*算法,由于时间比较紧,就在网上百度此算法的C实现,确实有很多!
但经测试都有不同的问题,并不能用在商业游戏中,所以最后决定还是自己写吧!
A*原理 比较简单,网上有很多介绍的!我也是在网上看的,这里就不重复了!
由于我是Java程序员刚开始搞嵌入式C开发不久,所以有很多C用法不是很熟悉,通过搞这个算法又知道不少知识
比如
Java里的集合 C里要用链表
这也是此算法比较重要的一个技术点,遍历链表,还有删减节点,这些对于C程序员来说应该都是很简单的事情,
这里还是说一下,以便那些从JAVA转入C开发的程序员快速理解
PS:
这里使用的是 前插式单向链表
1. 数据定义
- typedef struct Node
- {//节点结构体
- int f,g,h;
- int row; //该节点所在行
- int col; //该节点所在列
- int direction;//parent节点要移动的方向就能到达本节点
- struct Node * parent;
- }Node, *Lnode;
- typedef struct Stack
- {//OPEN CLOSED 表结构体
- Node * npoint;
- struct Stack * next;
- }Stack, *Lstack;
2. 插入链表
链表有头,有下一个节点的指针,新建的链表都是 HEAD->NULL,就是 头 下一个节点是NULL,
增加节点是在 头和NEXT之间插入的,这时就是 HEAD->NEXT0->NULL,
再增加一个节点: HEAD->NEXT1->NEXT0->NULL,
插入链表的代码示例
- void PutintoOpen(Node * suc )
- {//把节点放入OPEN 或CLOSED 表中
- Stack * temp;
- temp =(Stack *) malloc(sizeof(Stack));
- temp->npoint = suc;
- temp->next = Open->next;
- Open->next = temp;
- }
3. 链表上节点的删除
有时,需要将一个节点从链表中删除,比如当前链表数据为 HEAD->NEXT1->NEXT0->NULL,
将其中的NEXT1节点删除(释放)掉,
那么就需要先有一个指针指向NEXT1
然后把HEAD指向NEXT1的下一个节点,也就是NEXT0
最后再free( NEXT1 );
删除链表某节点的示例代码
- Node * getNodeFromOpen()
- {//选取OPEN表上f值最小的节点,返回该节点地址
- Lstack temp = Open->next,min = Open->next,minp = Open;
- Node * 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;
- }
4. 遍历链表
链表的遍历,就是从HEAD开始,按下一个节点,顺序找到一个为NULL的节点就遍历完整个链表了!
遍历链表的代码示例
- Node * BelongInOpen( Node * suc )
- {//判断节点是否属于OPEN表或CLOSED表,是则返回节点地址,否则返回空地址
- Lstack temp = Open -> next ;
- if(temp == NULL)
- return NULL;
- while( temp != NULL )
- {
- if( Equal(suc,temp->npoint) )
- {
- return temp -> npoint;
- }
- else
- {
- temp = temp->next;
- }
- }
- return NULL;
- }
5. 链表的删除
链表,链表上节点的数据都是在程序中临时创建出来的,这些数据都使用的是堆内存,当链表不在使用的时候需要手动释放,
链表的删除代码示例
- //正序遍历链表
- void printfOpenData()
- {//判断节点是否属于OPEN表或CLOSED表,是则返回节点地址,否则返回空地址
- Lstack temp = Open -> next;
- Node *p_node;
- if(temp == NULL)
- return;
- while(temp != NULL)
- {
- Lstack 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("\n Open库数据 数据全部清楚 \n");
- return;
- }
好了,链表的操作基本讲完了!
接下来的全部代码应该都比较好理解,整个算法的演示程序仅有一个 .c文件即可,您可以在TC, VC6, C-Free5 下编译通过!
A*算法 C语言实现的代码示例
- /*******************************************************************************
- * CopyRight (c) HYTC Ltd. All rights reserved.
- * Filename: main.c
- * Creator: GaoLei
- * Version: 0.0
- * Date: 2011-06-15
- * QQ: 38929568
- * Description: A*寻路算法 测试类
- *******************************************************************************/
- #include <stdlib.h>
- #include <stdio.h>
- #include <math.h>
- #define FALSE 0
- #define TRUE 1
- #define NULL 0
- typedef int BOOL;
- 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, *Lnode;
- typedef struct Stack
- {//OPEN CLOSED 表结构体
- Node * npoint;
- struct Stack * next;
- }Stack, *Lstack;
- int rows = 20; //地图行数
- int cols = 20; //地图列数
- int G_OFFSET = 1; //每个图块G值的增加值
- int destinationRow; //目标所在行
- int destinationCol; //目标所在列
- int canMoveIndex = 0; //可以通行的地图图块索引
- int tileSize = 1; //图块大小
- Lstack Open = NULL;
- Lstack Closed = NULL;
- Node * getNodeFromOpen()
- {//选取OPEN表上f值最小的节点,返回该节点地址
- Lstack temp = Open->next,min = Open->next,minp = Open;
- Node * 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;
- }
- BOOL Equal(Node * suc,Node * goal)
- {//判断节点是否相等,相等,不相等
- if ( (suc->row == goal->row) && (suc->col == goal->col) )
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
- Node * BelongInOpen( Node * suc )
- {//判断节点是否属于OPEN表或CLOSED表,是则返回节点地址,否则返回空地址
- Lstack temp = Open -> next ;
- if(temp == NULL)
- return NULL;
- while( temp != NULL )
- {
- if( Equal(suc,temp->npoint) )
- {
- return temp -> npoint;
- }
- else
- {
- temp = temp->next;
- }
- }
- return NULL;
- }
- Node * BelongInClosed( Node * suc )
- {//判断节点是否属于OPEN表或CLOSED表,是则返回节点地址,否则返回空地址
- Lstack temp = Closed -> next ;
- if(temp == NULL)
- return NULL;
- while(temp != NULL)
- {
- if( Equal(suc,temp->npoint) )
- {
- return temp -> npoint;
- }
- else
- {
- temp = temp->next;
- }
- }
- return NULL;
- }
- void PutintoOpen(Node * suc )
- {//把节点放入OPEN 或CLOSED 表中
- Stack * temp;
- temp =(Stack *) malloc(sizeof(Stack));
- temp->npoint = suc;
- temp->next = Open->next;
- Open->next = temp;
- }
- void PutintoClosed(Node * suc )
- {//把节点放入OPEN 或CLOSED 表中
- Stack * temp;
- temp =(Stack *) malloc(sizeof(Stack));
- temp->npoint = suc;
- 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);
- }
- //检测该图块是否可通行
- BOOL isCanMove(int col, int row)
- {
- if(col < 0 || col >= cols)
- return FALSE;
- if(row < 0 || row >= rows)
- return FALSE;
- return map[col][row] == canMoveIndex;
- }
- Node* checkOpen(int row, int col)
- {
- Lstack 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;
- }
- BOOL isInClose(int row, int col)
- {
- Lstack temp = Closed -> next;
- if ( temp == NULL )
- return FALSE;
- while (temp != NULL)
- {
- if ( (temp->npoint->row==row) && (temp->npoint->col == col) )
- {
- return TRUE;
- }
- else
- {
- temp = temp->next;
- }
- }
- return FALSE;
- }
- int directionIndex =0;
- int direction[256];
- void creatSeccessionNode(Node *bestNode, int row, int col)
- {
- int g = bestNode->g + G_OFFSET;
- if(!isInClose(row, col))
- {
- Node *oldNode = NULL;
- if((oldNode = checkOpen(row, col)) != NULL)
- {
- if(oldNode->g < g)
- {
- oldNode->parent = bestNode;
- oldNode->g = g;
- oldNode->f = g + oldNode->h;
- }
- }
- else
- {
- Node *node = (Node *) 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;
- // openNode.addElement(node);
- PutintoOpen( node );
- }
- }
- }
- /**
- * 根据传入的节点生成子节点
- * @param bestNode
- * @param destinationRow
- * @param destinationCol
- */
- void seachSeccessionNode(Node *bestNode)
- {
- int row, col;
- // Node *bestNodeInOpen = NULL;
- //上部节点
- 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);
- }
- PutintoClosed( bestNode );
- }
- //正序遍历链表
- void printfOpenData()
- {//判断节点是否属于OPEN表或CLOSED表,是则返回节点地址,否则返回空地址
- Lstack temp = Open -> next;
- Node *p_node;
- if(temp == NULL)
- return;
- while(temp != NULL)
- {
- Lstack 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("\n Open库数据 数据全部清楚 \n");
- return;
- }
- void printfClosedData()
- {//判断节点是否属于OPEN表或CLOSED表,是则返回节点地址,否则返回空地址
- Lstack temp = Closed -> next ;
- Node *p_node;
- if(temp == NULL)
- return;
- while(temp != NULL)
- {
- Lstack 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("\n Closed库数据 数据全部清楚 \n");
- /*
- temp = Closed -> next;
- while(temp != NULL)
- {
- printf("Closed库数据!节点");
- temp = temp->next;
- }*/
- return;
- }
- void getPath(int startX, int StartY, int destinationX, int destinationY)
- {
- Node *startNode = (Node *) malloc(sizeof(Node));
- Node *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 );// openNode.add(startNode);
- while(TRUE)
- {
- bestNode = getNodeFromOpen(); //从OPEN表中取出f值最小的节点
- if(bestNode == NULL)//未找到路径
- {
- printf("未找到路径\n");
- return;
- }
- else if(bestNode->row == destinationRow
- && bestNode->col == destinationCol )
- {
- Node *_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)
- {
- Node *_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 = (Stack *) malloc(sizeof(Stack));
- Open->next = NULL;
- Closed = (Stack *) malloc(sizeof(Stack));
- Closed->next = NULL;
- //-----------------------------------
- getPath( 0, 0, 19, 19 );
- printf("程序认定该起始状态无法道达目标状态!\n");
- printfOpenData();
- printfClosedData();
- free(Open);
- free(Closed);
- //--------------------------------------
- }
本程序很容易修改成您工程需要的形式,比如
我的使用方式是这样的
- //将main() 方法改成
- void autoPathfinding( uint16 *MD, uint8 map_w, uint8 map_h, int16 startX, int16 startY, int16 destinationX, int16 destinationY )
这样的目的是 将地图数据 以及当前位置,和目标位置传递个 自动寻路方法
还需要修改 能否通过的方法,可以根据自己的地图来做处理
- //检测该图块是否可通行
- BOOL isCanMove(int col, int row)
- {
- if(col < 0 || col >= cols)
- return FALSE;
- if(row < 0 || row >= rows)
- return FALSE;
- //return map[col][row] == canMoveIndex;
- return isCanGoByMapTile( MapData[ (col)*rows+(row) ] );
- }
PS:
看源代码的时候,如果你觉得有些变量定义的位置不舒服,是因为我的嵌入式C所用的环境要求,当然您可以根据您的环境自由修改!
时间关系,并没有特别优化,如果您有好的优化方案,我愿意聆听!
转载于:https://blog.51cto.com/kome2000/590579