python實現地牢迷宮生成的完整步驟
基本屬性
定義當前地牢的等級,地圖長寬,房間數量,房間的最小最大長度,如下
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
看一下效果
生成墻壁
編寫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
效果
生成門口
隨機選取房間的一個外圍點當做房門,思路是在房間的長寬內隨機兩個數作為偏移量,預定義好四個方向的覆蓋模板對偏移量進行加權和偏置,在這裡我編寫房間的類,加進地圖的屬性列表裡。
除此之外,房間連通的思路是:在所有房間列表中隨機抽出兩個房間,將這兩個房間連通,再隨機選一個房間加回原來的房間列表,直至最後列表裡隻剩下一個房間。那麼現在先來生成房門,代碼如下
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
看下效果
生成通道
接著完善上述函數,在隨機選取房門後,連接兩個房門。
在這我選擇的是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]]))
效果
這樣,一個隨機房間的地牢就已經生成,貼上完整代碼
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實現地牢迷宮生成的文章就介紹到這瞭,更多相關python地牢迷宮生成內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 一文教你用python編寫Dijkstra算法進行機器人路徑規劃
- python基於opencv批量生成驗證碼的示例
- python Graham求凸包問題並畫圖操作
- python六種基本數據類型及常用函數展示
- Python用 matplotlib 繪制柱狀圖