簡單介紹Python迷宮生成和迷宮破解演算法

ckxllf發表於2020-04-24

  1迷宮生成

  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, c - 1, 4] == 0:

  trace.append((r, c - 1))

  m[r, c - 1, 4] = 2 # 標記為已訪問

  if r > 0:

  if m[r - 1, c, 4] == 1:

  check.append('U')

  elif m[r - 1, c, 4] == 0:

  trace.append((r - 1, c))

  m[r - 1, c, 4] = 2

  if c < num_cols - 1:

  if m[r, c + 1, 4] == 1:

  check.append('R')

  elif m[r, c + 1, 4] == 0:

  trace.append((r, c + 1))

  m[r, c + 1, 4] = 2

  if r < num_rows - 1:

  if m[r + 1, c, 4] == 1:

  check.append('D')

  elif m[r + 1, c, 4] == 0:

  trace.append((r + 1, c))

  m[r + 1, c, 4] = 2

  if len(check):

  direction = random.choice(check)

  if direction == 'L': # 打通一面牆

  m[r, c, 0] = 1

  c = c - 1

  m[r, c, 2] = 1

  if direction == 'U':

  m[r, c, 1] = 1

  r = r - 1

  m[r, c, 3] = 1

  if direction == 'R':

  m[r, c, 2] = 1

  c = c + 1

  m[r, c, 0] = 1

  if direction == 'D':

  m[r, c, 3] = 1

  r = r + 1

  m[r, c, 1] = 1

  m[0, 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, num_cols, 5), dtype=np.uint8)

  r = 0

  c = 0

  trace = [(r, c)]

  while trace:

  m[r, c, 4] = 1 # 標記為已訪問

  check = []

  if c > 0 and m[r, c - 1, 4] == 0:

  check.append('L')

  if r > 0 and m[r - 1, c, 4] == 0:

  check.append('U')

  if c < num_cols - 1 and m[r, c + 1, 4] == 0:

  check.append('R')

  if r < num_rows - 1 and m[r + 1, c, 4] == 0:

  check.append('D')

  if len(check):

  trace.append([r, c])

  direction = random.choice(check)

  if direction == 'L':

  m[r, c, 0] = 1

  c = c - 1

  m[r, c, 2] = 1

  if direction == 'U':

  m[r, c, 1] = 1

  r = r - 1

  m[r, c, 3] = 1

  if direction == 'R':

  m[r, c, 2] = 1

  c = c + 1

  m[r, c, 0] = 1

  if direction == 'D':

  m[r, c, 3] = 1

  r = r + 1

  m[r, c, 1] = 1

  else:

  r, c = trace.pop()

  m[0, 0, 0] = 1

  m[num_rows - 1, num_cols - 1, 2] = 1

  return m

  2.迷宮破解

  1.填坑法

  思路:從起點開始,不斷隨機選擇沒牆的方向前進,當處於一個坑(除了來時的方向外三面都是牆)中時,退一步並建造一堵牆將坑封上。不斷重複以上步驟,最終就能抵達終點。

  優缺點:可以處理含有環路的迷宮,但是處理時間較長還需要更多的儲存空間。

  程式碼:

  def solve_fill(num_rows, num_cols, m): # 填坑法

  map_arr = m.copy() # 複製一份迷宮來填坑

  map_arr[0, 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, c, 0] = 0

  c = c - 1

  map_arr[r, c, 2] = 0

  elif go == 'U':

  map_arr[r, c, 1] = 0

  r = r - 1

  map_arr[r, c, 3] = 0

  elif go == 'R':

  map_arr[r, c, 2] = 0

  c = c + 1

  map_arr[r, c, 0] = 0

  elif go == 'D':

  map_arr[r, c, 3] = 0

  r = r + 1

  map_arr[r, c, 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, num_cols, map_arr): # 回溯法

  move_list = ['R']

  m = 1 # 回溯點組號

  mark = []

  r, c = (0, 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, c, m, way.pop())) # 記錄當前座標和可行移動方向

  if mark:

  r, c, m, 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, cols, Map)

  plt.imshow(draw_path(draw(rows, cols, Map), move), cmap='hot')

  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('bbb.png', format='png', transparent=True, dpi=300, pad_inches=0)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945560/viewspace-2688143/,如需轉載,請註明出處,否則將追究法律責任。

相關文章