当前位置:   article > 正文

C++之A*算法的简单实现_c++ a*算法

c++ a*算法


目录

何为A*算法

伪代码

代码


活动地址:CSDN21天学习挑战赛

何为A*算法

A*算法(启发式搜索)的首要条件是在静态路网中,相对于广度优先遍历(BFS)求最短路径的最有效算法(BFS缺点是效率低耗时久,而A*更高效)

启发式搜索,高薪开发者必会的人工智能寻路算法

A*BFC
高效率且耗时短效率低

 路径规划是指的是机器人的最优路径规划问题,即依据某个或某些优化准则(如工作代价最小、行走路径最短、行走时间最短等),在工作空间中找到一个从起始状态到目标状态能避开障碍物的最优路径。机器人的路径规划应用场景极丰富,最常见如游戏中NPC及控制角色的位置移动,百度地图等导航问题,小到家庭扫地机器人、无人机大到各公司正争相开拓的无人驾驶汽车等。

A*算法原理:

 在计算机科学中,A*算法作为Dijkstra算法的扩展,因其高效性而被广泛应用于寻路及图的遍历,如星际争霸等游戏中就大量使用。在理解算法前,我们需要知道几个概念:

  • 搜索区域(The Search Area):图中的搜索区域被划分为了简单的二维数组,数组每个元素对应一个小方格,当然我们也可以将区域等分成是五角星,矩形等,通常将一个单位的中心点称之为搜索区域节点(Node)。  
  • 开放列表(Open List):我们将路径规划过程中待检测的节点存放于Open List中,而已检测过的格子则存放于Close List中。
  • 父节点(parent):在路径规划中用于回溯的节点,开发时可考虑为双向链表结构中的父结点指针。
  • 路径排序(Path Sorting):具体往哪个节点移动由以下公式确定:F(n) = G + H 。G代表的是从初始位置A沿着已生成的路径到指定待检测格子的移动开销。H指定待测格子到目标节点B的估计移动开销。
  • 启发函数(Heuristics Function):H为启发函数,也被认为是一种试探,由于在找到唯一路径前,我们不确定在前面会出现什么障碍物,因此用了一种计算H的算法,具体根据实际场景决定。在我们简化的模型中,H采用的是传统的曼哈顿距离(Manhattan Distance),也就是横纵向走的距离之和。

A*算法公式:

F= G + H + W

f:代价
g:已经付出的代价
h:预估代价
w:权重
   就是不讲人话

g:已经付出的代价
    直接累加
h:预估代价
    只算直线距离 并且 无视障碍 即为曼哈顿距离

每走一步 都计算 周围能走的点的F值  并存到数组中 并入树
找出数组中f值最小的点  走上去
循环继续 遇到终点 循环结束   

伪代码

  1. int open_list;//一个记录下所有被考虑来寻找最短路径的格子
  2. int close_list; //一个记录下不会再被考虑的格子
  3. typedef struct point{
  4. bool Is_Wall;
  5. struct point* father;//父节点
  6. int G;// 表示从起点 A 移动到网格上指定方格的移动耗费 (上下左右,还可沿斜方向移动)
  7. int old_G;//旧G 第一次:从起点 A 直接移动到 A 四周方格的移动耗费 ;上次更新得到的G
  8. int new_G; //新G 从起点 A 经过当前搜索中心点到其四周指定点的移动耗费
  9. int H;//表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 这里我们设定只可以上下左右移动)
  10. int F=G+H;//表示该点的总耗费
  11. }Point;
  12. point* start_point;
  13. point* end_point;
  14. point* min_point;
  15. point* now_point;

代码

  1. #include <stdio.h>
  2. #include <vector>//动态数组
  3. using namespace std;
  4. #define ROWS 10
  5. #define COLS 10
  6. //直线代价
  7. #define ZXDJ 10
  8. //斜线代价
  9. #define XXDJ 14
  10. enum direct{ p_up, p_down, p_left, p_right,
  11. p_lup, p_ldown, p_rup, p_rdown };
  12. //存下标
  13. struct MyPoint{
  14. int row;
  15. int col;
  16. int f;
  17. int g;
  18. int h;
  19. };
  20. //树的节点类型
  21. struct treeNode
  22. {
  23. MyPoint pos;
  24. vector<treeNode*> child;
  25. treeNode* pParent;
  26. };
  27. //创建树节点
  28. treeNode* createTreeNode(int row, int col);
  29. //计算h值并返回
  30. int getH(MyPoint pos, MyPoint endPos);
  31. int main(){
  32. int map[ROWS][COLS] = {
  33. { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
  34. { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
  35. { 0, 1, 0, 1, 1, 0, 0, 0, 0, 0 },
  36. { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 },
  37. { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
  38. { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
  39. { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
  40. { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
  41. { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
  42. { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }
  43. };
  44. //标记每个点是否走过
  45. bool pathMap[ROWS][COLS] = { 0 };//0 没有走过 false 1 true 走过
  46. MyPoint begPos = { 1, 1 };
  47. MyPoint endPos = { 6, 7 };
  48. //标记起点走过
  49. pathMap[begPos.row][begPos.col] = true;
  50. //准备一颗树
  51. treeNode* pRoot = NULL;
  52. //起点入树
  53. pRoot = createTreeNode(begPos.row, begPos.col);
  54. vector<treeNode*> buff;//准备一个数组
  55. bool isFindEnd = false;
  56. treeNode* current = pRoot;
  57. vector<treeNode*>::iterator it;
  58. vector<treeNode*>::iterator itMin;
  59. while (1){
  60. //1 把当前点周围能走的点找出来
  61. for (int i = 0; i < 8; i++){
  62. treeNode* pChild = createTreeNode(current->pos.row,
  63. current->pos.col);
  64. switch (i){
  65. case p_up:
  66. pChild->pos.row--;
  67. pChild->pos.g += ZXDJ;
  68. break;
  69. case p_down:
  70. pChild->pos.row++;
  71. pChild->pos.g += ZXDJ;
  72. break;
  73. case p_left:
  74. pChild->pos.col--;
  75. pChild->pos.g += ZXDJ;
  76. break;
  77. case p_right:
  78. pChild->pos.col++;
  79. pChild->pos.g += ZXDJ;
  80. break;
  81. case p_lup:
  82. pChild->pos.col--;
  83. pChild->pos.row--;
  84. pChild->pos.g += XXDJ;
  85. break;
  86. case p_ldown:
  87. pChild->pos.col--;
  88. pChild->pos.row++;
  89. pChild->pos.g += XXDJ;
  90. break;
  91. case p_rup:
  92. pChild->pos.col++;
  93. pChild->pos.row--;
  94. pChild->pos.g += XXDJ;
  95. break;
  96. case p_rdown:
  97. pChild->pos.col++;
  98. pChild->pos.row++;
  99. pChild->pos.g += XXDJ;
  100. break;
  101. }
  102. //2 判断能不能走
  103. if (map[pChild->pos.row][pChild->pos.col] != 1 && //不是障碍物
  104. pathMap[pChild->pos.row][pChild->pos.col] == false
  105. ){//能走
  106. //的计算出f值 入树,存入数组
  107. pChild->pos.h = getH(pChild->pos, endPos);
  108. pChild->pos.f = pChild->pos.g + pChild->pos.h;
  109. //入树
  110. current->child.push_back(pChild);
  111. pChild->pParent = current;
  112. //存入数组
  113. buff.push_back(pChild);
  114. }
  115. else{//不能走
  116. //delete pChild;
  117. }
  118. }
  119. //3 找出数组中f值最小的点,走上去
  120. itMin = buff.begin();//假设第一个的f值最小
  121. for (it = buff.begin(); it != buff.end(); it++){
  122. itMin =( ( (*it)->pos.f < (*itMin)->pos.f ) ? it : itMin );
  123. }
  124. //走上去
  125. current = *itMin;
  126. //标记走过
  127. pathMap[current->pos.row][current->pos.col] = true;
  128. //从数组中删掉
  129. buff.erase(itMin);
  130. //4 判断是否找到终点了
  131. if (current->pos.row == endPos.row &&
  132. current->pos.col == endPos.col){
  133. isFindEnd = true;
  134. break;
  135. }
  136. //5 判断数组是否为空 为空说明都试过了找不到终点
  137. if (0 == buff.size()) break;
  138. }
  139. if (isFindEnd){
  140. printf("找到终点了!\n");
  141. while (current){
  142. printf("(%d,%d)", current->pos.row, current->pos.col);
  143. current = current->pParent;
  144. }
  145. printf("\n");
  146. }
  147. while (1);
  148. return 0;
  149. }
  150. //创建树节点
  151. treeNode* createTreeNode(int row,int col){
  152. treeNode* pNew = new treeNode;//开内存
  153. memset(pNew, 0, sizeof(treeNode));//全部赋值为NULL
  154. pNew->pos.row = row;
  155. pNew->pos.col = col;
  156. return pNew;
  157. }
  158. //计算h值并返回
  159. int getH(MyPoint pos, MyPoint endPos){
  160. int x = ((pos.col>endPos.col)?(pos.col-endPos.col):(endPos.col-pos.col));
  161. int y = ((pos.row>endPos.row) ? (pos.row - endPos.row) : (endPos.row - pos.row));
  162. return ZXDJ*(x + y);
  163. }
  1. //转载 https://blog.csdn.net/dujuancao11/article/details/109749219?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166081157616780366520908%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166081157616780366520908&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-109749219-null-null.142^v41^pc_rank_v36,185^v2^control&utm_term=A*%E7%AE%97%E6%B3%95&spm=1018.2226.3001.4187
  2. import time,sys
  3. from PyQt5.QtWidgets import QDialogButtonBox,QDialog,QMainWindow,QGridLayout,QTextEdit,QLineEdit,QWidget, QMessageBox, QApplication,QLabel,QPushButton,QHBoxLayout,QVBoxLayout
  4. from PyQt5.QtCore import Qt,QTimer,QObject,pyqtSignal,QBasicTimer
  5. from PyQt5.QtGui import QPainter, QColor, QFont,QPen
  6. import json
  7. class config:
  8. WIDTH=20#地图列数
  9. HEIGHT=20#地图行数
  10. blockLength=30#绘制画面时每一个节点方块的边长
  11. class point:#点类(每一个唯一坐标只有对应的一个实例)
  12. _list=[]#储存所有的point类实例
  13. _tag=True#标记最新创建的实例是否为_list中的已有的实例,True表示不是已有实例
  14. def __new__(cls,x,y):#重写new方法实现对于同样的坐标只有唯一的一个实例
  15. for i in point._list:
  16. if i.x==x and i.y==y:
  17. point._tag=False
  18. return i
  19. nt=super(point,cls).__new__(cls)
  20. point._list.append(nt)
  21. return nt
  22. def __init__(self,x,y):
  23. if point._tag:
  24. self.x=x
  25. self.y=y
  26. self.father=None
  27. self.F=0#当前点的评分 F=G+H
  28. self.G=0#起点到当前节点所花费的消耗
  29. self.cost=0#父节点到此节点的消耗
  30. else:
  31. point._tag=True
  32. @classmethod
  33. def clear(cls):#clear方法,每次搜索结束后,将所有点数据清除,以便进行下一次搜索的时候点数据不会冲突。
  34. point._list=[]
  35. def __eq__(self,T):#重写==运算以便实现point类的in运算
  36. if type(self)==type(T):
  37. return (self.x,self.y)==(T.x,T.y)
  38. else:
  39. return False
  40. def __str__(self):
  41. return'(%d,%d)[F=%d,G=%d,cost=%d][father:(%s)]'%(self.x,self.y,self.F,self.G,self.cost,str((self.father.x,self.father.y)) if self.father!=None else 'null')
  42. class A_Search:#核心部分,寻路类
  43. def __init__(self,arg_start,arg_end,arg_map):
  44. self.start=arg_start#储存此次搜索的开始点
  45. self.end=arg_end#储存此次搜索的目的点
  46. self.Map=arg_map#一个二维数组,为此次搜索的地图引用
  47. self.open=[]#开放列表:储存即将被搜索的节点
  48. self.close=[]#关闭列表:储存已经搜索过的节点
  49. self.result=[]#当计算完成后,将最终得到的路径写入到此属性中
  50. self.count=0#记录此次搜索所搜索过的节点数
  51. self.useTime=0#记录此次搜索花费的时间--在此演示中无意义,因为process方法变成了一个逐步处理的生成器,统计时间无意义。
  52. #开始进行初始数据处理
  53. self.open.append(arg_start)
  54. def cal_F(self,loc):
  55. print('计算值:',loc)
  56. G=loc.father.G+loc.cost
  57. H=self.getEstimate(loc)
  58. F=G+H
  59. print("F=%d G=%d H=%d"%(F,G,H))
  60. return {'G':G,'H':H,'F':F}
  61. def F_Min(self):#搜索open列表中F值最小的点并将其返回,同时判断open列表是否为空,为空则代表搜索失败
  62. if len(self.open)<=0:
  63. return None
  64. t=self.open[0]
  65. for i in self.open:
  66. if i.F<t.F:
  67. t=i
  68. return t
  69. def getAroundPoint(self,loc):#获取指定点周围所有可通行的点,并将其对应的移动消耗进行赋值。
  70. l=[(loc.x,loc.y+1,10),(loc.x+1,loc.y+1,14),(loc.x+1,loc.y,10),(loc.x+1,loc.y-1,14),(loc.x,loc.y-1,10),(loc.x-1,loc.y-1,14),(loc.x-1,loc.y,10),(loc.x-1,loc.y+1,14)]
  71. for i in l[::-1]:
  72. if i[0]<0 or i[0]>=config.HEIGHT or i[1]<0 or i[1]>=config.WIDTH:
  73. l.remove(i)
  74. nl=[]
  75. for i in l:
  76. if self.Map[i[0]][i[1]]==0:
  77. nt=point(i[0],i[1])
  78. nt.cost=i[2]
  79. nl.append(nt)
  80. return nl
  81. def addToOpen(self,l,father):#此次判断的点周围的可通行点加入到open列表中,如此点已经在open列表中则对其进行判断,如果此次路径得到的F值较之之前的F值更小,则将其父节点更新为此次判断的点,同时更新F、G值。
  82. for i in l:
  83. if i not in self.open:
  84. if i not in self.close:
  85. i.father=father
  86. self.open.append(i)
  87. r=self.cal_F(i)
  88. i.G=r['G']
  89. i.F=r['F']
  90. else:
  91. tf=i.father
  92. i.father=father
  93. r=self.cal_F(i)
  94. if i.F>r['F']:
  95. i.G=r['G']
  96. i.F=r['F']
  97. # i.father=father
  98. else:
  99. i.father=tf
  100. def getEstimate(self,loc):#H :从点loc移动到终点的预估花费
  101. return (abs(loc.x-self.end.x)+abs(loc.y-self.end.y))*10
  102. def DisplayPath(self):#在此演示中无意义
  103. print('搜索花费的时间:%.2fs.迭代次数%d,路径长度:%d'%(self.useTime,self.count,len(self.result)))
  104. if self.result!=None:
  105. for i in self.result:
  106. self.Map[i.x][i.y]=8
  107. for i in self.Map:
  108. for j in i:
  109. if j==0:
  110. print('%s'%'□',end='')
  111. elif j==1:
  112. print('%s'%'▽',end='')
  113. elif j==8:
  114. print('%s'%'★',end='')
  115. print('')
  116. else:
  117. print('搜索失败,无可通行路径')
  118. def process(self):#使用yield将process方法变成一个生成器,可以逐步的对搜索过程进行处理并返回关键数据
  119. while True:
  120. self.count+=1
  121. tar=self.F_Min()#先获取open列表中F值最低的点tar
  122. if tar==None:
  123. self.result=None
  124. self.count=-1
  125. break
  126. else:
  127. aroundP=self.getAroundPoint(tar)#获取tar周围的可用点列表aroundP
  128. self.addToOpen(aroundP,tar)#把aroundP加入到open列表中并更新F值以及设定父节点
  129. self.open.remove(tar)#将tar从open列表中移除
  130. self.close.append(tar)#已经迭代过的节点tar放入close列表中
  131. if self.end in self.open:#判断终点是否已经处于open列表中
  132. e=self.end
  133. self.result.append(e)
  134. while True:
  135. e=e.father
  136. if e==None:
  137. break
  138. self.result.append(e)
  139. yield (tar,self.open,self.close)
  140. break
  141. # self.repaint()
  142. # print('返回')
  143. yield (tar,self.open,self.close)
  144. time.sleep(5)#暂停
  145. self.useTime=time2-time1
  146. class GameBoard(QMainWindow):#可视化类,pyqt5进行编写。
  147. def __init__(self):
  148. print('初始化地图...')
  149. self.Map=[]
  150. for i in range(config.HEIGHT):
  151. col=[]
  152. for j in range(config.WIDTH):
  153. col.append(0)
  154. self.Map.append(col)
  155. self.startPoint=None
  156. self.endPoint=None
  157. self.search=None
  158. self.centerTimer=None
  159. self.yi=None
  160. self.special=None
  161. self.displayFlush=False
  162. super().__init__()
  163. print('初始化UI...')
  164. self.initUI()
  165. def initUI(self):
  166. #开始初始化UI部分
  167. #创建UI控件
  168. self.label_tips=QLabel("<p style='color:green'>使用说明:</p>右键单击格子选定起始点,左键格子选定格子为墙壁或删除墙壁。\n<p style='color:green'>颜色说明:</p>\n黄色代表起点,绿色代表终点,黑色代表墙壁,红色代表待搜索的open列表,灰色代表已搜索过的close列表,蓝色代表当前搜索到的路径",self)
  169. self.label_display=QLabel("",self)
  170. self.button_start=QPushButton("开始搜索",self)
  171. self.button_clearSE=QPushButton("重选起始点",self)
  172. self.button_clearWall=QPushButton("清空地图墙壁",self)
  173. self.button_saveMap=QPushButton("保存地图",self)
  174. self.button_loadMap=QPushButton("加载地图",self)
  175. #设置控件属性
  176. self.label_tips.setWordWrap(True)
  177. self.label_display.setWordWrap(True)
  178. #设置控件样式
  179. self.label_display.setStyleSheet("border:1px solid black")
  180. self.label_display.setAlignment(Qt.AlignLeft)
  181. self.label_display.setAlignment(Qt.AlignTop)
  182. #设置控件的尺寸和位置
  183. self.label_tips.resize(200,150)
  184. self.button_saveMap.resize(80,30)
  185. self.button_loadMap.resize(80,30)
  186. self.label_display.resize(200,300)
  187. self.label_tips.move(100+(config.WIDTH-1)*config.blockLength,0)
  188. self.label_display.move(100+(config.WIDTH-1)*config.blockLength,400)
  189. self.button_start.move(100+(config.WIDTH-1)*config.blockLength,200)
  190. self.button_clearSE.move(100+(config.WIDTH-1)*config.blockLength,250)
  191. self.button_clearWall.move(100+(config.WIDTH-1)*config.blockLength,300)
  192. self.button_saveMap.move(100+(config.WIDTH-1)*config.blockLength,350)
  193. self.button_loadMap.move(200+(config.WIDTH-1)*config.blockLength,350)
  194. #给控件绑定事件
  195. self.button_start.clicked.connect(self.button_StartEvent)
  196. self.button_clearSE.clicked.connect(self.button_Clear)
  197. self.button_clearWall.clicked.connect(self.button_Clear)
  198. self.button_saveMap.clicked.connect(self.button_SaveMap)
  199. self.button_loadMap.clicked.connect(self.button_LoadMap)
  200. #UI初始化完成
  201. self.setGeometry(0, 0, 150+(config.WIDTH*config.blockLength-config.blockLength)+200, 150+(config.HEIGHT*config.blockLength-config.blockLength))
  202. self.setMinimumSize(150+(config.WIDTH*config.blockLength-config.blockLength)+200, 150+(config.HEIGHT*config.blockLength-config.blockLength))
  203. self.setMaximumSize(150+(config.WIDTH*config.blockLength-config.blockLength)+200, 150+(config.HEIGHT*config.blockLength-config.blockLength))
  204. self.setWindowTitle('A*搜索')
  205. self.show()
  206. def addDisplayText(self,text):
  207. if self.displayFlush:
  208. self.label_display.setText(text+'\n')
  209. self.displayFlush=False
  210. else:
  211. self.label_display.setText(self.label_display.text()+text+'\n')
  212. def mousePressEvent(self,event):
  213. x,y=event.x()-50,event.y()-50
  214. x=x//config.blockLength
  215. y=y//config.blockLength
  216. if x>=0 and x<config.WIDTH and y>=0 and y<config.HEIGHT:
  217. if event.button()==Qt.LeftButton:
  218. if (x,y)!=self.startPoint and (x,y)!=self.endPoint:
  219. self.Map[y][x]=(1 if self.Map[y][x]==0 else 0)
  220. if event.button()==Qt.RightButton:
  221. if self.Map[y][x]==0:
  222. if self.startPoint==None:
  223. self.startPoint=(x,y)
  224. self.addDisplayText('添加了一个起点:(%d,%d)'%(x,y))
  225. elif self.endPoint==None and self.startPoint!=(x,y):
  226. self.endPoint=(x,y)
  227. self.addDisplayText('添加了一个终点:(%d,%d)'%(x,y))
  228. self.repaint()
  229. def button_StartEvent(self):
  230. sender=self.sender()
  231. print(sender)
  232. if self.startPoint!=None and self.endPoint!=None:
  233. if self.centerTimer==None:
  234. self.centerTimer=QBasicTimer()
  235. self.button_start.setEnabled(False)
  236. self.button_clearSE.setEnabled(False)
  237. self.button_clearWall.setEnabled(False)
  238. self.centerTimer.start(200,self)
  239. self.search=A_Search(point(self.startPoint[1],self.startPoint[0]),point(self.endPoint[1],self.endPoint[0]),self.Map)
  240. self.yi=self.search.process()
  241. self.addDisplayText('开始进行搜索')
  242. def button_SaveMap(self):
  243. with open('map.txt','w') as f:
  244. f.write(json.dumps(self.Map))
  245. self.addDisplayText('地图保存成功-->map.txt')
  246. # else:
  247. # self.addDisplayText('地图保存失败')
  248. def button_LoadMap(self):
  249. try:
  250. with open('map.txt','r') as f:
  251. self.Map=json.loads(f.read())
  252. config.HEIGHT=len(self.Map)
  253. config.WIDTH=len(self.Map[0])
  254. self.addDisplayText('地图加载成功')
  255. self.repaint()
  256. except Exception as e:
  257. print('失败',e,type(e))
  258. if type(e)==FileNotFoundError:
  259. self.addDisplayText('地图加载失败:地图文件不存在')
  260. elif type(e)==json.decoder.JSONDecodeError:
  261. self.addDisplayText('地图加载失败:错误的地图文件')
  262. def button_Clear(self):
  263. sender=self.sender()
  264. print(self.button_clearSE,type(self.button_clearSE))
  265. if sender==self.button_clearSE:
  266. self.startPoint=None
  267. self.endPoint=None
  268. self.repaint()
  269. self.addDisplayText('清空起始点')
  270. elif sender==self.button_clearWall:
  271. for i in range(len(self.Map)):
  272. for j in range(len(self.Map[i])):
  273. self.Map[i][j]=0
  274. self.repaint()
  275. self.addDisplayText('清空所有墙壁')
  276. def paintEvent(self, event):
  277. qp = QPainter()
  278. qp.begin(self)
  279. self.drawBoard(event,qp)
  280. qp.end()
  281. def drawBoard(self, event, qp):
  282. self.drawMap(qp)
  283. def drawMap(self,qp):#画面绘制方法,每次地图有所改动都将重绘
  284. time1=time.time()
  285. if self.search!=None:
  286. if self.special!=None:
  287. e=self.special[0]
  288. path=[e]
  289. while True:
  290. e=e.father
  291. if e!=None:
  292. path.append(e)
  293. else:
  294. break
  295. else:
  296. path=None
  297. pen=QPen(QColor(0,0,0),1,Qt.SolidLine)
  298. qp.setPen(pen)
  299. for i in range(len(self.Map)):
  300. for j in range(len(self.Map[i])):
  301. wordTag=False
  302. if i==self.search.start.x and j==self.search.start.y:
  303. qp.setBrush(QColor(255,255,0))
  304. elif i==self.search.end.x and j==self.search.end.y:
  305. qp.setBrush(QColor(100,200,50))
  306. else:
  307. if self.Map[i][j]==0:
  308. tagx=True
  309. if path:
  310. for k in path:
  311. if k.x==i and k.y==j:
  312. tagx=False
  313. qp.setBrush(QColor(0,100,255))
  314. if tagx:
  315. if self.special!=None:
  316. if i==self.special[0].x and j==self.special[0].y:
  317. qp.setBrush(QColor(0,255,0))
  318. else:
  319. tag=True
  320. for k in self.special[1]:
  321. if k.x==i and k.y==j:
  322. tag=False
  323. wordTag=True
  324. word=str(k.F)
  325. qp.setBrush(QColor(150,0,0))
  326. break
  327. else:
  328. qp.setBrush(QColor(220,220,220))
  329. if tag:
  330. for k in self.special[2]:
  331. if k.x==i and k.y==j:
  332. qp.setBrush(QColor(150,150,150))
  333. break
  334. else:
  335. qp.setBrush(QColor(220,220,220))
  336. else:
  337. qp.setBrush(QColor(220,220,220))
  338. elif self.Map[i][j]==1:
  339. qp.setBrush(QColor(0,0,0))
  340. else:
  341. qp.setBrush(QColor(255,0,0))
  342. qp.drawRect(50+j*config.blockLength,50+i*config.blockLength,config.blockLength,config.blockLength)
  343. if wordTag:
  344. qp.setFont(QFont('楷体',5,QFont.Light))
  345. qp.drawText(50+10+j*config.blockLength,50+10+i*config.blockLength,word)
  346. wordTag=False
  347. #time.sleep(20)
  348. else:
  349. for i in range(len(self.Map)):
  350. for j in range(len(self.Map[i])):
  351. if (j,i)==self.startPoint:
  352. qp.setBrush(QColor(255,255,0))
  353. elif (j,i)==self.endPoint:
  354. qp.setBrush(QColor(100,200,50))
  355. else:
  356. if self.Map[i][j]==0:
  357. qp.setBrush(QColor(220,220,220))
  358. elif self.Map[i][j]==1:
  359. qp.setBrush(QColor(0,0,0))
  360. else:
  361. qp.setBrush(QColor(255,0,0))
  362. qp.drawRect(50+j*config.blockLength,50+i*config.blockLength,config.blockLength,config.blockLength)
  363. time2=time.time()
  364. #time.sleep(20)
  365. # print('绘制时间:',time2-time1)
  366. def timerEvent(self,e):
  367. try:
  368. data=next(self.yi)
  369. except Exception as e:
  370. self.addDisplayText('搜索结束:')
  371. print('搜索结束!')
  372. if self.search.result==None:
  373. self.addDisplayText('未找到可行路径')
  374. print('搜索结束!')
  375. else:
  376. self.addDisplayText('总计搜索节点数:%d'%self.search.count)
  377. self.addDisplayText('最终路径长度:%d'%len(self.search.result))
  378. self.centerTimer.stop()
  379. self.search=None
  380. self.yi=None
  381. self.special=None
  382. point.clear()
  383. self.button_start.setEnabled(True)
  384. self.button_clearSE.setEnabled(True)
  385. self.button_clearWall.setEnabled(True)
  386. self.displayFlush=True
  387. else:
  388. self.special=data
  389. self.repaint()
  390. if __name__ == '__main__':
  391. app = QApplication(sys.argv)
  392. ex = GameBoard()
  393. sys.exit(app.exec_())

python

  1. import pygame
  2. import sys
  3. import math
  4. from tkinter import *
  5. from tkinter import ttk
  6. from tkinter import messagebox
  7. import os
  8. screen = pygame.display.set_mode((800,800))
  9. class spot:
  10. def __init__(self,x,y):
  11. #坐标
  12. self.i = x
  13. self.j = y
  14. #分数
  15. self.f = 0
  16. self.g = 0
  17. self.h = 0
  18. #父节点
  19. self.previous = None
  20. #是不是障碍
  21. self.obs = False
  22. #选择状态
  23. self.closed = False
  24. #权
  25. self.value = 1
  26. #相邻节点
  27. self.neighbors = []
  28. def show(self,color,st):
  29. if self.closed == False:
  30. pygame.draw.rect(screen,color,(self.i*w,self.j*h,w,h),st)
  31. pygame.display.update()
  32. def path(self,color,st):
  33. pygame.draw.rect(screen,color,(self.i*w,self.j*h,w,h),st)
  34. pygame.display.update()
  35. def addNeighbors(self):
  36. i = self.i
  37. j = self.j
  38. if i < rows-1 and grid[i+1][j].obs == False:
  39. self.neighbors.append(grid[i+1][j])
  40. if i > 0 and grid[i-1][j].obs == False:
  41. self.neighbors.append(grid[i-1][j])
  42. if j < cols-1 and grid[i][j+1].obs == False:
  43. self.neighbors.append(grid[i][j+1])
  44. if j > 0 and grid[i][j-1].obs == False:
  45. self.neighbors.append(grid[i][j-1])
  46. #行列数
  47. cols = 50
  48. rows = 50
  49. #颜色
  50. red = (255,0,0)
  51. green = (0,255,0)
  52. blue = (0,0,255)
  53. grey = (220,220,220)
  54. #格子长宽
  55. w = 800/cols
  56. h = 800/rows
  57. #父节点列表
  58. cameFrom = []
  59. #创建节点
  60. grid = [0 for i in range(cols)]
  61. for i in range(cols):
  62. grid[i] = [0 for i in range(rows)]
  63. for i in range(rows):
  64. for j in range(cols):
  65. grid[i][j] = spot(i,j)
  66. #默认起点、终点
  67. start = grid[5][5]
  68. end = grid[7][19]
  69. #画界面
  70. for i in range(rows):
  71. for j in range(cols):
  72. grid[i][j].show((255,255,255),1)
  73. #画围墙
  74. for i in range(rows):
  75. grid[i][0].show(grey,0)
  76. grid[i][cols-1].show(grey,0)
  77. grid[0][i].show(grey,0)
  78. grid[cols-1][i].show(grey,0)
  79. grid[i][0].show(grey,0)
  80. grid[i][cols-1].show(grey,0)
  81. grid[0][i].show(grey,0)
  82. grid[cols-1][i].show(grey,0)
  83. grid[i][0].obs = True
  84. grid[i][cols-1].obs = True
  85. grid[0][i].obs = True
  86. grid[cols-1][i].obs = True
  87. grid[i][0].obs = True
  88. grid[i][cols-1].obs = True
  89. grid[0][i].obs = True
  90. grid[cols-1][i].obs = True
  91. def onsubmit():
  92. global start
  93. global end
  94. st = startBox.get().split(',')
  95. ed = endBox.get().split(',')
  96. start = grid[int(st[0])][int(st[1])]
  97. end = grid[int(ed[0])][int(ed[1])]
  98. window.quit()
  99. window.destroy()
  100. #输入界面
  101. window = Tk()
  102. window.title('请输入')
  103. label_1 = Label(window,text = '起点坐标(x,y): ')
  104. startBox = Entry(window)
  105. label_2 = Label(window,text = '终点坐标(x,y): ')
  106. endBox = Entry(window)
  107. var = IntVar()
  108. showPath = ttk.Checkbutton(window,text = '显示每一步',onvalue=1,offvalue=0,variable=var)
  109. submit = Button(window,text='提交',command=onsubmit)
  110. #布局
  111. label_1.grid(row = 0,column = 0,pady = 3)
  112. label_2.grid(row = 1,column = 0,pady = 3)
  113. startBox.grid(row = 0,column = 1,pady = 3)
  114. endBox.grid(row = 1,column = 1,pady = 3)
  115. showPath.grid(columnspan = 2,row = 2)
  116. submit.grid(columnspan = 2,row = 3)
  117. #启动输入界面
  118. mainloop()
  119. #两个表
  120. openSet = [start]
  121. closeSet = []
  122. #显示起点终点
  123. start.show((255,8,127),0)
  124. end.show((255,8,127),0)
  125. #监听鼠标位置
  126. def mousePress(x):
  127. t = x[0]
  128. w = x[1]
  129. #判断在第几个格子
  130. g1 = t//(800//cols)
  131. g2 = w//(800//rows)
  132. #设置障碍
  133. set_obs = grid[g1][g2]
  134. if set_obs != start and set_obs!= end:
  135. set_obs.obs = True
  136. set_obs.show(grey,0)
  137. #画障碍
  138. loop = True
  139. while loop:
  140. ev = pygame.event.poll()
  141. if pygame.mouse.get_pressed()[0]:
  142. try:
  143. pos = pygame.mouse.get_pos()
  144. mousePress(pos)
  145. except AttributeError:
  146. pass
  147. if ev.type == pygame.QUIT:
  148. pygame.quit()
  149. elif ev.type == pygame.KEYDOWN:
  150. if ev.key == pygame.K_SPACE:
  151. loop = False
  152. #画好障碍后,初始邻接节点列表
  153. for i in range(rows):
  154. for j in range(cols):
  155. grid[i][j].addNeighbors()
  156. #启发式方法
  157. def heurisitic(n,e):
  158. d = math.sqrt((n.i - e.i)**2 + (n.j - e.j)**2)
  159. return d
  160. def main():
  161. #openSet初始化时已经包含起点
  162. #从中选择f分数最小的
  163. if(len(openSet) > 0):
  164. lowestIndex = 0
  165. for i in range(len(openSet)):
  166. if(openSet[i].f < openSet[lowestIndex].f):
  167. lowestIndex = i
  168. #对当前节点操作
  169. current = openSet[lowestIndex]
  170. #找到 打印路径
  171. if current == end:
  172. temp = current.f
  173. while current != start:
  174. current.closed = False
  175. current.show(blue, 0)
  176. current = current.previous
  177. end.show(red, 0)
  178. Tk().wm_withdraw()
  179. result = messagebox.askokcancel('Program Finished', ('The program finished, the shortest distance \n to the path is ' + str(temp) + ' blocks away, \n would you like to re run the program?'))
  180. if result == True:
  181. os.execl(sys.executable,sys.executable, *sys.argv)
  182. else:
  183. ag = True
  184. while ag:
  185. ev = pygame.event.get()
  186. for event in ev:
  187. if event.type == pygame.KEYDOWN:
  188. ag = False
  189. break
  190. pygame.quit()
  191. openSet.pop(lowestIndex)
  192. closeSet.append(current)
  193. neighbors = current.neighbors
  194. for i in range(len(neighbors)):
  195. neighbor = neighbors[i]
  196. if neighbor not in closeSet:
  197. tmpG = current.g + current.value
  198. if neighbor in openSet:
  199. if neighbor.g > tmpG:
  200. neighbor.g = tmpG
  201. neighbor.previous = current
  202. else:
  203. neighbor.g = tmpG
  204. openSet.append(neighbor)
  205. neighbor.previous = current
  206. neighbor.h = heurisitic(neighbor,end)
  207. neighbor.f = neighbor.g + neighbor.h
  208. if var.get():
  209. for i in range(len(openSet)):
  210. openSet[i].show(green,0)
  211. for i in range(len(closeSet)):
  212. if closeSet[i] != start:
  213. closeSet[i].show(red,0)
  214. current.closed = True
  215. while True:
  216. ev = pygame.event.poll()
  217. if ev.type == pygame.QUIT:
  218. pygame.quit()
  219. main()

鼠标左键长按添加障碍,空格开始寻路

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/526213
推荐阅读
相关标签
  

闽ICP备14008679号