实验三 代码+走迷宫(BFS生成迷宫+A*寻路)

该文详细介绍了如何利用BFS算法生成迷宫地图,并通过A*算法寻找地图上的最短路径。实验内容包括Block类、Map类和DrawMap类的设计,以及tkinter界面的交互按钮,实现了地图的可视化和动态刷新。最终,通过A*算法成功找到了从起点到终点的最短路径,并在界面上进行了展示。
摘要由CSDN通过智能技术生成

一、实验目的

1.BFS生成迷宫
2.A*算法寻找最短路径
3.可视化界面和人机交互按钮

二、实验内容

1.设计Block类用于生成障碍物
2.设计Map类用于生成地图和刷新地图
3.设计DrawMap类用于生成最短路径的路线
4.使用tkinter开发组件设计全局布局界面

三、实验过程

3.1 BLOCK类功能函数

创建队列,通过对周围的点的遍历,利用队列先进先出的特点,将需要扩展出去的点加入队列,进行扩展,然后移出队列。直到队列为空就可以完全遍历。
这里我们为了更好的操作,首先先创建Block类用于填充功能函数:
初始化操作,主要是初始化Wall的四个方向为空,继承self中墙体的位置,代码如下:
在这里插入图片描述

Get_next_block_pos()函数获取周围的目标,通过输入的点和方向,返回首位四个点的坐标情况,方便BFS遍历的时候不需要使用长篇的for循环和if判断,代码如下:
在这里插入图片描述

Get_next_block()函数时通过对四个方向的判断,遍历每个方向,如果没有超出边界,并且没有墙体,就返回空。如果碰到墙体,则将walls设置为false,并且将此时的方向和坐标信息反馈给Block类,记录墙体位置。代码如下:
在这里插入图片描述

3.2 BFS 生成迷宫

这里设置block_stack列表存放路径,这里block_stack将起始点存放入内,接下来的待扩展的节点也存放入内。
设置空集solution存放到达的节点,只要block_stack不为空,就从block_stack中取出一个元素block进行扩展。对这个block元素所有的邻居next_block,只要next_block不在solution中,说明没有到达过,就把next_block放入到block_stack里面,然后放到solution中标记为已经到达。
直道solution中放入目的地节点,循环结束。代码如下:
在这里插入图片描述

3.3 A*算法寻路

代码原理:A*吸取了Dijkstra 算法中的cost_so_far,为每个边长设置权值,不停的计算每个顶点到起始顶点的距离(G),以获得最短路线,
同时也汲取贪婪最佳优先搜索算法中不断向目标前进优势,并持续计算每个顶点到目标顶点的距离(Heuristic distance),以引导搜索队列不断想目标逼近,从而搜索更少的顶点,保持寻路的高效。
这里我们设置一个open表存放路径信息,设置openList表存放估值,将估值最小的点存放区index=0中,通过对open表和close表的检测,判断是否需要添加入openList中,这里还需要检测是否到达边界。
通过不断更新估值表中的值,将最优路径加入open中直到目的节点存入表中,循环解释,代码如下:

在这里插入图片描述

3.4 地图组件

地图模块组件,开始按钮 ,主要是反馈初始条件,调用寻路函数,答应solution中的路径信息,实现一键寻路。

刷新地图的操作这里调用tkinter组件delete将mmap中路径信息删除,然后调用draw_map重新生成新地图。我编写draw_end函数,将reset_map功能集成到tkinter界面的按钮中,用户可以点击change按键就可以i实现地图的刷新。
在这里插入图片描述

绘图模块,主要是画墙和画路线,墙体的数据全部都存放在wall数组中,我们通过draw_cell函数遍历数组,调用tkinter中的creat_line函数,画线。
路径数据我存放在solution中,和画墙一样的操作,代码如下:
在这里插入图片描述

四、成果展示

4.1 地图展示

通过tkinter组件我们成功的生成了迷宫,如下如所示:

4.2 寻路路线展示

通过A*算法我们生成了一条最短路径,并通过绘图组件展示,如下图:

4.3 刷新地图展示

点击change刷新地图,获得新的地图和新的路径,如下图所示:
在这里插入图片描述

代码展示

# -*- coding: utf-8 -*-
# 单文件版本
from __future__ import unicode_literals
import random

try:
    from tkinter import *
except ImportError:
    from tkinter import *
import matplotlib.image as mpimg  # 用于显示图片
import numpy


class Block(object):
    def __init__(self, mmap, x, y, direction=None):
        super(Block, self).__init__()
        # 初始定义四个方向都没有堵塞
        self.walls = [True, True, True, True]  # top,right,bottom,left
        self.mmap = mmap
        if mmap:
            mmap.mmap[x][y] = self
        self.x, self.y = x, y
        # 根据算法设置堵塞的墙
        if direction is not None:
            direction = (direction + 2) % 4
            self.walls[direction] = False

    def __unicode__(self):
        return "%s" % [1 if e else 0 for e in self.walls]

    def __str__(self):
        return unicode(self).encode('utf-8')

    # 根据已有点的位置坐标,获取周围四个方向的坐标
    def get_next_block_pos(self, direction):
        x = self.x
        y = self.y
        if direction == 0:  # Top
            y -= 1
        elif direction == 1:  # Right
            x += 1
        if direction == 2:  # Bottom
            y += 1
        if direction == 3:  # Left
            x -= 1
        return x, y

    def get_next_block(self):
        directions = list(range(4))  # 四个方向
        random.shuffle(directions)  # 随机找一个方向
        for direction in directions:  # 每个方向遍历查找
            x, y = self.get_next_block_pos(direction)  # 下一个方向的坐标
            if x >= self.mmap.max_x or x < 0 or y >= self.mmap.max_y or y < 0:
                continue  # 可以往下走
            if self.mmap.mmap[x][y]:  # if walked
                continue  # 走了
            self.walls[direction] = False  # 此路不通
            return Block(self.mmap, x, y, direction)  # 记住堵塞的位置
        return None


class Map(object):
    def __init__(self):
        super(Map, self).__init__()

    # 重画地图
    def reset_map(self):
        self.gen_map(self.max_x, self.max_y)

    def reset_map_changesize(self, chang, kuan):
        self.max_x = chang
        self.max_y = kuan
        self.gen_map(self.max_x, self.max_y)

    # 生成地图
    def gen_map(self, max_x=10, max_y=10):
        self.max_x, self.max_y = max_x, max_y
        self.mmap = [[None for j in range(self.max_y)] for i in range(self.max_x)]
        self.solution = []  # 定义路线的列表
        block_stack = [Block(self, 0, 0)]  # a unused block
        while block_stack:
            block = block_stack.pop()
            next_block = block.get_next_block()
            if next_block:
                block_stack.append(block)
                block_stack.append(next_block)
                # 如果到了终点,开始循环加载路线图
                if next_block.x == self.max_x - 1 and next_block.y == self.max_y - 1:  # is end
                    for o in block_stack:
                        self.solution.append((o.x, o.y))  # 将正确的路线记录下来

    # python2用的
    def __unicode__(self):
        out = ""
        for y in range(self.max_y):
            for x in range(self.max_x):
                out += "%s" % self.mmap[x][y]
            out += "\n"
        return out

    # python3用的
    def __str__(self):
        return unicode(self).encode('utf-8')


class DrawMap(object):
    def __init__(self, mmap, cell_width=10):
        super(DrawMap, self).__init__()
        self.mmap = mmap
        self.cell_width = cell_width

    # 地图尺寸
    def get_map_size(self):
        # width, height
        return (self.mmap.max_x + 2) * self.cell_width, (self.mmap.max_y + 2) * self.cell_width

    # 画地图的线
    def create_line(self, x1, y1, x2, y2, **kwarg):
        raise NotImplemented()

    # 行进路线
    def create_solution_line(self, x1, y1, x2, y2):
        self.create_line(x1, y1, x2, y2)

    def draw_start(self):
        raise NotImplemented()

    def draw_end(self):
        raise NotImplemented()

    def get_cell_center(self, x, y):
        w = self.cell_width
        return (x + 1) * w + w // 2, (y + 1) * w + w // 2

    def draw_solution(self):
        pre = (0, 0)  # 起点位置
        for o in self.mmap.solution:
            p1 = self.get_cell_center(*pre)  # 起始点位置
            p2 = self.get_cell_center(*o)  # 下一个点的位置
            self.create_solution_line(p1[0], p1[1], p2[0], p2[1])
            pre = o  # 起始点为上一个点

    def draw_cell(self, block):
        width = self.cell_width
        x = block.x + 1
        y = block.y + 1
        walls = block.walls
        if walls[0]:
            self.create_line(x * width, y * width, (x + 1) * width + 1, y * width)
        if walls[1]:
            self.create_line((x + 1) * width, y * width, (x + 1) * width, (y + 1) * width + 1)
        if walls[2]:
            self.create_line(x * width, (y + 1) * width, (x + 1) * width + 1, (y + 1) * width)
        if walls[3]:
            self.create_line(x * width, y * width, x * width, (y + 1) * width + 1)

    # 画图
    def draw_map(self):
        for y in range(self.mmap.max_y):
            for x in range(self.mmap.max_x):
                self.draw_cell(self.mmap.mmap[x][y])
        self.draw_start()
        self.draw_end()


class TKDrawMap(DrawMap):
    def __init__(self, mmap):
        super(TKDrawMap, self).__init__(mmap, cell_width=10)
        master = Tk()  # 调用tkinter
        master.title('汤海彤的迷宫')
        master.geometry('500x300')  # 这里的乘号不是 * ,而是小写英文字母 x
        btn_start = Button(master, text='start', command=self.draw_solution)
        btn_start.place(x=10, y=10)
        btn_change = Button(master, text='change', command=self.reset_map)
        btn_change.place(x=10, y=60)



        width, height = self.get_map_size()  # 获取尺寸大小
        self.w = Canvas(master, width=width, height=width)  # 调用Canvas
        self.w.pack()  # 显示
        self.draw_map()
        mainloop()  # 循环

    def ok(self):
        change_size = self.var.get()
        change_size2 = self.var2.get()
        change_size = int(change_size)
        change_size2 = int(change_size2)

        self.mmap.reset_map_changesize(change_size, change_size2)
        self.w.delete('all')
        self.draw_map()

    # 按开始处红点按钮,开始画路线图
    def draw_start(self):
        r = self.cell_width // 3
        x, y = self.get_cell_center(0, 0)
        # start = self.w.create_oval(x - r, y - r, x + r, y + r, fill="red")
        start = self.w.create_rectangle(x - r, y - r, x + r, y + r, dash=(4, 4), fill='pink')
        self.w.tag_bind(start, '<ButtonPress-1>', lambda e: self.draw_solution())

    # 按结尾处红点按钮,更换地图
    def draw_end(self):
        r = self.cell_width // 3
        x, y = self.get_cell_center(self.mmap.max_x - 1, self.mmap.max_y - 1)
        end = self.w.create_oval(x - r, y - r, x + r, y + r, fill="red")
        self.w.tag_bind(end, '<ButtonPress-1>', lambda e: self.reset_map())

    # 重画地图
    def reset_map(self):
        self.mmap.reset_map()
        self.w.delete('all')
        self.draw_map()

    def create_line(self, x1, y1, x2, y2, **kwargs):
        self.w.create_line(x1, y1, x2, y2, **kwargs)

    def create_solution_line(self, x1, y1, x2, y2):
        self.create_line(x1, y1, x2, y2, fill="green")


def main():
    m = Map()  # 创建对象
    m.gen_map(20, 20)  # 调用方法,生成地图
    TKDrawMap(m)  # 画出路线图


if __name__ == '__main__':
    main()

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值