499 lines
15 KiB
Python
499 lines
15 KiB
Python
import heapq
|
|
import time
|
|
import codecs
|
|
import csv
|
|
|
|
class Cell:
|
|
def __init__(self,x,y,isWall,isStart,isExit):
|
|
self.x=x
|
|
self.y=y
|
|
self.isWall=isWall
|
|
self.isStart=isStart
|
|
self.isExit=isExit
|
|
def isPassable(self):
|
|
if self.isWall==False:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
class Maze:
|
|
def __init__(self,grid):
|
|
self.grid=grid
|
|
self.width=len(self.grid[0])
|
|
self.height=len(self.grid)
|
|
self.start=None
|
|
self.exit=None
|
|
for i in range(self.height):
|
|
for j in range(self.width):
|
|
if self.grid[i][j].isStart==True:
|
|
self.start=(j,i)
|
|
if self.grid[i][j].isExit==True:
|
|
self.exit=(j,i)
|
|
|
|
def getCell(self,x,y):
|
|
return self.grid[y][x]
|
|
|
|
def getNeighbours(self,cell):
|
|
x=cell.x
|
|
y=cell.y
|
|
cell_up=None
|
|
cell_down=None
|
|
cell_left=None
|
|
cell_right=None
|
|
|
|
#up
|
|
if y>0:
|
|
if self.grid[y-1][x].isPassable()==True:
|
|
cell_up=self.grid[y-1][x]
|
|
#down
|
|
if y<(self.height-1):
|
|
if self.grid[y+1][x].isPassable()==True:
|
|
cell_down=self.grid[y+1][x]
|
|
#left
|
|
if x>0:
|
|
if self.grid[y][x-1].isPassable()==True:
|
|
cell_left=self.grid[y][x-1]
|
|
#right
|
|
if x<(self.width-1):
|
|
if self.grid[y][x+1].isPassable()==True:
|
|
cell_right=self.grid[y][x+1]
|
|
|
|
#neighbours=[cell_up, cell_down, cell_left, cell_right]
|
|
neighbours=[cell_down, cell_right, cell_up, cell_left]
|
|
return neighbours
|
|
|
|
class TextFileMazeBuilder:
|
|
def build(self,filename):
|
|
with open(filename,"r") as f:
|
|
lines=f.readlines()
|
|
grid=[]
|
|
x=0
|
|
y=0
|
|
for i in range(len(lines)):
|
|
line=lines[i]
|
|
x=0
|
|
row=[]
|
|
for j in range(len(line)):
|
|
if line[j]=="#":
|
|
row.append(Cell(x,y,True,False,False))
|
|
elif line[j]==" ":
|
|
row.append(Cell(x,y,False,False,False))
|
|
elif line[j]=="S":
|
|
row.append(Cell(x,y,False,True,False))
|
|
elif line[j]=="E":
|
|
row.append(Cell(x,y,False,False,True))
|
|
x+=1
|
|
grid.append(row)
|
|
y+=1
|
|
return grid
|
|
|
|
class MazeBuilder:
|
|
def buildFromFile(self,filename):
|
|
grid = TextFileMazeBuilder().build(filename)
|
|
return Maze(grid)
|
|
|
|
class SearchStats:
|
|
def __init__(self,ttime,visited_cells,path_length):
|
|
self.ttime=ttime
|
|
self.visited_cells=visited_cells
|
|
self.path_length=path_length
|
|
|
|
class BFSStrategy:
|
|
def findPath(self,maze,start,exxit):
|
|
tstart=time.perf_counter()
|
|
queue=[]
|
|
queue.append((maze.getCell(start[0],start[1]),0))
|
|
visited_cells=[maze.getCell(start[0],start[1])]
|
|
Path={}
|
|
f=0
|
|
count=0
|
|
while len(queue)!=0:
|
|
temp=queue.pop(0)
|
|
cell=temp[0]
|
|
steps=temp[1]
|
|
directions=maze.getNeighbours(cell)
|
|
count+=1
|
|
|
|
if (cell.x,cell.y)==exxit:
|
|
end=time.perf_counter()
|
|
#print(end-tstart)
|
|
f=1
|
|
break
|
|
|
|
for i in directions:
|
|
if i!=None:
|
|
flag=0
|
|
for j in visited_cells:
|
|
if i==j:
|
|
flag=1
|
|
break
|
|
if flag==0:
|
|
queue.append((i,steps+1))
|
|
visited_cells.append(i)
|
|
Path[(i.x,i.y)]=(cell.x,cell.y)
|
|
reversePath=[]
|
|
if f==1:
|
|
cell=exxit
|
|
while cell!=start:
|
|
reversePath.append(cell)
|
|
cell=Path[cell]
|
|
reversePath.append(cell)
|
|
reversePath.reverse()
|
|
#print(len(reversePath)-1)
|
|
return reversePath, count
|
|
|
|
class DFSStrategy:
|
|
def findPath(self,maze,start,exxit):
|
|
tstart=time.perf_counter()
|
|
queue=[]
|
|
queue.append((maze.getCell(start[0],start[1]),0))
|
|
visited_cells=[maze.getCell(start[0],start[1])]
|
|
Path={}
|
|
f=0
|
|
count=0
|
|
while len(queue)!=0:
|
|
temp=queue.pop()
|
|
cell=temp[0]
|
|
steps=temp[1]
|
|
directions=maze.getNeighbours(cell)
|
|
count+=1
|
|
|
|
if (cell.x,cell.y)==exxit:
|
|
end=time.perf_counter()
|
|
#print(end-tstart)
|
|
f=1
|
|
break
|
|
|
|
for i in directions:
|
|
if i!=None:
|
|
flag=0
|
|
for j in visited_cells:
|
|
if i==j:
|
|
flag=1
|
|
break
|
|
if flag==0:
|
|
queue.append((i,steps+1))
|
|
visited_cells.append(i)
|
|
Path[(i.x,i.y)]=(cell.x,cell.y)
|
|
reversePath=[]
|
|
if f==1:
|
|
cell=exxit
|
|
while cell!=start:
|
|
reversePath.append(cell)
|
|
cell=Path[cell]
|
|
reversePath.append(cell)
|
|
reversePath.reverse()
|
|
#print(len(reversePath)-1)
|
|
return reversePath, count
|
|
|
|
|
|
|
|
class AStarStrategy:
|
|
def H(self,exxit,cell):
|
|
Hn=0
|
|
if exxit!=None:
|
|
Hn=abs(exxit[0]-cell.x)+abs(exxit[1]-cell.y)
|
|
return Hn
|
|
def findPath(self,maze,start,exxit):
|
|
tstart=time.perf_counter()
|
|
queue=[]
|
|
f=self.H(exxit,maze.getCell(start[0],start[1]))
|
|
heapq.heappush(queue, (f, f, (start[0],start[1])))
|
|
|
|
f_scores=[]
|
|
for i in range(maze.height):
|
|
row=[]
|
|
for j in range(maze.width):
|
|
row.append(None)
|
|
f_scores.append(row)
|
|
|
|
g_scores=[]
|
|
for i in range(maze.height):
|
|
row=[]
|
|
for j in range(maze.width):
|
|
row.append(None)
|
|
g_scores.append(row)
|
|
|
|
f_scores[start[1]][start[0]]=f
|
|
g_scores[start[1]][start[0]]=0
|
|
Path={}
|
|
flag=0
|
|
count=0
|
|
while len(queue)!=0:
|
|
temp=heapq.heappop(queue)
|
|
cell=temp[2]
|
|
directions=maze.getNeighbours(maze.getCell(cell[0], cell[1]))
|
|
count+=1
|
|
|
|
if (cell[0],cell[1])==exxit:
|
|
end=time.perf_counter()
|
|
#print(end-tstart)
|
|
flag=1
|
|
break
|
|
|
|
for i in directions:
|
|
if i!=None:
|
|
temp_g=g_scores[cell[1]][cell[0]]+1
|
|
temp_f=temp_g+self.H(exxit,i)
|
|
|
|
if f_scores[i.y][i.x]==None:
|
|
g_scores[i.y][i.x]=temp_g
|
|
f_scores[i.y][i.x]=temp_f
|
|
heapq.heappush(queue,(temp_f,self.H(exxit,i),(i.x,i.y)))
|
|
Path[(i.x,i.y)]=cell
|
|
elif temp_f<f_scores[i.y][i.x]:
|
|
f_scores[i.y][i.x]=temp_f
|
|
g_scores[i.y][i.x]=temp_g
|
|
heapq.heappush(queue,(temp_f,self.H(exxit,i),(i.x,i.y)))
|
|
Path[(i.x,i.y)]=cell
|
|
|
|
reversePath=[]
|
|
if flag==1:
|
|
cell=exxit
|
|
while cell!=start:
|
|
reversePath.append(cell)
|
|
cell=Path[cell]
|
|
reversePath.append(cell)
|
|
reversePath.reverse()
|
|
#print(len(reversePath)-1)
|
|
return reversePath, count
|
|
|
|
|
|
class PathFindingStrategy:
|
|
def __init__(self,strategy):
|
|
self.strategy=strategy
|
|
|
|
def findPath(self,maze,start,exxit):
|
|
if self.strategy=="BFS":
|
|
return BFSStrategy().findPath(maze,start,exxit)
|
|
elif self.strategy=="DFS":
|
|
return DFSStrategy().findPath(maze,start,exxit)
|
|
elif self.strategy=="AStar":
|
|
return AStarStrategy().findPath(maze,start,exxit)
|
|
|
|
class MazeSolver:
|
|
def __init__(self, maze, strategy):
|
|
self.maze=maze
|
|
self.strategy=PathFindingStrategy(strategy)
|
|
|
|
def setStrategy(self, strategy):
|
|
self.strategy=PathFindingStrategy(strategy)
|
|
|
|
def solve(self):
|
|
start=time.perf_counter()
|
|
path, visited = self.strategy.findPath(self.maze, self.maze.start, self.maze.exit)
|
|
end=time.perf_counter()
|
|
if len(path)>0:
|
|
path_length=len(path)-1
|
|
else:
|
|
path_length=0
|
|
return SearchStats(end-start, visited, path_length)
|
|
|
|
def test():
|
|
#maze=MazeBuilder().buildFromFile("1.txt")
|
|
#maze=MazeBuilder().buildFromFile("2.txt")
|
|
maze=MazeBuilder().buildFromFile("v.txt")
|
|
#maze=MazeBuilder().buildFromFile("maze_10x10.txt")
|
|
#maze=MazeBuilder().buildFromFile("maze_50x50.txt")
|
|
#maze=MazeBuilder().buildFromFile("maze_100x100.txt")
|
|
#maze=MazeBuilder().buildFromFile("maze_no_walls.txt")
|
|
#maze=MazeBuilder().buildFromFile("maze_no_exit.txt")
|
|
print(maze.exit)
|
|
print(maze.start)
|
|
stats=MazeSolver(maze, "BFS").solve()
|
|
print(stats.ttime,stats.visited_cells,stats.path_length)
|
|
stats=MazeSolver(maze, "DFS").solve()
|
|
print(stats.ttime,stats.visited_cells,stats.path_length)
|
|
stats=MazeSolver(maze, "AStar").solve()
|
|
print(stats.ttime,stats.visited_cells,stats.path_length)
|
|
#print(PathFindingStrategy("BFS").findPath(maze,maze.start,maze.exit))
|
|
#print(PathFindingStrategy("DFS").findPath(maze,maze.start,maze.exit))
|
|
#print(PathFindingStrategy("AStar").findPath(maze,maze.start,maze.exit))
|
|
#PathFindingStrategy("BFS").findPath(maze,maze.start,maze.exit)
|
|
#PathFindingStrategy("DFS").findPath(maze,maze.start,maze.exit)
|
|
#PathFindingStrategy("AStar").findPath(maze,maze.start,maze.exit)
|
|
|
|
def run():
|
|
maze=MazeBuilder().buildFromFile("maze_10x10.txt")
|
|
results = [
|
|
[u"Лабиринт", u"Стратегия", u"Среднее время (мс)", u"Посещено клеток",u"Длина пути"]
|
|
]
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "BFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["10x10", "BFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "DFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["10x10", "DFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "AStar").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["10x10", "AStar", temp, stats.visited_cells, stats.path_length])
|
|
|
|
with codecs.open("docs/data/[2]results.csv", "w", "utf-16") as f:
|
|
writer = csv.writer(f)
|
|
writer.writerows(results)
|
|
writer.writerow("")
|
|
|
|
maze=MazeBuilder().buildFromFile("maze_50x50.txt")
|
|
results = []
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "BFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["50x50", "BFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "DFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["50x50", "DFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "AStar").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["50x50", "AStar", temp, stats.visited_cells, stats.path_length])
|
|
|
|
with codecs.open("docs/data/[2]results.csv", "a+", "utf-16") as f:
|
|
writer = csv.writer(f)
|
|
writer.writerows(results)
|
|
writer.writerow("")
|
|
|
|
maze=MazeBuilder().buildFromFile("maze_100x100.txt")
|
|
results = []
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "BFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["100x100", "BFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "DFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["100x100", "DFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "AStar").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append(["100x100", "AStar", temp, stats.visited_cells, stats.path_length])
|
|
|
|
with codecs.open("docs/data/[2]results.csv", "a+", "utf-16") as f:
|
|
writer = csv.writer(f)
|
|
writer.writerows(results)
|
|
writer.writerow("")
|
|
|
|
maze=MazeBuilder().buildFromFile("maze_no_walls.txt")
|
|
results = []
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "BFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append([u"Без стен", "BFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "DFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append([u"Без стен", "DFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "AStar").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append([u"Без стен", "AStar", temp, stats.visited_cells, stats.path_length])
|
|
|
|
with codecs.open("docs/data/[2]results.csv", "a+", "utf-16") as f:
|
|
writer = csv.writer(f)
|
|
writer.writerows(results)
|
|
writer.writerow("")
|
|
|
|
maze=MazeBuilder().buildFromFile("maze_no_exit.txt")
|
|
results = []
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "BFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append([u"Без выхода", "BFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "DFS").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append([u"Без выхода", "DFS", temp, stats.visited_cells, stats.path_length])
|
|
|
|
temp=0
|
|
for i in range(10):
|
|
stats=MazeSolver(maze, "AStar").solve()
|
|
temp+=stats.ttime
|
|
temp=temp/10
|
|
temp=temp*(10**3)
|
|
results.append([u"Без выхода", "AStar", temp, stats.visited_cells, stats.path_length])
|
|
with codecs.open("docs/data/[2]results.csv", "a+", "utf-16") as f:
|
|
writer = csv.writer(f)
|
|
writer.writerows(results)
|
|
writer.writerow("")
|
|
|
|
run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|