200行python实现一个迷宫

代码如下:

#coding=utf-8
import random, sys, time
from Tkinter import *
 
def timing(out_prefix, func, *args, **kwargs):
  print("%s:timing start..."%(out_prefix))
  t1 = time.time()
  r = func(*args, **kwargs)
  t2 = time.time()
  print("%s:%ss"%(out_prefix, t2 - t1))
  return r

class UI:
  def __init__(self, maze):
    self.maze = maze
    self.w, self.b = 1, 2
    self.root, self.canvas = None, None

  def output_cell(self, clac_func):
    self.root = Tk()
    self.root.title('MAZE')
    self.canvas = Canvas(self.root, width=self.maze.width*self.w*4+self.w+self.b*2-4, height=self.maze.heigh*self.w*4+self.w+self.b*2-4)
    self.canvas.create_rectangle(self.b, self.b, self.maze.width*self.w*4+self.w+self.b, self.b+self.w, fill='dimgray', width = 0)
    self.canvas.create_rectangle(self.b, self.b, self.b+self.w, self.maze.heigh*self.w*4+self.w+self.b, fill='dimgray', width = 0)
    for y in range(self.maze.heigh):
      for x in range(self.maze.width):
        if not self.maze.cells[x][y].is_open(Cell.EDGE_RIGHT):
          self.canvas.create_rectangle(self.b+(x+1)*self.w*4, self.b+y*self.w*4, self.b+(x+1)*self.w*4+self.w, self.b+y*self.w*4+self.w*5, fill='dimgray', width = 0)
        if not self.maze.cells[x][y].is_open(Cell.EDGE_BOTTOM):
          self.canvas.create_rectangle(self.b+x*self.w*4, self.b+(y+1)*self.w*4, self.b+x*self.w*4+self.w*5, self.b+(y+1)*self.w*4+self.w, fill='dimgray', width = 0)
    self.canvas.pack()
    self.root.bind('<Button-1>', clac_func)
    self.root.mainloop()

  def output_path(self, paths, entrance, exit):
    self.canvas.create_rectangle(self.b+entrance[0]*self.w*4+self.w, self.b+entrance[1]*self.w*4+self.w, self.b+entrance[0]*self.w*4+self.w+3*self.w, self.b+entrance[1]*self.w*4+self.w+3*self.w, fill='lime', width = 0)
    self.canvas.create_rectangle(self.b+exit[0]*self.w*4+self.w, self.b+exit[1]*self.w*4+self.w, self.b+exit[0]*self.w*4+self.w+3*self.w, self.b+exit[1]*self.w*4+self.w+3*self.w, fill='fuchsia', width = 0)
    line_colors = ['LightSkyBlue4', 'LightYellow2', 'pale goldenrod', 'SlateGray1', 'PaleVioletRed4', 'DarkOliveGreen2', 'SlateGray2', 'khaki3', 'PaleVioletRed3', 'DeepPink3', 'gold3', 'honeydew4', 'snow2', 'OliveDrab1', 'medium slate blue', 'DeepPink2', 'DeepSkyBlue3', 'coral3', 'plum3', 'goldenrod2']
    line_color = line_colors[random.randint(0, len(line_colors) - 1)]
    for p in paths:
      for i in range(len(p)):
        x0, y0 = p[i]
        if len(p) <= i + 1:
          break
        x1, y1 = p[i+1]
        if x0 > x1:
          x0, x1 = x1, x0
        if y0 > y1:
          y0, y1 = y1, y0
        p1x, p1y, p2x, p2y = self.b+x0*self.w*4+self.w*2, self.b+y0*self.w*4+self.w*2, self.b+x1*self.w*4+self.w*3, self.b+y1*self.w*4+self.w*3
        self.canvas.create_rectangle(p1x, p1y, p2x, p2y, fill=line_color, width = 0)
        self.root.update()
        time.sleep(0.01)
 
class Cell:
  EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM = 1, 2, 4, 8
  EDGE_ALL = EDGE_LEFT | EDGE_TOP | EDGE_RIGHT | EDGE_BOTTOM
  EDGES = [EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM]
  __slots__ = ('edges', 'branch_no')
  def __init__(self):
    self.edges = self.EDGE_ALL
    self.branch_no = 0
  def is_open(self, edge):
    return self.edges & edge != edge
  def open_edge(self, edge, branch_no):
    if self.is_open(edge):
      return False
    self.edges =  self.edges & (~edge)
    if self.branch_no == 0:
      self.branch_no = branch_no
    return True
  def get_branch_no(self):
    return self.branch_no
 
class Maze:
  def __init__(self, width, heigh):
    self.width, self.heigh = int(width), int(heigh)
    self.cells = [[Cell() for y in range(heigh)] for x in range(width)]
    self.valid = self.width > 0 and self.heigh > 0 and self.width + self.heigh > 2
    if not self.valid:
      print('args for constructor wrong, the new Maze objest is invalid!')
      return
    timing('generate', self._generate)
    self.ui = UI(self)
    self.ui.output_cell(lambda e: self.calc((random.randint(0, self.width - 1), random.randint(0, self.width - 1)), (random.randint(0, self.heigh - 1), random.randint(0, self.heigh - 1))))
    timing('generate', self._generate)
 
  def calc(self, entrance, exit):
    entrance = int(max(0, min(self.width-1, entrance[0]))), int(max(0, min(self.heigh-1, entrance[1])))
    exit = int(max(0, min(self.width-1, exit[0]))), int(max(0, min(self.heigh-1, exit[1])))
    paths = self._calc(entrance, exit)
    self.ui.output_path(paths, entrance, exit)
     
  def _generate(self):
    left =  [(x, y) for x in range(self.width) for y in range(self.heigh)]
    edges = Cell.EDGES
    branch_no = 0
    while len(left):
      x, y = left[random.randint(0, len(left) - 1)]
      branch_no, branchs = branch_no + 1, []
      while True:
        if (x, y) not in branchs:
          branchs.append((x, y))
          left.remove((x, y))
        random.shuffle(edges)
        for e in edges:
          if self._open_edge(x, y, e, branch_no):
            x, y, e = self._get_neighbor(x, y, e)
            break
        else:
          if branch_no == 1:
            break
          x, y = branchs[random.randint(0, len(branchs) - 1)]
          continue
        if self.cells[x][y].is_open(~e & Cell.EDGE_ALL):
          break
 
  def _calc(self, entrance, exit):
    return timing('calc_bfs',self._calc_bfs, entrance, exit)
    return timing('calc_dfs',self._calc_dfs, entrance, exit)
 
  def _open_edge(self, x, y, e, branch_no):
    if not self._check_open_edge(x, y, e, branch_no):
      return False
    if not self.cells[x][y].open_edge(e, branch_no):
      return False
    self._open_neighbors(x, y, e, branch_no)
    return True
 
  def _check_open_edge(self, x, y, e, branch_no):
    if x < 0 or x >= self.width or y < 0 or y >= self.heigh:
      return False
    for edge in Cell.EDGES:
      if e & edge:
        x1, y1, e1 = self._get_neighbor(x, y, edge)
        if x1 is None or y1 is None or e1 is None or branch_no == self.cells[x1][y1].get_branch_no():
          return False
    return True
 
  def _open_neighbors(self, x, y, e, branch_no):
    for edge in Cell.EDGES:
      if e & edge:
        self._open_neighbor(x, y, edge, branch_no)
 
  def _open_neighbor(self, x, y, e, branch_no):
    x, y, e = self._get_neighbor(x, y, e)
    if x is None or y is None or e is None:
      return False
    return self.cells[x][y].open_edge(e, branch_no)
 
  def _get_neighbor(self, x, y, e):
    if e == Cell.EDGE_LEFT:
      x, y, e = x - 1, y, Cell.EDGE_RIGHT
    elif e == Cell.EDGE_TOP:
      x, y, e = x, y - 1, Cell.EDGE_BOTTOM
    elif e == Cell.EDGE_RIGHT:
      x, y, e = x + 1, y, Cell.EDGE_LEFT 
    elif e == Cell.EDGE_BOTTOM:
      x, y, e = x, y + 1, Cell.EDGE_TOP
    else:
      return None, None, None
    if x < 0 or x >= self.width or y < 0 or y >= self.heigh:
      return None, None, None
    return x, y, e
 
  def _calc_bfs(self, entrance, exit): #广度优先
    paths = []
    idx, todos, searchs = 0, [(entrance[0], entrance[1], 0)], [[None for y in range(self.heigh)] for x in range(self.width)]
    while idx < len(todos):
      x, y, edge_from = todos[idx]
      idx += 1
      for e in Cell.EDGES:
        if e == edge_from:
          continue
        if self.cells[x][y].is_open(e):
          x1, y1, e1 = self._get_neighbor(x, y, e)
          searchs[x1][y1] = (x, y)
          if (x1, y1) == exit:
            path = [(x1, y1)]
            prev =searchs[x1][y1]
            while prev:
              path = [prev] + path
              if prev == entrance:
                break
              prev = searchs[prev[0]][prev[1]]
            paths.append(path)
          else:
            todos.append((x1, y1, e1))
    return paths
 
  def _calc_dfs(self, entrance, exit): #深度优先
    sys.setrecursionlimit(1000000) #设置递归深度
    def __calc(paths, path, x, y, edge_from = 0):
      def __add_path(path, i):
        path.append((i[0], i[1]))
        if i[0] == exit[0] and i[1] == exit[1]:
          paths.append(path)
        else:
          __calc(paths, path, i[0], i[1], i[2])
      next = []
      for e in Cell.EDGES:
        if e == edge_from:
          continue
        if self.cells[x][y].is_open(e):
          x1, y1, e1 = self._get_neighbor(x, y, e)
          next.append((x1, y1, e1))
      if len(next):
        if len(next) > 1:
          for i in next:
            path1 = [p for p in path]
            __add_path(path1, i)
        else:
          __add_path(path, next[0])
    paths = []
    __calc(paths, [entrance], entrance[0], entrance[1])
    return paths
 
def main():
  m = Maze(80, 50)
 
if __name__ == '__main__':
  main()

运行结果:

1. 求解的过程有两种方式深度优先(DFS)和广度优先(BFS),DFS方式使用了递归,一旦迷宫的行列数过大的时候递归层数太深就会触发python的异常(默认递归深度是1000),所以需要设置递归深度,相比之下BFS性能更好。

2. 生成迷宫的算法效率并不是很高,测试了下,生成一个400x300的大迷宫,需要耗时大概1min的样子。

3. 此随机生成的迷宫所有的节点都是连通的,并且是唯一路径连通,所以生成完成之后,以任何点为起终点都可以。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值