1. 程式人生 > 程式設計 >Python迷宮生成和迷宮破解演算法例項

Python迷宮生成和迷宮破解演算法例項

迷宮生成

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迷宮生成和迷宮破解演算法例項就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。