找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 文档 工具 设计
查看: 130|回复: 0

Python练习——走迷宫

[复制链接]

2万

主题

1249

回帖

2万

积分

超级版主

教育辅助界扛把子

附加身份标识
精华
1
热心
7
听众
1
威望
48
贡献
14329
违规
0
书币
50008
注册时间
2020-4-8

论坛元老灌水之王

发表于 2022-7-29 00:45 | 显示全部楼层 |阅读模式

迷宫地图是随机生成的,部分结果如下:


QQ图片20220729003740.png

[Python] 纯文本查看 复制代码
import random
import turtle
from random import randint, choice

CELL_SIZE = 16      # 设置用于绘制迷宫的单元格的尺寸
DOT_SIZE = 5          # 设置起点和终点的尺寸
LINE_SIZE = 3       # 设置探索路径的尺寸
scr = turtle.Screen()  # 建立屏幕对象 src
scr.colormode(255)  # 设置颜色模式为rgb数值模式
wallPen = turtle.Turtle()  # 建立画笔对象 wallPen 用于绘制墙体
pointPen = turtle.Turtle()  # 建立画笔对象 pointPen 用于绘制点
linePen = turtle.Turtle()  # 建立画笔对象 linePen 用于绘制探索路径
linePen.pensize(LINE_SIZE)  #设置画笔粗细为路径尺寸
difficulty=[15,25,45]

class CellType:
    ROAD = 0
    WALL = 1

# 墙的方向
class Direction:
    LEFT = 0,
    UP = 1,
    RIGHT = 2,
    DOWN = 3,

# 迷宫地图
class Maze:

    def __init__(self, width, height):
        self.width = width
        self.height = height
        #生成规定长宽的且内部元素全为0的二维表
        self.maze = [[0 for x in range(self.width)] for y in range(self.height)]

    def reset_maze(self, value):
        for y in range(self.height):
            for x in range(self.width):
                self.set_maze(x, y, value)

    def set_maze(self, x, y, value):
        self.maze[x][y] = CellType.ROAD if value == CellType.ROAD else CellType.WALL

    def visited(self, x, y):
        return self.maze[x][y] != 1

def check_neighbors(maze, x, y, width, height, checklist):
    directions = []
    #判断点的上下左右是否有邻居点
    if x > 0:
        if not maze.visited(2 * (x - 1) + 1, 2 * y + 1):
            directions.append(Direction.LEFT)
    if y > 0:
        if not maze.visited(2 * x + 1, 2 * (y - 1) + 1):
            directions.append(Direction.DOWN)
    if x < width - 1:
        if not maze.visited(2 * (x + 1) + 1, 2 * y + 1):
            directions.append(Direction.RIGHT)
    if y < height - 1:
        if not maze.visited(2 * x + 1, 2 * (y + 1) + 1):
            directions.append(Direction.UP)
    #对邻居点之间的墙进行拆墙并把新的添加到checklist中进行下一次的判断
    if len(directions):
        direction = choice(directions)
        if direction == Direction.LEFT:
            maze.set_maze(2 * (x - 1) + 1, 2 * y + 1, CellType.ROAD)
            maze.set_maze(2 * x, 2 * y + 1, CellType.ROAD)
            checklist.append((x - 1, y))
        elif direction == Direction.DOWN:
            maze.set_maze(2 * x + 1, 2 * (y - 1) + 1, CellType.ROAD)
            maze.set_maze(2 * x + 1, 2 * y, CellType.ROAD)
            checklist.append((x, y - 1))
        elif direction == Direction.RIGHT:
            maze.set_maze(2 * (x + 1) + 1, 2 * y + 1, CellType.ROAD)
            maze.set_maze(2 * x + 2, 2 * y + 1, CellType.ROAD)
            checklist.append((x + 1, y))
        elif direction == Direction.UP:
            maze.set_maze(2 * x + 1, 2 * (y + 1) + 1, CellType.ROAD)
            maze.set_maze(2 * x + 1, 2 * y + 2, CellType.ROAD)
            checklist.append((x, y + 1))
        return True
    return False

def random_prime(map, width, height):
    start_x, start_y = (randint(0, width - 1), randint(0, height - 1))
    #把二维表中所有下标x,y为奇数的元素设置为通路(即值为0)
    map.set_maze(2 * start_x + 1, 2 * start_y + 1, CellType.ROAD)
    checklist = [(start_x, start_y)]
    while len(checklist):
        #在通路中随机找到一个通路点位
        entry = choice(checklist)
        #判断该点位是否有邻居点位,若没有就移除该点位,之后不再参与搜索
        if not check_neighbors(map, entry[0], entry[1], width, height, checklist):
            checklist.remove(entry)

def do_random_prime(map):
    #把二维表中所有的元素都设置为墙(即值为1)
    map.reset_maze(CellType.WALL)
    random_prime(map, (map.width - 1) // 2, (map.height - 1) // 2)

def set_entrance_exit(maze):
    entrance = []
    for i in range(maze.height):
        #找到第一个maze[1] == 0的元素,并把maze[0]设置为通路(即值为0)
        if maze.maze[1] == 0:
            maze.set_maze(0, i, 0)
            entrance = [0, i]
            #把起点标记为A,方便后面对起点的填充
            maze.maze[0] = "A"
            break
    exit = []
    for i in range(maze.height - 1, 0, -1):
        # 找到第一个maze[maze.width -2] == 0的元素,并把maze[maze.width-1]设置为通路(即值为0)
        if maze.maze[maze.width - 2] == 0:
            maze.set_maze(maze.width - 1, i, 0)
            exit = [maze.width - 1, i]
            #把终点标记为B,方便后面对终点的填充
            maze.maze[maze.width - 1] = "B"
            break
    return entrance, exit

def generate_maze(width, height):
    # 初始化迷宫
    maze = Maze(width, height)
    # 生成地图
    do_random_prime(maze)
    # 选择起点和终点
    entrance, exit = set_entrance_exit(maze)
    # 返回地图
    return maze.maze, entrance, exit

#绘制墙体单元格
def draw_cell(y0, x0):
    # y0: 单元格所在列序号  x0: 单元格所在行序号
    #计算出单元格左上角的坐标
    tx = y0 * CELL_SIZE - L * CELL_SIZE / 2     #L-列序号  H-行序号
    ty = H * CELL_SIZE / 2 - x0 * CELL_SIZE
    wallPen.penup()  # 提起墙画笔
    #turtle.goto(x,y)       x负左移正右移,y负下移正上移
    wallPen.goto(tx, ty)  # 根据计算出来的坐标移动到单元格左上角
    wallPen.pendown()  # 放下画笔
    wallPen.begin_fill()  # 开启填充,此时经过的形状会被填充
    # 由单元格左上角开始绘制一个边长为CELL_SIZE的正方形
    for i in range(4):
        wallPen.fd(CELL_SIZE)       #向前走cell_size(单元格)像素
        wallPen.right(90)           #基于当前角度向右转90度
    wallPen.end_fill()  # 关闭填充

# 在单元格绘制圆点
def draw_dot(y0, x0, color="black"):
     # x0: 单元格所在行序号  y0: 单元格所在列序号    color: 圆点的颜色
    # 计算出单元格左上角的坐标
    tx = y0 * CELL_SIZE - L * CELL_SIZE / 2
    ty = H * CELL_SIZE / 2 - x0 * CELL_SIZE
    # 计算出所在单元格中心的坐标
    cx = tx + CELL_SIZE / 2
    cy = ty - CELL_SIZE / 2
    pointPen.penup()    #提起画笔

    pointPen.goto(cx, cy)       #根据计算出来的坐标移动到单元格中心
        #turtle.dot(r)绘制一个指定直径和颜色的圆点
    pointPen.dot(DOT_SIZE, color)       #绘制一个直径为DOT_SIZE和颜色为color的圆点

def draw_mazeMap(mazeMap):      #画迷宫
    scr.tracer(0)  # 跳过绘制动画
    for h in range(len(mazeMap)):      #  h,行号
        row = mazeMap[h]
        for l in range(len(row)):   # l,列号
            item = row[l]
            if item == CellType.WALL:
                draw_cell(l, h)         # 绘制具体的单元格
            elif item == "A":           # 设置起点颜色为红色
                draw_dot(l, h, "red")
            elif item == "B":              # 设置终点颜色为蓝色
                draw_dot(l, h, "blue")

# 绘制路径,以画笔当前所在位置为起点,以单元格(y0, x0)中心作为终点,绘制路径。
def draw_path(y0, x0, color="blue"):
    # 计算出单元格左上角的坐标
    tx = y0 * CELL_SIZE - L * CELL_SIZE / 2
    ty = H * CELL_SIZE / 2 - x0 * CELL_SIZE
    # 计算出所在单元格中心的坐标
    cx = tx + CELL_SIZE / 2
    cy = ty - CELL_SIZE / 2

    linePen.color(color)            #设置填充颜色为color(蓝色)
    linePen.goto(cx, cy)            #根据计算出来的坐标移动到单元格中心

def DFS(mazeMap,y0,x0):
    # 1,找到终点,探索完成,
    if [x0,y0] == exit:
        draw_path(y0, x0)
        return True
    # 2,找到墙或者试探过的点或者越界,试探失败,然后告诉上一步这一步试探是失败的
    if not (0 <= y0 < len(mazeMap) and 0 <= x0 < len(mazeMap)):
        return False
    if mazeMap[x0][y0] in [CellType.WALL, "#"]:
        return False

    mazeMap[x0][y0] = "#"           # 试探后标记该点为已试探过
    draw_path(y0, x0)               #填充路径

    # 右下左上四个探索的方向
    direction = [(0, 1),(0,-1),(1, 0),(-1, 0)]

    for d in direction:         #试探四个方向
        dx, dy = d
        found = DFS(mazeMap, y0 + dy, x0 + dx)
        if found:
        # 3,向某个方向的试探得出的结论是成功的,那么探索完成,不再试探,并且告诉上一步试探这一方向是能够试探成功的
            draw_path(y0, x0, "blue")                 #填充路径为蓝色
            return True
        else:
            # 4,向某个方向的试探得出的结论是失败的,那么换一个方向进行试探
            draw_path(y0, x0, "red")                #填充走过的错误路径为红色
    # 5,向所有方向试探都失败了,那么试探失败,并告诉上一步这一方向试探是失败的
    return False

#  开始迷宫探索
def find_path(mazeMap):
    # 获取起点所在行和列的序号
    starty, startx = 0, 0                    #  h,行号
    for h in range(len(mazeMap)):
        row = mazeMap[h]
        for l in range(len(row)):           # l,列号
            item = row[l]
            if item == "A":
                starty, startx = l, h
    linePen.penup()                 #提起路径画笔
    draw_path(starty, startx)       #填充路径
    linePen.pendown()               #放下路径画笔
    DFS(mazeMap, starty, startx)    #进入递归搜索

if __name__=="__main__":
    num=random.randint(0,2)
    t=difficulty[num]
    mazeMap, entrance, exit = generate_maze(t, t)
    H, L = len(mazeMap), len(mazeMap[0])        # 获取迷宫的长宽,H-行数,L-列数
    scr.setup(width=L * CELL_SIZE+20, height=H * CELL_SIZE+20)  # 设置屏幕尺寸大小,显示所有迷宫的单元格
    draw_mazeMap(mazeMap)       #绘制迷宫
    scr.tracer(8)           #绘制速度
    find_path(mazeMap)      #探索出路
    turtle.done()           # 防止绘制完窗口自动退出

Great works are not done by strength, but by persistence! 历尽艰辛的飞升者,成了围剿孙悟空的十万天兵之一。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则 需要先绑定手机号


免责声明:
本站所发布的第三方软件及资源(包括但不仅限于文字/图片/音频/视频等仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢某程序或某个资源,请支持正版软件及版权方利益,注册或购买,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To: admin@cdsy.xyz

QQ|Archiver|手机版|小黑屋|城东书院 ( 湘ICP备19021508号-1|湘公网安备 43102202000103号 )

GMT+8, 2024-11-23 17:31 , Processed in 0.041122 second(s), 30 queries .

Powered by Discuz! CDSY.XYZ

Copyright © 2019-2023, Tencent Cloud.

快速回复 返回顶部 返回列表