python实现地牢迷宫生成的完整步骤

作者:揉揉漆 时间:2021-07-26 19:27:56 

基本属性

定义当前地牢的等级,地图长宽,房间数量,房间的最小最大长度,如下


class Map:
   def __init__(self):
       self.width = 30
       self.heigh = 30
       self.level = 1
       self.roomNum = 5
       self.map = np.zeros((self.heigh,self.width))
       self.roomMin = 3
       self.roomMax = 11

生成房间

编写initRoom()随机生成房间,限制最多循环次数,为了简单起见,先做一个不会重叠的房间。基本思路是:随机房间的中心点,随机房间的长宽,再进行判断房间有无重叠(在后续会生成通道,简单起见在这里也保证房间不会紧贴),若无重叠,房间有效,房间数加1。贴代码


   def initRoom(self):
       count = 0
       roomCount = 1
       while True:
           count += 1
           if count > 300:
               break
           if roomCount > self.roomNum:
               break
           x = random.randint(1,self.width-1)
           y = random.randint(1,self.heigh-1)
           wd = random.randint(self.roomMin,self.roomMax)
           ht = random.randint(self.roomMin, self.roomMax)
           r1 = ceil(y - ht/2)
           r2 = ceil(y + ht/2)
           c1 = ceil(x - wd/2)
           c2 = ceil(x + wd/2)
           if r1 < 1:
               r1 = 1
           if r2 >= self.heigh - 1:
               r2 = self.heigh - 2
           if c1 < 1:
               c1 = 1
           if c2 >= self.width - 1:
               c2 = self.width - 2
           w = c2 - c1 + 1
           h = r2 - r1 + 1
           if h / w >= 3 or w / h >= 3: #保证房间不是细长的
               continue
           judge = self.isValidRoom(r1,r2,c1,c2)
           if judge == 0:
               roomCount += 1
               self.room.append(Room(r1,r2,c1,c2))
               for i in range(r1,r2):
                   for j in range(c1,c2):
                       self.map[i,j] = 1

def isValidRoom(self,r1,r2,c1,c2):
       #检测有无覆盖
       for i in range(r1,r2):
           for j in range(c1,c2):
               if self.map[i,j] == 1:
                   return -1
       #检测有无紧贴房间
       for i in range(r1,r2):
           if self.map[i,c1-1] == 1 or self.map[i,c2+1] == 1:
               return 2
       for i in range(c1,c2):
           if self.map[r1-1,i] == 1 or self.map[r2+1,i] == 1:
               return 2
       return 0

看一下效果

python实现地牢迷宫生成的完整步骤

生成墙壁

编写initTile()生成包围房间和通道的墙壁,直接贴代码


   def initTile(self):
       offset = [[-1,0],[0,-1],[1,0],[0,1],[-1,-1],[1,1],[1,-1],[-1,1]]
       for i in range(self.heigh):
           for j in range(self.width):
               if self.map[i,j] == 0:
                   tag = 0
                   for it in offset:
                       if i+it[0] >= self.heigh or j+it[1] >= self.width or i+it[0] < 0 or j+it[1] < 0:
                           continue
                       if self.map[i+it[0],j+it[1]] != 3 and self.map[i+it[0],j+it[1]] != 4:
                           tag += self.map[i+it[0],j+it[1]]
                   if tag:
                       self.map[i,j] = 3

效果

python实现地牢迷宫生成的完整步骤

生成门口

随机选取房间的一个外围点当做房门,思路是在房间的长宽内随机两个数作为偏移量,预定义好四个方向的覆盖模板对偏移量进行加权和偏置,在这里我编写房间的类,加进地图的属性列表里。

除此之外,房间连通的思路是:在所有房间列表中随机抽出两个房间,将这两个房间连通,再随机选一个房间加回原来的房间列表,直至最后列表里只剩下一个房间。那么现在先来生成房门,代码如下


class Room():
   def __init__(self,r1,r2,c1,c2):
       w = c2 - c1
       h = r2 - r1
       self.width = w
       self.height = h
       self.cx = c1 + ceil(w/2)
       self.cy = r1 + ceil(h/2)
       self.xStart = c1
       self.xEnd = c2 - 1
       self.yStart = r1
       self.yEnd = r2 - 1

def randomTile(self):
       direction = random.randint(0,3)
       dir = [[0,1,-1,0],[1,0,0,-1],[1,0,0,self.height],[0,1,self.width,0]]
       x_off = random.randint(0,self.width-1)
       y_off = random.randint(0,self.height-1)
       x = self.xStart + x_off*dir[direction][0] + dir[direction][2]
       y = self.yStart + y_off*dir[direction][1] + dir[direction][3]
       if y == 0 or x == 0:
           return self.randomTile()
       else:
           return [y,x]
class Map:
   def initPath(self):
       #初始化门
       rm = self.room.copy()
       while len(rm) > 1:
           r1 = random.choice(rm)
           rm.remove(r1)
           r2 = random.choice(rm)
           rm.remove(r2)
           point0 = r1.randomTile()
           while point0[0] == self.heigh-1 or point0[1] == self.width-1:
               point0 = r1.randomTile()
           self.map[point0[0],point0[1]] = 2
           self.door.append(point0)
           self.breakTile(point0)
           point1 = r2.randomTile()
           while point1[0] == self.heigh-1 or point1[1] == self.width-1:
               point1 = r2.randomTile()
           self.map[point1[0],point1[1]] = 2
           self.breakTile(point1)
           self.door.append(point1)
           rn = random.randint(0,1)
           #a*算法寻找从point0到point1的路径
           #self.aStar(point0,point1)
           if rn == 0:
               rm.append(r1)
           else:
               rm.append(r2)

def breakTile(self,p):
       # 打通堵住的周围的墙壁
       if self.map[p[0] - 1, p[1]] == 1 and self.map[p[0] + 1, p[1]] == 3:
           self.map[p[0] + 1, p[1]] = 2
       elif self.map[p[0], p[1] - 1] == 1 and self.map[p[0], p[1] + 1] == 3:
           self.map[p[0], p[1] + 1] = 2
       elif self.map[p[0] + 1, p[1]] == 1 and self.map[p[0] - 1, p[1]] == 3:
           self.map[p[0] - 1, p[1]] = 2
       elif self.map[p[0], p[1] + 1] == 1 and self.map[p[0], p[1] - 1] == 3:
           self.map[p[0], p[1] - 1] = 2

看下效果

python实现地牢迷宫生成的完整步骤

生成通道

接着完善上述函数,在随机选取房门后,连接两个房门。

在这我选择的是A星算法,打通两个房门,直接上代码


   def aStar(self,p0,p1):
       open_list = []
       close_list = []
       offset = [[-1,0],[0,-1],[1,0],[0,1]]
       f = h = abs(p0[0] - p1[0]) * 10 + abs(p0[1] - p1[1]) * 10
       g = 0

def isInClose(p):
           for it in close_list:
               if it.value[3] == p:
                   return True
           return False
       def isInOpen(p):
           for it in open_list:
               if it.value[3] == p:
                   return True
           return False
       def findFather(p):
           for it in close_list:
               if it.value[3] == p:
                   return it.value[4]
           return [-1,-1]
       def findInOpen(p):
           for it in open_list:
               if it.value[3] == p:
                   return it
           return None

open_list.append(Node([f,g,h,p0,[-1,-1]]))
       while open_list:
           #for it in open_list:
           #    print(it.value)
           open_list.sort(key=(lambda x:x.value[0]))
           f_min = open_list[0]
           close_list.append(f_min)
           open_list.remove(f_min)
           for it in offset:
               p2 = [f_min.value[3][0]+it[0], f_min.value[3][1]+it[1]]
               if p2[0] == p1[0] and p2[1] == p1[1]:
                   #找到
                   close_list.append(Node([f,g,h,p2,f_min]))
                   p_father = f_min.value[3]
                   while True:
                       self.map[p_father[0],p_father[1]] = 2
                       p_father = findFather(p_father)
                       if p_father[0] == -1:
                           break
                   self.map[p0[0], p0[1]] = 4
                   return
               if p2[0] < 0 or p2[0] >= self.heigh or p2[1] < 0 or p2[1] >= self.width:
                   continue
               if (self.map[p2[0],p2[1]] != 0 and self.map[p2[0],p2[1]] != 2 and self.map[p2[0],p2[1]] != 4) or isInClose(p2):
                   continue
               h = abs(p2[0] - p1[0]) * 10 + abs(p2[1] - p1[1]) * 10
               g = f_min.value[1] + 10
               f = h + g
               if not isInOpen(p2):
                   open_list.append(Node([f,g,h,p2,f_min.value[3]]))
               else:
                   #比较最小的G 值
                   temp = findInOpen(p2)
                   if g < temp.value[1]:
                       open_list.remove(temp)
                       open_list.append(Node([f,g,h,p2,f_min.value[3]]))

效果

python实现地牢迷宫生成的完整步骤

这样,一个随机房间的地牢就已经生成,贴上完整代码


import random
import numpy as np
from math import ceil

class Node():
   def __init__(self, val=None):
       if val is None:
           val = [0, 0, 0, [-1, -1], [-1, -1]]
       self.value = val

class Room():
   def __init__(self,r1,r2,c1,c2):
       w = c2 - c1
       h = r2 - r1
       self.width = w
       self.height = h
       self.cx = c1 + ceil(w/2)
       self.cy = r1 + ceil(h/2)
       self.xStart = c1
       self.xEnd = c2 - 1
       self.yStart = r1
       self.yEnd = r2 - 1

def info(self):
       print('r1 c1 r2 c2:  ',self.yStart,self.xStart,self.yEnd,self.xEnd)
       print('cx    cy:     ',self.cx,self.cy)
       print('width height: ',self.width,self.height)

def randomTile(self):
       direction = random.randint(0,3)
       dir = [[0,1,-1,0],[1,0,0,-1],[1,0,0,self.height],[0,1,self.width,0]]
       x_off = random.randint(0,self.width-1)
       y_off = random.randint(0,self.height-1)
       x = self.xStart + x_off*dir[direction][0] + dir[direction][2]
       y = self.yStart + y_off*dir[direction][1] + dir[direction][3]
       if y == 0 or x == 0:
           return self.randomTile()
       else:
           return [y,x]

class Map:
   def __init__(self):
       self.width = 30
       self.heigh = 30
       self.level = 1
       self.roomNum = 5
       #0 is null, 1 is room, 2 is path, 3 is wall, 4 is door, 5 is up stair, 6 is downstair
       self.map = np.zeros((self.width,self.heigh))
       self.roomMin = 3
       self.roomMax = 11
       self.room = []
       self.door = []

self.initRoom()
       self.initTile()
       self.initPath()
       #self.initTile()
       #self.initDoor()

def initRoom(self):
       count = 0
       roomCount = 1
       while True:
           count += 1
           if count > 300:
               break
           if roomCount > self.roomNum:
               break
           x = random.randint(1,self.width-1)
           y = random.randint(1,self.heigh-1)
           wd = random.randint(self.roomMin,self.roomMax)
           if wd % 2 == 0:
               wd += 1
           ht = random.randint(self.roomMin, self.roomMax)
           if ht % 2 == 0:
               ht += 1
           r1 = ceil(y - ht/2)
           r2 = ceil(y + ht/2)
           c1 = ceil(x - wd/2)
           c2 = ceil(x + wd/2)
           if r1 < 1:
               r1 = 1
           if r2 >= self.heigh - 1:
               r2 = self.heigh - 2
           if c1 < 1:
               c1 = 1
           if c2 >= self.width - 1:
               c2 = self.width - 2
           w = c2 - c1 + 1
           h = r2 - r1 + 1
           if w == 0:
               continue
           if h == 0:
               continue
           if h / w >= 3 or w / h >= 3:
               continue
           judge = self.isValidRoom(r1,r2,c1,c2)
           if judge == 0:
               roomCount += 1
               self.room.append(Room(r1,r2,c1,c2))
               for i in range(r1,r2):
                   for j in range(c1,c2):
                       self.map[i,j] = 1

def initPath(self):
       #初始化门
       rm = self.room.copy()
       while len(rm) > 1:
           r1 = random.choice(rm)
           rm.remove(r1)
           r2 = random.choice(rm)
           rm.remove(r2)
           point0 = r1.randomTile()
           while point0[0] == self.heigh-1 or point0[1] == self.width-1:
               point0 = r1.randomTile()
           self.map[point0[0],point0[1]] = 2
           self.door.append(point0)
           self.breakTile(point0)
           point1 = r2.randomTile()
           while point1[0] == self.heigh-1 or point1[1] == self.width-1:
               point1 = r2.randomTile()
           self.map[point1[0],point1[1]] = 2
           self.breakTile(point1)
           self.door.append(point1)
           rn = random.randint(0,1)
           #a*算法寻找从point0到point1的路径
           self.aStar(point0,point1)
           if rn == 0:
               rm.append(r1)
           else:
               rm.append(r2)

def initDoor(self):
       for it in self.door:
           self.map[it[0],it[1]] = 4

def breakTile(self,p):
       # 打通堵住的周围的墙壁
       if self.map[p[0] - 1, p[1]] == 1 and self.map[p[0] + 1, p[1]] == 3:
           self.map[p[0] + 1, p[1]] = 2
       elif self.map[p[0], p[1] - 1] == 1 and self.map[p[0], p[1] + 1] == 3:
           self.map[p[0], p[1] + 1] = 2
       elif self.map[p[0] + 1, p[1]] == 1 and self.map[p[0] - 1, p[1]] == 3:
           self.map[p[0] - 1, p[1]] = 2
       elif self.map[p[0], p[1] + 1] == 1 and self.map[p[0], p[1] - 1] == 3:
           self.map[p[0], p[1] - 1] = 2

def initTile(self):
       offset = [[-1,0],[0,-1],[1,0],[0,1],[-1,-1],[1,1],[1,-1],[-1,1]]
       for i in range(self.heigh):
           for j in range(self.width):
               if self.map[i,j] == 0:
                   tag = 0
                   for it in offset:
                       if i+it[0] >= self.heigh or j+it[1] >= self.width or i+it[0] < 0 or j+it[1] < 0:
                           continue
                       if self.map[i+it[0],j+it[1]] != 3 and self.map[i+it[0],j+it[1]] != 4:
                           tag += self.map[i+it[0],j+it[1]]
                   if tag:
                       self.map[i,j] = 3

def isValidRoom(self,r1,r2,c1,c2):
       #检测有无覆盖
       for i in range(r1,r2):
           for j in range(c1,c2):
               if self.map[i,j] == 1:
                   return -1
       #检测有无紧贴房间
       for i in range(r1,r2):
           if self.map[i,c1-1] == 1 or self.map[i,c2+1] == 1:
               return 2
       for i in range(c1,c2):
           if self.map[r1-1,i] == 1 or self.map[r2+1,i] == 1:
               return 2
       return 0

def aStar(self,p0,p1):
       open_list = []
       close_list = []
       offset = [[-1,0],[0,-1],[1,0],[0,1]]
       f = h = abs(p0[0] - p1[0]) * 10 + abs(p0[1] - p1[1]) * 10
       g = 0

def isInClose(p):
           for it in close_list:
               if it.value[3] == p:
                   return True
           return False
       def isInOpen(p):
           for it in open_list:
               if it.value[3] == p:
                   return True
           return False
       def findFather(p):
           for it in close_list:
               if it.value[3] == p:
                   return it.value[4]
           return [-1,-1]
       def findInOpen(p):
           for it in open_list:
               if it.value[3] == p:
                   return it
           return None

open_list.append(Node([f,g,h,p0,[-1,-1]]))
       while open_list:
           #for it in open_list:
           #    print(it.value)
           open_list.sort(key=(lambda x:x.value[0]))
           f_min = open_list[0]
           close_list.append(f_min)
           open_list.remove(f_min)
           for it in offset:
               p2 = [f_min.value[3][0]+it[0], f_min.value[3][1]+it[1]]
               if p2[0] == p1[0] and p2[1] == p1[1]:
                   #找到
                   close_list.append(Node([f,g,h,p2,f_min]))
                   p_father = f_min.value[3]
                   while True:
                       self.map[p_father[0],p_father[1]] = 2
                       p_father = findFather(p_father)
                       if p_father[0] == -1:
                           break
                   self.map[p0[0], p0[1]] = 4
                   return
               if p2[0] < 0 or p2[0] >= self.heigh or p2[1] < 0 or p2[1] >= self.width:
                   continue
               if (self.map[p2[0],p2[1]] != 0 and self.map[p2[0],p2[1]] != 2 and self.map[p2[0],p2[1]] != 4) or isInClose(p2):
                   continue
               h = abs(p2[0] - p1[0]) * 10 + abs(p2[1] - p1[1]) * 10
               g = f_min.value[1] + 10
               f = h + g
               if not isInOpen(p2):
                   open_list.append(Node([f,g,h,p2,f_min.value[3]]))
               else:
                   #比较最小的G 值
                   temp = findInOpen(p2)
                   if g < temp.value[1]:
                       open_list.remove(temp)
                       open_list.append(Node([f,g,h,p2,f_min.value[3]]))

def printMap(self):
       for i in range(self.heigh):
           for j in range(self.width):
               print(int(self.map[i,j]),end='')
           print()

def printRoom(self):
       for r in self.room:
           r.info()

if __name__ == '__main__':
   map = Map()
   map.printMap()

可视化一下

python实现地牢迷宫生成的完整步骤

总结 

来源:https://blog.csdn.net/weixin_44715221/article/details/120458006

标签:python,迷宫,生成
0
投稿

猜你喜欢

  • python求最大值,不使用内置函数的实现方法

    2021-02-06 09:13:12
  • Django框架实现的普通登录案例【使用POST方法】

    2021-10-12 18:05:10
  • HTML语言将加入硬件操作功能

    2009-12-16 19:19:00
  • python列表推导式的原理及使用方法

    2022-01-23 13:47:03
  • Python如何调用JS文件中的函数

    2022-11-21 01:23:11
  • Python中使用第三方库xlrd来写入Excel文件示例

    2023-08-16 21:37:29
  • python threading模块操作多线程介绍

    2021-03-16 20:02:13
  • python加密打包程序详解

    2021-03-02 02:02:46
  • 解决Python 爬虫URL中存在中文或特殊符号无法请求的问题

    2022-04-17 13:47:18
  • python2.7安装opencv-python很慢且总是失败问题

    2021-01-10 06:11:15
  • Python脚本导出为exe程序的方法

    2022-08-22 21:33:05
  • php for 循环语句使用方法详细说明

    2023-11-17 21:43:21
  • Python中列表与元组的乘法操作示例

    2021-05-09 17:11:25
  • Python之is与==的区别详解

    2021-08-23 21:00:04
  • 对Python 除法负数取商的取整方式详解

    2023-10-02 20:21:41
  • Python列表删除的三种方法代码分享

    2022-10-26 19:13:35
  • python入门学习笔记分享

    2023-01-29 17:46:16
  • Python中强大的命令行库click入门教程

    2023-05-01 04:24:09
  • [翻译]标记语言和样式手册 Chapter 2 标题

    2008-01-16 11:56:00
  • 微信小程序-拍照或选择图片并上传文件

    2023-09-04 20:22:45
  • asp之家 网络编程 m.aspxhome.com