Python迷宮生成和迷宮破解演算法例項
阿新 • • 發佈:2020-01-09
迷宮生成
1.隨機PRIM
思路:先讓迷宮中全都是牆,不斷從列表(最初只含有一個啟始單元格)中選取一個單元格標記為通路,將其周圍(上下左右)未訪問過的單元格放入列表並標記為已訪問,再隨機選取該單元格與周圍通路單元格(若有的話)之間的一面牆打通。重複以上步驟直到列表為空,迷宮生成完畢。這種方式生成的迷宮難度高,岔口多。
效果:
程式碼:
import random import numpy as np from matplotlib import pyplot as plt def build_twist(num_rows,num_cols): # 扭曲迷宮 # (行座標,列座標,四面牆的有無&訪問標記) m = np.zeros((num_rows,num_cols,5),dtype=np.uint8) r,c = 0,0 trace = [(r,c)] while trace: r,c = random.choice(trace) m[r,c,4] = 1 # 標記為通路 trace.remove((r,c)) check = [] if c > 0: if m[r,c - 1,4] == 1: check.append('L') elif m[r,4] == 0: trace.append((r,c - 1)) m[r,4] = 2 # 標記為已訪問 if r > 0: if m[r - 1,4] == 1: check.append('U') elif m[r - 1,4] == 0: trace.append((r - 1,c)) m[r - 1,4] = 2 if c < num_cols - 1: if m[r,c + 1,4] == 1: check.append('R') elif m[r,c + 1)) m[r,4] = 2 if r < num_rows - 1: if m[r + 1,4] == 1: check.append('D') elif m[r + 1,4] == 0: trace.append((r + 1,c)) m[r + 1,4] = 2 if len(check): direction = random.choice(check) if direction == 'L': # 打通一面牆 m[r,0] = 1 c = c - 1 m[r,2] = 1 if direction == 'U': m[r,1] = 1 r = r - 1 m[r,3] = 1 if direction == 'R': m[r,2] = 1 c = c + 1 m[r,0] = 1 if direction == 'D': m[r,3] = 1 r = r + 1 m[r,1] = 1 m[0,0] = 1 m[num_rows - 1,num_cols - 1,2] = 1 return m
2.深度優先
思路:從起點開始隨機遊走並在前進方向兩側建立牆壁,標記走過的單元格,當無路可走(周圍無未訪問過的單元格)時重複返回上一個格子直到有新的未訪問單元格可走。最終所有單元格都被訪問過後迷宮生成完畢。這種方式生成的迷宮較為簡單,由一條明顯但是曲折的主路徑和不多的分支路徑組成。
效果:
程式碼:
def build_tortuous(num_rows,num_cols): # 曲折迷宮 m = np.zeros((num_rows,dtype=np.uint8) r = 0 c = 0 trace = [(r,c)] while trace: m[r,4] = 1 # 標記為已訪問 check = [] if c > 0 and m[r,4] == 0: check.append('L') if r > 0 and m[r - 1,4] == 0: check.append('U') if c < num_cols - 1 and m[r,4] == 0: check.append('R') if r < num_rows - 1 and m[r + 1,4] == 0: check.append('D') if len(check): trace.append([r,c]) direction = random.choice(check) if direction == 'L': m[r,1] = 1 else: r,c = trace.pop() m[0,2] = 1 return m
迷宮破解
效果:
1.填坑法
思路:從起點開始,不斷隨機選擇沒牆的方向前進,當處於一個坑(除了來時的方向外三面都是牆)中時,退一步並建造一堵牆將坑封上。不斷重複以上步驟,最終就能抵達終點。
優缺點:可以處理含有環路的迷宮,但是處理時間較長還需要更多的儲存空間。
程式碼:
def solve_fill(num_rows,m): # 填坑法 map_arr = m.copy() # 拷貝一份迷宮來填坑 map_arr[0,0] = 0 map_arr[num_rows-1,num_cols-1,2] = 0 move_list = [] xy_list = [] r,c = (0,0) while True: if (r == num_rows-1) and (c == num_cols-1): break xy_list.append((r,c)) wall = map_arr[r,c] way = [] if wall[0] == 1: way.append('L') if wall[1] == 1: way.append('U') if wall[2] == 1: way.append('R') if wall[3] == 1: way.append('D') if len(way) == 0: return False elif len(way) == 1: # 在坑中 go = way[0] move_list.append(go) if go == 'L': # 填坑 map_arr[r,0] = 0 c = c - 1 map_arr[r,2] = 0 elif go == 'U': map_arr[r,1] = 0 r = r - 1 map_arr[r,3] = 0 elif go == 'R': map_arr[r,2] = 0 c = c + 1 map_arr[r,0] = 0 elif go == 'D': map_arr[r,3] = 0 r = r + 1 map_arr[r,1] = 0 else: if len(move_list) != 0: # 不在坑中 come = move_list[len(move_list)-1] if come == 'L': if 'R' in way: way.remove('R') elif come == 'U': if 'D' in way: way.remove('D') elif come == 'R': if 'L' in way: way.remove('L') elif come == 'D': if 'U' in way: way.remove('U') go = random.choice(way) # 隨機選一個方向走 move_list.append(go) if go == 'L': c = c - 1 elif go == 'U': r = r - 1 elif go == 'R': c = c + 1 elif go == 'D': r = r + 1 r_list = xy_list.copy() r_list.reverse() # 行動座標記錄的反轉 i = 0 while i < len(xy_list)-1: # 去掉重複的移動步驟 j = (len(xy_list)-1) - r_list.index(xy_list[i]) if i != j: # 說明這兩個座標之間的行動步驟都是多餘的,因為一頓移動回到了原座標 del xy_list[i:j] del move_list[i:j] r_list = xy_list.copy() r_list.reverse() i = i + 1 return move_list
2.回溯法
思路:遇到岔口則將岔口座標和所有可行方向壓入棧,從棧中彈出一個座標和方向,前進。不斷重複以上步驟,最終就能抵達終點。
優缺點:計算速度快,需要空間小,但無法處理含有環路的迷宮。
程式碼:
def solve_backtrack(num_rows,map_arr): # 回溯法 move_list = ['R'] m = 1 # 回溯點組號 mark = [] r,0) while True: if (r == num_rows-1) and (c == num_cols-1): break wall = map_arr[r,c] way = [] if wall[0] == 1: way.append('L') if wall[1] == 1: way.append('U') if wall[2] == 1: way.append('R') if wall[3] == 1: way.append('D') come = move_list[len(move_list) - 1] if come == 'L': way.remove('R') elif come == 'U': way.remove('D') elif come == 'R': way.remove('L') elif come == 'D': way.remove('U') while way: mark.append((r,m,way.pop())) # 記錄當前座標和可行移動方向 if mark: r,go = mark.pop() del move_list[m:] # 刪除回溯點之後的移動 else: return False m = m + 1 move_list.append(go) if go == 'L': c = c - 1 elif go == 'U': r = r - 1 elif go == 'R': c = c + 1 elif go == 'D': r = r + 1 del move_list[0] return move_list
測試
rows = int(input("Rows: ")) cols = int(input("Columns: ")) Map = build_twist(rows,cols) plt.imshow(draw(rows,cols,Map),cmap='gray') fig = plt.gcf() fig.set_size_inches(cols/10/3,rows/10/3) plt.gca().xaxis.set_major_locator(plt.NullLocator()) plt.gca().yaxis.set_major_locator(plt.NullLocator()) plt.subplots_adjust(top=1,bottom=0,right=1,left=0,hspace=0,wspace=0) plt.margins(0,0) fig.savefig('aaa.png',format='png',transparent=True,dpi=300,pad_inches=0) move = solve_backtrack(rows,Map) plt.imshow(draw_path(draw(rows,move),cmap='hot') fig = plt.gcf() fig.set_size_inches(cols/10/3,0) fig.savefig('bbb.png',pad_inches=0)
以上這篇Python迷宮生成和迷宮破解演算法例項就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。