当前位置: 首页 > news >正文

人工智能导论第一次A*算法实现走迷宫

搭建环境:

首先下载Anaconda,这一步是大前提!!!!!!

https://www.bilibili.com/video/BV1tNK3z3EeE/?vd_source=4f1a7ab31a6e7a590a8a8a5088e74b3c

之后可以再base里面,也可以搭建新的环境

这里我conda create -n keshe python=3.8搭建了一个叫做keshe的环境

之后我们再VScode里面使用这个环境,如图右下角是环境选取,右上角三角是开始运行

会看到有下面的报错,

这个时候我们在环境中增加对应的包

pip install PyQt5,再次运行即可成功。。

如果没有Vscode的话,你可以桌面建立一个文本,然后把代码复制进去

import time, sys
from PyQt5.QtWidgets import QDialogButtonBox, QDialog, QMainWindow, QGridLayout, QTextEdit, QLineEdit, QWidget, \QMessageBox, QApplication, QLabel, QPushButton, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import Qt, QTimer, QObject, pyqtSignal, QBasicTimer
from PyQt5.QtGui import QPainter, QColor, QFont, QPen
import jsonclass config:WIDTH = 67  # 地图列数 - 根据迷宫数据确定HEIGHT = 41  # 地图行数 - 根据迷宫数据确定blockLength = 12  # 绘制画面时每一个节点方块的边长 - 改小一点以适应更大的迷宫class point:  # 点类(每一个唯一坐标只有对应的一个实例)_list = []  # 储存所有的point类实例_tag = True  # 标记最新创建的实例是否为_list中的已有的实例,True表示不是已有实例def __new__(cls, x, y):  # 重写new方法实现对于同样的坐标只有唯一的一个实例for i in point._list:if i.x == x and i.y == y:point._tag = Falsereturn int = super(point, cls).__new__(cls)point._list.append(nt)return ntdef __init__(self, x, y):if point._tag:self.x = xself.y = yself.father = Noneself.F = 0  # 当前点的评分  F=G+Hself.G = 0  # 起点到当前节点所花费的消耗self.cost = 0  # 父节点到此节点的消耗else:point._tag = True@classmethoddef clear(cls):  # clear方法,每次搜索结束后,将所有点数据清除,以便进行下一次搜索的时候点数据不会冲突。point._list = []def __eq__(self, T):  # 重写==运算以便实现point类的in运算if type(self) == type(T):return (self.x, self.y) == (T.x, T.y)else:return Falsedef __str__(self):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')class A_Search:  # 核心部分,寻路类def __init__(self, arg_start, arg_end, arg_map):self.start = arg_start  # 储存此次搜索的开始点self.end = arg_end  # 储存此次搜索的目的点self.Map = arg_map  # 一个二维数组,为此次搜索的地图引用self.open = []  # 开放列表:储存即将被搜索的节点self.close = []  # 关闭列表:储存已经搜索过的节点self.result = []  # 当计算完成后,将最终得到的路径写入到此属性中self.count = 0  # 记录此次搜索所搜索过的节点数self.useTime = 0  # 记录此次搜索花费的时间--在此演示中无意义,因为process方法变成了一个逐步处理的生成器,统计时间无意义。# 开始进行初始数据处理self.open.append(arg_start)def cal_F(self, loc):print('计算值:', loc)G = loc.father.G + loc.costH = self.getEstimate(loc)F = G + Hprint("F=%d G=%d H=%d" % (F, G, H))return {'G': G, 'H': H, 'F': F}def F_Min(self):  # 搜索open列表中F值最小的点并将其返回,同时判断open列表是否为空,为空则代表搜索失败if len(self.open) <= 0:return Nonet = self.open[0]for i in self.open:if i.F < t.F:t = ireturn tdef getAroundPoint(self, loc):  # 获取指定点周围所有可通行的点,并将其对应的移动消耗进行赋值。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)]for i in l[::-1]:if i[0] < 0 or i[0] >= config.HEIGHT or i[1] < 0 or i[1] >= config.WIDTH:l.remove(i)nl = []for i in l:if self.Map[i[0]][i[1]] == 0:nt = point(i[0], i[1])nt.cost = i[2]nl.append(nt)return nldef addToOpen(self, l,father):  # 此次判断的点周围的可通行点加入到open列表中,如此点已经在open列表中则对其进行判断,如果此次路径得到的F值较之之前的F值更小,则将其父节点更新为此次判断的点,同时更新F、G值。for i in l:if i not in self.open:if i not in self.close:i.father = fatherself.open.append(i)r = self.cal_F(i)i.G = r['G']i.F = r['F']else:tf = i.fatheri.father = fatherr = self.cal_F(i)if i.F > r['F']:i.G = r['G']i.F = r['F']# i.father=fatherelse:i.father = tfdef getEstimate(self, loc):  # H :从点loc移动到终点的预估花费return (abs(loc.x - self.end.x) + abs(loc.y - self.end.y)) * 10def DisplayPath(self):  # 在此演示中无意义print('搜索花费的时间:%.2fs.迭代次数%d,路径长度:%d' % (self.useTime, self.count, len(self.result)))if self.result != None:for i in self.result:self.Map[i.x][i.y] = 8for i in self.Map:for j in i:if j == 0:print('%s' % '□', end='')elif j == 1:print('%s' % '▽', end='')elif j == 8:print('%s' % '★', end='')print('')else:print('搜索失败,无可通行路径')def process(self):  # 使用yield将process方法变成一个生成器,可以逐步的对搜索过程进行处理并返回关键数据while True:self.count += 1tar = self.F_Min()  # 先获取open列表中F值最低的点tarif tar == None:self.result = Noneself.count = -1breakelse:aroundP = self.getAroundPoint(tar)  # 获取tar周围的可用点列表aroundPself.addToOpen(aroundP, tar)  # 把aroundP加入到open列表中并更新F值以及设定父节点self.open.remove(tar)  # 将tar从open列表中移除self.close.append(tar)  # 已经迭代过的节点tar放入close列表中if self.end in self.open:  # 判断终点是否已经处于open列表中e = self.endself.result.append(e)while True:e = e.fatherif e == None:breakself.result.append(e)yield (tar, self.open, self.close)break# self.repaint()# print('返回')yield (tar, self.open, self.close)time.sleep(0.05)  # 暂停时间改短一点,加快动画速度# self.useTime = time2 - time1class GameBoard(QMainWindow):  # 可视化类,pyqt5进行编写。def __init__(self):print('初始化地图...')self.Map = []self.startPoint = Noneself.endPoint = Noneself.createMazeFromText()  # 从文本数据创建迷宫self.search = Noneself.centerTimer = Noneself.yi = Noneself.special = Noneself.displayFlush = Falsesuper().__init__()print('初始化UI...')self.initUI()def createMazeFromText(self):"""从文本数据创建迷宫地图"""maze_text = """#################################################################################
#.#...#....$....#...................#...#.........#.......#.............#.......#
#.#.#.#.###.###.#########.#########.#.#####.#####.#####.#.#.#######.###.#.#####.#
#...#.....#...#.#.........#.#.....#.#...#...#...#.......#.#.#.......#.#.#.#...#.#
#############.#.#.#########.#.###.#.###.#.###.#.#.#######.###.#######.#.#.#.#.#.#
#...........#.#...#.#.....#...#...#...#.#.#.#.#...#...#.......#.......#.#.#.#.#.#
#.#########.#.#####.#.#.#.#.###.#####.#.#.#.#.#####.#.#########.###.###.###.#.#.#
#.#.........#...#...#.#.#.#...#.....#.#.#.#...#.#...#.......#.....#.#...#...#...#
#.#########.#.#.###.#.#.#####.###.#.#.#.#.#.###.#.#########.#####.#.#.###.#####.#
#.#.......#.#.#...#...#.#.....#.#.#.#...#.#.....#.#.....#.#...#...#.......#...#.#
#.#.#####.#.#.###.#####.#.#####.#.#.###.#.#######.###.#.#.###.#.###########.#.#.#
#...#...#.#.#...#.....#.#.......#.#.#...#.....#...#...#.....#.#.#...#...#...#...#
#####.#.#.#.#########.#.#######.#.###.#######.#.###.#########.###.#.#.#.#.#######
#.....#...#.#.........#.......#.#...#.#.#.....#.#.....#.......#...#.#.#.#.#.....#
#.#########.#.#########.###.###.###.#.#.#.###.#.#.###.#.#######.###.#.###.#.###.#
#...#.#.....#...#.....#.#.#...#.#.#.....#...#.#.#...#.#...#...#...#.#.#...#...#.#
###.#.#.#####.#.#.#.###.#.###.#.#.#####.###.###.#####.###.#.#.#.###.#.#.#####.#.#
#...#...#.....#.#.#.#...#...#.....#...#.#...#...........#.#.#...#...#.......#.#.#
#.###.#########.#.#.#.###.#.#####.#.#.###.###.###########.#.#####.#########.###.#
#.#.............#.#.......#.#...#.#.#...#.#...#.#.......#.......#.#...#.....#...#
#.#.#############.#########.#.#.###.###.#.#.###.#.#####.#.#######.#.#.#.#####.#.#
#.#.#...........#.#.#.#.....#.#.....#...#.#.....#...#.#.#.#.#...#.#.#.#.#.....#.#
#.###.#########.#.#.#.#######.#######.###.#####.###.#.#.#.#.###.#.#.#.#.#####.#.#
#.....#...#.....#...#.........#.....#...#.....#...#...#.#.....#.#...#.#.#.....#.#
#.#####.#.#.#######.###########.#######.#.#######.###.#.###.###.#####.#.#.#####.#
#.....#.#.#...#...#.#.......#.........#.#...#.......#.#.#...#...#.....#.#.#...#.#
#######.#.###.#.###.#.#####.#.#####.###.#.#.#.#######.#.#####.###.#####.#.###.#.#
#.......#.#...#.....#.#...#.#...#.#.....#.#.#.#.#.....#...#...#...#.....#...#.#.#
#.#######.#.#.#####.#.###.#.###.#.#######.#.#.#.#.#######.#.###.#.###.#####.#.#.#
#.#.#.....#.#.#...#.#...#.#...#...#.#...#.#...#.#.....#.#...#...#...#.......#...#
#.#.#.#####.#.#.#.#####.#.###.###.#.#.#.#.#####.#####.#.#####.#####.#########.###
#.#...#.....#.#.#.#...#...#.#.#...#...#.#.#...#.....#...#.#...#...#.....#...#.#.#
#.###.###.#.###.#.#.#.###.#.#.#.#######.#.#.#.#####.###.#.#.###.#.#####.###.#.#.#
#...#...#.#.#...#.#.#...#.#.#.#.#.......#...#.........#.#...#...#.#...#...#.#...#
#.#.###.#.#.#.###.#.###.#.#.#.#.###.###.###########.###.#.###.###.###.###.#.###.#
#.#...#.#.#.#...#...#...#.#.#.#.....#...#...#.....#.#...#.....#.....#.#...#...#.#
#.###.#.#.#####.#####.#.#.#.#.#######.###.#.#####.#.#.#############.#.#.###.#.#.#
#...#.#...#...#.....#.#.#.#.#.#...#...#.#.#.......#.#.#...#...#...#...#.#.#.#...#
###.#.#####.#.#####.#.###.#.#.#.#.#.###.#.#########.#.#.#.#.#.#.#.#####.#.#.#####
#...#.......#.......#.......#...#.......@...........#...#...#...#.......#.......#
#################################################################################"""lines = maze_text.strip().split('\n')if not lines:print("迷宫数据为空")return# 设置地图尺寸config.HEIGHT = len(lines)config.WIDTH = len(lines[0])# 初始化地图self.Map = []for i in range(config.HEIGHT):row = []for j in range(config.WIDTH):char = lines[i][j]if char == '#':  # 墙壁row.append(1)elif char == '$':  # 起点row.append(0)self.startPoint = (j, i)print(f"找到起点: ({j}, {i})")elif char == '@':  # 终点row.append(0)self.endPoint = (j, i)print(f"找到终点: ({j}, {i})")else:  # 可通行的路径row.append(0)self.Map.append(row)print(f"迷宫创建成功: {config.WIDTH}x{config.HEIGHT}")print(f"起点: {self.startPoint}, 终点: {self.endPoint}")def initUI(self):# 开始初始化UI部分# 创建UI控件self.label_tips = QLabel("<p style='color:green'>迷宫求解器</p>基于A*算法的迷宫求解可视化程序\n<p style='color:green'>颜色说明:</p>\n黄色代表起点,绿色代表终点,黑色代表墙壁,红色代表待搜索的open列表,灰色代表已搜索过的close列表,蓝色代表当前搜索到的路径",self)self.label_display = QLabel("", self)self.button_start = QPushButton("开始搜索", self)self.button_reset = QPushButton("重置迷宫", self)# 设置控件属性self.label_tips.setWordWrap(True)self.label_display.setWordWrap(True)# 设置控件样式self.label_display.setStyleSheet("border:1px solid black")self.label_display.setAlignment(Qt.AlignLeft)self.label_display.setAlignment(Qt.AlignTop)# 设置控件的尺寸和位置self.label_tips.resize(200, 150)self.button_start.resize(80, 30)self.button_reset.resize(80, 30)  # 修复这里的笔误self.label_tips.move(50 + (config.WIDTH) * config.blockLength, 20)self.label_display.move(50 + (config.WIDTH) * config.blockLength, 200)self.button_start.move(50 + (config.WIDTH) * config.blockLength, 170)self.button_reset.move(150 + (config.WIDTH) * config.blockLength, 170)# 给控件绑定事件self.button_start.clicked.connect(self.button_StartEvent)self.button_reset.clicked.connect(self.button_Reset)# UI初始化完成window_width = 100 + (config.WIDTH * config.blockLength) + 250window_height = 100 + (config.HEIGHT * config.blockLength)self.setGeometry(100, 100, window_width, window_height)self.setMinimumSize(window_width, window_height)self.setMaximumSize(window_width, window_height)self.setWindowTitle('迷宫求解器 - A*算法')self.show()def addDisplayText(self, text):if self.displayFlush:self.label_display.setText(text + '\n')self.displayFlush = Falseelse:self.label_display.setText(self.label_display.text() + text + '\n')def button_StartEvent(self):if self.startPoint != None and self.endPoint != None:if self.centerTimer == None:self.centerTimer = QBasicTimer()self.button_start.setEnabled(False)self.centerTimer.start(30, self)  # 加快动画速度self.search = A_Search(point(self.startPoint[1], self.startPoint[0]),point(self.endPoint[1], self.endPoint[0]), self.Map)self.yi = self.search.process()self.addDisplayText('开始进行搜索')def button_Reset(self):"""重置迷宫"""if self.centerTimer and self.centerTimer.isActive():self.centerTimer.stop()self.search = Noneself.yi = Noneself.special = Nonepoint.clear()self.button_start.setEnabled(True)self.displayFlush = Trueself.addDisplayText('迷宫已重置')self.repaint()def paintEvent(self, event):qp = QPainter()qp.begin(self)self.drawBoard(event, qp)qp.end()def drawBoard(self, event, qp):self.drawMap(qp)def drawMap(self, qp):  # 画面绘制方法,每次地图有所改动都将重绘if self.search != None:if self.special != None:e = self.special[0]path = [e]while True:e = e.fatherif e != None:path.append(e)else:breakelse:path = Nonepen = QPen(QColor(0, 0, 0), 1, Qt.SolidLine)qp.setPen(pen)for i in range(len(self.Map)):for j in range(len(self.Map[i])):wordTag = Falseif i == self.search.start.x and j == self.search.start.y:qp.setBrush(QColor(255, 255, 0))  # 起点 - 黄色elif i == self.search.end.x and j == self.search.end.y:qp.setBrush(QColor(100, 200, 50))  # 终点 - 绿色else:if self.Map[i][j] == 0:  # 可通行路径tagx = Trueif path:for k in path:if k.x == i and k.y == j:tagx = Falseqp.setBrush(QColor(0, 100, 255))  # 路径 - 蓝色if tagx:if self.special != None:if i == self.special[0].x and j == self.special[0].y:qp.setBrush(QColor(0, 255, 0))  # 当前搜索点 - 绿色else:tag = Truefor k in self.special[1]:  # open列表if k.x == i and k.y == j:tag = FalsewordTag = Trueword = str(k.F)qp.setBrush(QColor(150, 0, 0))  # open列表 - 红色breakif tag:for k in self.special[2]:  # close列表if k.x == i and k.y == j:qp.setBrush(QColor(150, 150, 150))  # close列表 - 灰色breakelse:qp.setBrush(QColor(220, 220, 220))  # 空白路径 - 浅灰色else:qp.setBrush(QColor(220, 220, 220))  # 空白路径 - 浅灰色elif self.Map[i][j] == 1:  # 墙壁qp.setBrush(QColor(0, 0, 0))  # 墙壁 - 黑色else:qp.setBrush(QColor(255, 0, 0))qp.drawRect(50 + j * config.blockLength, 50 + i * config.blockLength, config.blockLength,config.blockLength)else:# 没有搜索时的绘制for i in range(len(self.Map)):for j in range(len(self.Map[i])):if (j, i) == self.startPoint:qp.setBrush(QColor(255, 255, 0))  # 起点 - 黄色elif (j, i) == self.endPoint:qp.setBrush(QColor(100, 200, 50))  # 终点 - 绿色else:if self.Map[i][j] == 0:qp.setBrush(QColor(220, 220, 220))  # 可通行路径 - 浅灰色elif self.Map[i][j] == 1:qp.setBrush(QColor(0, 0, 0))  # 墙壁 - 黑色else:qp.setBrush(QColor(255, 0, 0))qp.drawRect(50 + j * config.blockLength, 50 + i * config.blockLength, config.blockLength,config.blockLength)def timerEvent(self, e):try:data = next(self.yi)except Exception as e:self.addDisplayText('搜索结束:')print('搜索结束!')if self.search.result == None:self.addDisplayText('未找到可行路径')print('搜索结束!')else:self.addDisplayText('总计搜索节点数:%d' % self.search.count)self.addDisplayText('最终路径长度:%d' % len(self.search.result))self.centerTimer.stop()self.button_start.setEnabled(True)self.displayFlush = Trueelse:self.special = dataself.repaint()if __name__ == '__main__':app = QApplication(sys.argv)ex = GameBoard()sys.exit(app.exec_())

首先复制你的.py的文件路径

然后终端运行一下,如果你是base没有新建的话,那就直接在这个里面

python +文件地址,注意把双引号“”去掉

由此就成功了。

下面讲解一下代码部分:

1. 配置类 (config)

class config:WIDTH = 67  # 地图列数HEIGHT = 41  # 地图行数  blockLength = 12  # 每个方块的绘制边长

2. 点类 (point)

def __new__(cls, x, y):  # 重写new方法for i in point._list:if i.x == x and i.y == y:point._tag = Falsereturn i  # 返回已存在的实例nt = super(point, cls).__new__(cls)point._list.append(nt)return nt  # 创建新实例
  • 为了确保每个坐标只有唯一实例,避免重复创建相同坐标的点

3. A*搜索算法类 (A_Search)

初始化列表

def __init__(self, arg_start, arg_end, arg_map):self.open = []    # 待探索节点self.close = []   # 已探索节点  self.result = []  # 最终路径

核心算法流程

1. 代价计算

loc.cost就是邻居节点间的代价

def cal_F(self, loc):G = loc.father.G + loc.cost  # 实际代价H = self.getEstimate(loc)    # 预估代价F = G + H                   # 总代价return {'G': G, 'H': H, 'F': F}
2. 启发函数

用曼哈顿距离作为启发函数,哈佛曼就是两点间的距离D=|x1-x2|+|y1-y2|

并且由于我们的实际代价是一个格子是10,我们这里给我们的哈佛曼预测也乘以10,这样可以和移动代价保持一致

def getEstimate(self, loc):return (abs(loc.x - self.end.x) + abs(loc.y - self.end.y)) * 10
3. 邻居节点获取

def getAroundPoint(self, loc):l = [(loc.x, loc.y + 1, 10), (loc.x + 1, loc.y + 1, 14), ...]
4. 生成器实现逐步搜索
def process(self):while True:tar = self.F_Min()  # 获取F值最小的节点aroundP = self.getAroundPoint(tar)self.addToOpen(aroundP, tar)# ... 搜索逻辑yield (tar, self.open, self.close)  # 返回当前状态

4. 游戏界面类 (GameBoard)

迷宫数据解析

def createMazeFromText(self):

UI初始化

def initUI(self):

绘制系统

def drawMap(self, qp):

动画控制

def timerEvent(self, e):

剩下如果再加功能的话的功能就很简单了,你自己问ai加就行了

http://www.dtcms.com/a/471946.html

相关文章:

  • Bootstrap5 下拉菜单
  • 大模型-深入理解编码器与解码器
  • 科技类网站模板服装定制公司
  • 网站标题收录wordpress建站 域名
  • 久久建筑网站内搜索医疗设计网站
  • 网站制作费用多少平台推广话术
  • 开发网站的技术路线百度sem
  • 网站建设一般步骤天猫商城售后服务
  • 网站建设体质喝什么茶网络加速器app
  • 建设银行的网站是什么情况长沙域名注册
  • (14)100天python从入门到拿捏《Python的错误与异常机制》
  • 昆明网站建设方案托管怎么在网站首页做飘窗
  • 扁平化网站模板下载制作网页的流程步骤
  • 开发一个商城网站多少钱软件工程研究生学校排名
  • 自己做网站2008R2好还是win7如果做二手车网站
  • [嵌入式系统-98]:国内嵌入式AI算力板
  • iPaaS供应商推荐列表:国内外主流iPaaS平台有哪些
  • 湛江网站seo哪些网络公司可以做机票预订网站
  • 鄂城网站建设wordpress 关闭缩略图
  • 河南开元建设有限公司网站百度知道网页版
  • 多板块同时清洗治具:最大化提升清洗产能的工装方案
  • 周口建设企业网站公司wordpress 内嵌网页
  • 怎么做算命网站wordpress取消301跳转
  • 员工管理网站模板英文网站建设服务合同模板
  • 浙江省建设工程质监站网站php怎样做网站的注删页面
  • 泉州效率网络网站建设关于网站建设的奖项名称
  • 长沙市天心建设局网站网站国外推广
  • 德州建设网站有网站制作毕业设计论文
  • 网站开发网站制作报价织梦的网站收录不好
  • 学校网站建设是什么做网站完整过程