採用遞迴與棧結合的方式實現迷宮分析與走迷宮(python3)
阿新 • • 發佈:2018-12-25
一、場景
1. General presentation
2.Examples
The file named maze_1.txt has the following contents.
二、題目分析
題目首先給了我們一個迷宮,要我門做兩件事:
第一件事:分析迷宮
第二件事:在分析的基礎上畫迷宮
對於迷宮的分析:
所謂的迷宮只給了我們一些數字,數字的範圍是0-3。
0:代表當前這個點與右邊和下面都不相連;
1:代表這個點和和右面的點相連;
2:代表這個點和和下面的點相連;
3:代表這個點和下面與右面的點相連。
通過一個二維陣列,可以將迷宮的牆確定下來。
在上圖中,藍色的線就是迷宮的牆,所給檔案的第一個數字是1,所以在迷宮中,第一行的第一個點和第一行的第二個點相連。
第一件事:分析迷宮:分析六個資料
1.分析迷宮有多少個門,也可以說是迷宮有多少個入口。
2.分析迷宮有多少個牆體集合.
3.分析迷宮有多少不可達區域,也可以說有多少個死角塊,也就是某一塊是死的,從外面進不來.
4.分析迷宮中有多少個可達區域.
5.分析迷宮中有多少個紅叉X
6.分析迷宮有多少條路徑即圖中黃色路線有多少條
第二件事:畫圖
1.畫藍線
2.畫綠點
3.畫紅叉
4.畫黃線
題目輸入與輸出要求
輸入:
迷宮由數字的檔案組成的.
輸出:
1.六項分析結果
2.一個latex檔案
迷宮的建立來自檔案maze_1.txt:
1 0 2 2 1 2 3 0
3 2 2 1 2 0 2 2
3 0 1 1 3 1 0 0
2 0 3 0 0 1 2 0
3 2 2 0 1 2 3 2
1 0 0 1 1 0 0 0
以上檔案規定了牆體的連線.
三、編寫程式碼
class MazeError(Exception):
def __init__(self, value):
self.value = value
class Road:
up = True
down = True
left = True
right = True
is_border = False
def __init__(self, up, down, left, right):
self.up = up
self.down = down
self.left = left
self.right = right
class Maze:
file_name = ""
model = [1, 2, 3, 0, "0", "1", "2", "3"]
result = [0, 0, 0, 0, 0, 0, 0] # 記錄結果
maze = list() # 最初的迷宮
b_maze = list() # 擴大的迷宮
road = list() # 路徑
b_road = list() # 擴大的路徑
viewed = list() # 記錄路的訪問對應第5項
viewed6 = list() # 記錄唯一路徑的訪問對應第6項
viewed4 = list() # 記錄路的連通訪問對應第4項
viewed3 = list() # 記錄封閉點
site_view = list() # 記錄點的訪問
entry = list() # 記錄死角個數
death = 0 # 記錄封閉路的個數
death_list = list() # 記錄封閉路的起點
success_path_start = list() # 記錄能夠走出去的路徑的起始點
temp = list() # 臨時儲存一條走出去的路徑包括的點
path = list() # 儲存所有路徑的經過的點
walls = list() # 畫圖的牆
pillars = list() # 畫圖的點
inner_points = list() # 畫X的塊
entry_exit_paths = list() # 畫路徑
def __init__(self, file_name):
# 讀取file到陣列maze裡面,儲存所有牆體資訊
self.file_name = file_name
file = open(file_name).readlines()
for line in file:
a = list()
for i in line:
if i in self.model:
a.append(i)
if len(a) > 0:
self.maze.append(a)
# print(self.maze)
# 檢測maze是否滿足條件
self.check()
# 擴大邊界,和初始化點的訪問
for line in self.maze:
b = list()
b.append("0")
x_site_view = list()
x_site_view.append(False)
for i in line:
b.append(i)
x_site_view.append(False)
b.append("0")
x_site_view.append(False)
self.b_maze.append(b)
self.site_view.append(x_site_view)
tmp = list()
for i in range(len(self.maze[0])):
tmp.append(False)
self.site_view.insert(0, tmp)
self.site_view.append(tmp)
self.b_maze.insert(0, ["0"] * len(self.b_maze[0]))
self.b_maze.append(["0"]*len(self.b_maze[0]))
# 根據牆體資訊,設定路徑資訊
# 以每個點的左上角的點為基準,讀取其餘影響該方格的點
for y in range(len(self.maze)-1):
x_line = list()
for x in range(len(self.maze[0])-1):
up = True
down = True
left = True
right = True
if self.maze[y][x] == '1':
up = False
elif self.maze[y][x] == '2':
left = False
elif self.maze[y][x] == '3':
up = False
left = False
if self.maze[y+1][x] == "1" or self.maze[y+1][x] == "3":
down = False
if self.maze[y][x+1] == "2" or self.maze[y][x+1] == "3":
right = False
road = Road(up, down, left, right)
x_line.append(road)
self.road.append(x_line)
# 擴大路徑,並初始化路徑的訪問
for y in self.road:
x_line = list()
road = Road(True, True, True, True)
road.is_border = True
x_line.append(road)
for x in y:
x_line.append(x)
x_line.append(road)
self.b_road.append(x_line)
temp = list()
for _ in self.b_road[0]:
road = Road(True, True, True, True)
road.is_border = True
temp.append(road)
self.b_road.insert(0, temp)
self.b_road.append(temp)
# 畫圖初始化
self.pillars.append("% Pillars\n")
self.inner_points.append("% Inner points in accessible cul-de-sacs\n")
self.entry_exit_paths.append("% Entry-exit paths without intersections\n")
def check(self):
length = len(self.maze[0])
for i in self.maze:
if len(i) != length:
raise MazeError("Incorrect input.")
for i in self.maze[len(self.maze)-1]:
if i in ["2", "3"]:
raise MazeError("Input does not represent a maze.")
for i in range(len(self.maze)):
if self.maze[i][len(self.maze[0])-1] in ["3", "1"]:
raise MazeError("Input does not represent a maze.")
def init_road_view(self):
self.viewed = list()
self.viewed6 = list()
self.viewed4 = list()
self.viewed3 = list()
for y in self.b_road:
x_viewed = list()
x_viewed6 = list()
x_viewed4 = list()
x_viewed3 = list()
for _ in y:
x_viewed.append(False)
x_viewed6.append(False)
x_viewed4.append(False)
x_viewed3.append(False)
self.viewed.append(x_viewed)
self.viewed6.append(x_viewed6)
self.viewed4.append(x_viewed4)
self.viewed3.append(x_viewed3)
def analyse(self):
self.init_road_view()
self.analyse_gates() # 1
self.analyse_wall_sets() # 2
self.analyse_sets() # 5
self.analyse_path() # 6
self.analyse_accessible_areas() # 4
self.analyse_inner_points() # 3
self.print_result()
# 分析第一項:分析迷宮有多少個門
def analyse_gates(self):
# 分四條邊考慮
count = 0
for i in self.road[0]:
if i.up is True:
count += 1
for i in self.road[len(self.road)-1]:
if i.down is True:
count += 1
for j in range(len(self.road)):
if self.road[j][0].left is True:
count += 1
if self.road[j][len(self.road[0])-1].right is True:
count += 1
self.result[1] = count
# 分析第二項:
# 深度優先遍歷
def analyse_wall_sets(self):
count = 0
for y in range(1, len(self.b_maze)-1):
for x in range(1, len(self.b_maze[0])-1):
if self.site_view[y][x] is False:
if not self.is_inner(y, x):
count += 1
self.deep(y, x)
self.result[2] = count
def deep(self, y, x):
if self.site_view[y][x] is True:
return
else:
self.site_view[y][x] = True
# 檢測上面的線
if self.b_maze[y-1][x] in ["2", "3"]:
self.deep(y-1, x)
# 檢測左邊的線
if self.b_maze[y][x-1] in ["1", "3"]:
self.deep(y, x-1)
# 檢測下面和右面的線
if self.b_maze[y][x] == "1":
self.deep(y, x+1)
elif self.b_maze[y][x] == "2":
self.deep(y+1, x)
elif self.b_maze[y][x] == "3":
self.deep(y+1, x)
self.deep(y, x+1)
else:
return
def is_inner(self, i, j):
if self.b_maze[i][j] == '0' and self.b_maze[i - 1][j] in ['0', '1'] and self.b_maze[i][j - 1] in ['0', '2']:
return True
else:
return False
# 分析第三項:
def analyse_inner_points(self):
count = 0
for i in self.death_list:
self.go3(i[2], i[0], i[1])
for line in self.viewed3:
for i in line:
if i is True:
count += 1
for y in range(1, len(self.b_maze)-1):
for x in range(1, len(self.b_maze[0])-1):
if not self.not_death_block(y, x):
count += 1
self.result[3] = count
# 找到所有死角塊
def go3(self, direction, y, x):
self.viewed3[y][x] = True
next_y = y
next_x = x
if direction == "up":
next_y = y-1
next_x = x
# 走右面
elif direction == "right":
next_y = y
next_x = x + 1
# 走下面
elif direction == "down":
next_y = y + 1
next_x = x
# 走左面
elif direction == "left":
next_y = y
next_x = x - 1
# self.go(direction, y, x - 1)
# direction 代表當前點的出口的方向
up = True
down = True
left = True
right = True
road = self.b_road[next_y][next_x]
if direction == "right" or road.left is False:
left = False
if direction == "left" or road.right is False:
right = False
if direction == "up" or road.down is False:
down = False
if direction == "down" or road.up is False:
up = False
next_road = Road(up, down, left, right)
next_direction = self.is_three_suround(next_road)
if next_direction != "no":
return self.go3(next_direction, next_y, next_x)
elif next_road.up is False and next_road.down is False and next_road.left is False and next_road.right is False:
self.viewed3[next_y][next_x] = True
return
# 分析第四項:共有多少個連通塊
def analyse_accessible_areas(self):
count = 0
for y in range(1, len(self.b_road)-1):
for x in range(1, len(self.b_road[0])-1):
if self.viewed4[y][x] is False:
if self.not_death_block(y, x):
count += 1
self.view(y, x)
self.result[4] = count-self.death
def not_death_block(self, y, x):
# 不是四面的死角 返回True 是四面的死角返回False
return self.b_road[y][x].up or self.b_road[y][x].down or self.b_road[y][x].left or self.b_road[y][x].right
def view(self, y, x):
if self.viewed4[y][x] is True:
return
self.viewed4[y][x] = True
if not self.not_death_block(y, x):
return
else:
if self.b_road[y][x].is_border is True:
return
else:
# 四條路的判定與遍歷
if self.b_road[y][x].up is True:
self.view(y-1, x)
if self.b_road[y][x].down is True:
self.view(y+1, x)
if self.b_road[y][x].left is True:
self.view(y, x-1)
if self.b_road[y][x].right is True:
self.view(y, x+1)
# 分析第五項:共有多少個死角
# 思路:深度優先遍歷
def analyse_sets(self):
for y in range(1, len(self.b_road)-1):
for x in range(1, len(self.b_road[0])-1):
if self.viewed[y][x] is True:
continue
direction = self.is_three_suround(self.b_road[y][x])
if direction != "no":
b = self.go(direction, y, x)
if b is False:
self.death_list.append([y, x, direction])
self.result[5] = len(self.entry)
def go(self, direction, y, x):
self.viewed[y][x] = True
next_y = y
next_x = x
if direction == "up":
next_y = y-1
next_x = x
# 走右面
elif direction == "right":
next_y = y
next_x = x + 1
# 走下面
elif direction == "down":
next_y = y + 1
next_x = x
# 走左面
elif direction == "left":
next_y = y
next_x = x - 1
# self.go(direction, y, x - 1)
if self.b_road[next_y][next_x].is_border is True:
self.entry.append([-2, -2, "end"])
return True
# direction 代表當前點的出口的方向
up = True
down = True
left = True
right = True
road = self.b_road[next_y][next_x]
if direction == "right" or road.left is False:
left = False
if direction == "left" or road.right is False:
right = False
if direction == "up" or road.down is False:
down = False
if direction == "down" or road.up is False:
up = False
next_road = Road(up, down, left, right)
next_direction = self.is_three_suround(next_road)
if next_direction != "no":
return self.go(next_direction, next_y, next_x)
elif self.is_two_surround(next_road):
the_point = [next_y, next_x, direction]
index = self.is_point_in_list(the_point)
if index >= 0:
# past_direction,direction是兩個入口,轉化成邊之後即可與另一條邊構成三環塊,然後繼續往下走
past_direction = self.entry.pop(index)[2]
the_direction = self.get_the_direction(past_direction, direction, next_y, next_x)
# 找到出口,呼叫go方法
if the_direction != "no":
self.go(the_direction, next_y, next_x)
# 該點入list,並記錄該點的已知入口
else:
self.entry.append(the_point)
return True
elif next_road.up is False and next_road.down is False and next_road.left is False and next_road.right is False:
self.viewed[next_y][next_x] = True
self.death += 1
return False
else:
self.entry.append([next_y, next_x, "end"])
return True
def is_three_suround(self, road):
count = 0
direction = "no"
if road.up is False:
count += 1
else:
direction = "up"
if road.down is False:
count += 1
else:
direction = "down"
if road.left is False:
count += 1
else:
direction = "left"
if road.right is False:
count += 1
else:
direction = "right"
if count == 3:
return direction
else:
return "no"
def is_two_surround(self,road):
count = 0
if road.up is False:
count += 1
if road.down is False:
count += 1
if road.left is False:
count += 1
if road.right is False:
count += 1
if count == 2:
return True
else:
return False
def is_point_in_list(self, point):
b = -1
for i in range(len(self.entry)):
if self.entry[i][0] == point[0] and self.entry[i][1] == point[1] and self.entry[i][2] != "end":
b = i
break
return b
def get_the_direction(self, past_direction, direction, next_y, next_x):
# 尋找出口
up = True
down = True
left = True
right = True
if "up" in [past_direction, direction] or self.b_road[next_y][next_x].down is False:
down = False
if "down" in [past_direction, direction] or self.b_road[next_y][next_x].up is False:
up = False
if "right" in [past_direction, direction] or self.b_road[next_y][next_x].left is False:
left = False
if "left" in [past_direction, direction] or self.b_road[next_y][next_x].right is False:
right = False
road = Road(up, down, left, right)
return self.is_three_suround(road)
# 分析第六項:共有多少個連通路徑
def analyse_path(self):
# 分四面進入,上面的就從上面進入,下面的就從下面進入,左面從左面進入。
count = 0
for x1 in range(1, len(self.b_road[0])-1):
if self.viewed6[1][x1] is False and self.b_road[1][x1].up is True:
if self.cross("up", 1, x1) is True:
self.success_path_start.append(["up", 1, x1])
count += 1
n = len(self.b_road)-2
for xn in range(1, len(self.b_road[0])-1):
if self.viewed6[n][xn] is False and self.b_road[n][xn].down is True:
if self.cross("down", n, xn) is True:
self.success_path_start.append(["down", n, xn])
count += 1
for y1 in range(1, len(self.b_road)-1):
if self.viewed6[y1][1] is False and self.b_road[y1][1].left is True:
if self.cross("left", y1, 1) is True:
self.success_path_start.append(["left", y1, 1])
count += 1
n2 = len(self.b_road[0])-2
for yn in range(1, len(self.b_road) - 1):
if self.viewed6[yn][n2] is False and self.b_road[yn][n2].right is True:
if self.cross("right", yn, n2) is True:
self.success_path_start.append(["right", yn, n2])
count += 1
self.result[6] = count
def cross(self, direction, y, x):
self.viewed6[y][x] = True
up = True
down = True
left = True
right = True
if direction == "left" or self.viewed[y][x-1] is True or self.b_road[y][x].left is False:
left = False
if direction == "right" or self.viewed[y][x+1] is True or self.b_road[y][x].right is False:
right = False
if direction == "down" or self.viewed[y+1][x] is True or self.b_road[y][x].down is False:
down = False
if direction == "up" or self.viewed[y-1][x] is True or self.b_road[y][x].up is False:
up = False
road = Road(up, down, left, right)
next_direction = self.is_three_suround(road)
if next_direction != "no":
n_direction = ""
next_y = y
next_x = x
if next_direction == "up":
next_y = y - 1
next_x = x
n_direction = "down"
# self.go(direction, y - 1, x)
# 走右面
elif next_direction == "right":
next_y = y
next_x = x + 1
n_direction = "left"
# self.go(direction, y, x + 1)
# 走下面
elif next_direction == "down":
next_y = y + 1
next_x = x
n_direction = "up"
# self.go(direction, y + 1, x)
# 走左面
elif next_direction == "left":
next_y = y
next_x = x - 1
n_direction = "right"
# self.go(direction, y, x - 1)
if self.b_road[next_y][next_x].is_border is True:
return True
else:
return self.cross(n_direction, next_y, next_x)
else:
return False
def cross2(self, direction, y, x):
self.temp.append([x-0.5, y-0.5])
up = True
down = True
left = True
right = True
if direction == "left" or self.viewed[y][x-1] is True or self.b_road[y][x].left is False:
left = False
if direction == "right" or self.viewed[y][x+1] is True or self.b_road[y][x].right is False:
right = False
if direction == "down" or self.viewed[y+1][x] is True or self.b_road[y][x].down is False:
down = False
if direction == "up" or self.viewed[y-1][x] is True or self.b_road[y][x].up is False:
up = False
road = Road(up, down, left, right)
next_direction = self.is_three_suround(road)
if next_direction != "no":
n_direction = ""
next_y = y
next_x = x
if next_direction == "up":
next_y = y - 1
next_x = x
n_direction = "down"
# 走右面
elif next_direction == "right":
next_y = y
next_x = x + 1
n_direction = "left"
# 走下面
elif next_direction == "down":
next_y = y + 1
next_x = x
n_direction = "up"
# 走左面
elif next_direction == "left":
next_y = y
next_x = x - 1
n_direction = "right"
if self.b_road[next_y][next_x].is_border is True:
self.temp.append([next_x-0.5, next_y-0.5])
return True
else:
self.cross2(n_direction, next_y, next_x)
else:
return
def go2(self, direction, y, x):
self.viewed[y][x] = False
next_y = y
next_x = x
if direction == "up":
next_y = y-1
next_x = x
# self.go(direction, y - 1, x)
# 走右面
elif direction == "right":
next_y = y
next_x = x + 1
# self.go(direction, y, x + 1)
# 走下面
elif direction == "down":
next_y = y + 1
next_x = x
# self.go(direction, y + 1, x)
# 走左面
elif direction == "left":
next_y = y
next_x = x - 1
# self.go(direction, y, x - 1)
# direction 代表當前點的出口的方向
up = True
down = True
left = True
right = True
road = self.b_road[next_y][next_x]
if direction == "right" or road.left is False:
left = False
if direction == "left" or road.right is False:
right = False
if direction == "up" or road.down is False:
down = False
if direction == "down" or road.up is False:
up = False
next_road = Road(up, down, left, right)
next_direction = self.is_three_suround(next_road)
if next_direction != "no":
self.go(next_direction, next_y, next_x)
elif next_road.up is False and next_road.down is False and next_road.left is False and next_road.right is False:
self.viewed[next_y][next_x] = False
# 輸出結果
def print_result(self):
# print(self.result)
out_result = list()
for i in range(1, 7):
if self.result[i] == 0:
out_result.append(str('no'))
elif self.result[i] == 1:
out_result.append(str('a'))
else:
out_result.append(str(self.result[i]))
# 輸出
print("The maze has "+out_result[0]+" gates. ")
print("The maze has "+out_result[1]+" sets of walls that are all connected.")
print("The maze has "+out_result[2]+" inaccessible inner point.")
print("The maze has "+out_result[3]+" unique accessible area.")
print("The maze has "+out_result[4]+" sets of accessible cul-de-sacs that are all connected.")
print("The maze has "+out_result[5]+" unique entry-exit path with no intersection not to cul-de-sacs. ")
# 展示
def display(self):
tex_file_name = self.file_name.split('.')[0] + ".tex"
tex_file = open(tex_file_name, 'w')
head =["\\documentclass[10pt]{article}\n",
"\\usepackage{tikz}\n",
"\\usetikzlibrary{shapes.misc}\n",
"\\usepackage[margin=0cm]{geometry}\n",
"\\pagestyle{empty}\n",
"\n",
"\\tikzstyle{every node}=[cross out, draw, red]\n",
"\n",
"\\begin{document}\n",
"\\vspace*{\\fill}\n",
"\\begin{center}\n",
"\\begin{tikzpicture}[x=0.5cm, y=-0.5cm, ultra thick, blue]\n",
"% Walls\n"]
border = ["\\end{tikzpicture}\n","\\end{center}\n","\\vspace*{\\fill}\n","\n","\\end{document}\n"]
# 開始畫牆
for y in range(len(self.maze)):
for x in range(len(self.maze[0])):
if self.maze[y][x] in ["1", "3"]:
x1 = x+1
y1 = y
s = " \\draw (" + str(x)+" ,"+str(y)+") -- ("+str(x1)+", "+str(y1)+");\n"
self.walls.append(s)
if self.maze[y][x] in ["2", "3"]:
x1 = x
y1 = y+1
s = " \\draw (" + str(x)+" ,"+str(y)+") -- ("+str(x1)+", "+str(y1)+");\n"
self.walls.append(s)
# 開始畫點
for i in range(1, len(self.b_maze) - 1):
for j in range(1, len(self.b_maze[0]) - 1):
if self.is_inner(i, j):
s = " \\fill[green] (" + str(j - 1) + "," + str(i - 1) + ") circle(0.2);\n"
self.pillars.append(s)
# 開始畫path
for i in self.success_path_start:
self.temp = list()
if i[0] == "up":
self.temp.append([i[2]-0.5, -0.5])
elif i[0] == "down":
self.temp.append([i[2]-0.5, len(self.b_road) - 1.5])
elif i[0] == "left":
self.temp.append([-0.5, i[1]-0.5])
elif i[0] == "right":
self.temp.append([len(self.b_road[0]) - 1.5, i[1]-0.5])
self.cross2(i[0], i[1], i[2])
self.path.append(self.temp)
for i in self.path:
for j in range(len(i)-1):
s = " \draw[dashed, yellow] ("+str(i[j][0])+","+str(i[j][1])+") -- ("+str(i[j+1][0])+","+str(i[j+1][1])+");\n"
self.entry_exit_paths.append(s)
# 開始畫 X ,
# 1.計算出所有的死塊,設定viewed
for i in self.death_list:
self.go2(i[2], i[0], i[1])
# 2.將所有visited中的點畫上X
for y in range(1, len(self.b_road)-1):
for x in range(1, len(self.b_road[0])-1):
if self.viewed[y][x] is True:
y1 = str(y-0.5)
x1 = str(x-0.5)
s = " \\node at ("+x1+","+y1+") {};\n"
self.inner_points.append(s)
tex_file.writelines(head)
tex_file.writelines(self.walls)
tex_file.writelines(self.pillars)
tex_file.writelines(self.inner_points)
tex_file.writelines(self.entry_exit_paths)
tex_file.writelines(border)
my_maze = Maze('maze_1.txt')
my_maze.analyse()
my_maze.display()