设计和解决迷宫[在沙箱中暂停]


14

你的任务是扮演的角色在角色这一幕从成立。在其中,Cobb给Ariadne带来了挑战:

您有两分钟的时间设计一个迷宫,需要一分钟的时间来解决。

该描述将有一些自由。最重要的是,这种挑战不是基于时间的,而是基于迷宫和迷宫求解器的效率来得出分数。

对于此挑战的许多修改,我们深表歉意,因为我们正朝着一种简单而公平的格式过渡。

第一部分:迷宫格式

所有的迷宫都是正方形。迷宫中的一个单元表示为零索引元组row column

墙由两个二进制字符串表示:一个用于水平墙(阻止行之间的移动)和用于垂直墙(反之亦然)。在NxN迷宫中,Nx(N-1)每种类型的墙壁都有可能。让我们以一个3x3的示例为例,其中标记了单元格:

A   B | C
   ---
D | E   F
   ---
G   H | I

所有可能的垂直墙为:AB BC DE EF GH HI。转换为字符串,显示的墙壁011001用于垂直墙壁和010010水平墙壁。另外,“二进制字符串”是指“字符'0'和'1'”。

完整的迷宫格式是一个字符串,其中包含以下顺序:

  • 宽度
  • 开始元组
  • 末端细胞元组
  • 水平墙
  • 垂直墙

例如,这个迷宫:

   0 1 2 3 4
   _________
0 | |  E|  _|
1 |  _|_|_  |
2 |_ _ _  | |
3 |  _ _  | |
4 |____S|___|
start:(4,2)
end:(0,2)

格式化为:

5
4 2
0 2
00001011101110001100
10100110000100010010

第二部分:建筑师

建筑师程序创建迷宫。它必须遵守规则并提供有效的迷宫(一种存在解的迷宫,且末尾不在开头)。

输入:两个正整数:

size [random seed]

size将会在哪里[15, 50]。鼓励您使用随机种子,以便重播比赛,尽管这不是必需的。

输出:使用第I部分中描述的格式的有效大小x大小(正方形)迷宫。“有效”表示存在解决方案,并且起始像元不等于结束像元。

建筑师在给定迷宫上的得分为

   # steps taken to solve
–––––––––––––––––––––––––––––
max(dist(start,end),(# walls))

因此,建筑师会因复杂的迷宫而获得奖励,但会对建造的每一堵墙都受到惩罚(这是Ariadne的时间限制的替代品)。该dist()功能确保没有墙壁的迷宫不会得到无限的分数。迷宫的外部边界不会增加墙的数量。

第三部分:求解器

规划求解试图解决其他设计师产生的迷宫。有一种战争迷雾:只包括与拜访的牢房相邻的墙(所有其他墙都替换为“?”)

输入:相同的迷宫格式,但带有“?” 墙壁未知的地方,当前位置的额外行以及此位置的有效选择的逗号分隔列表。(这是一个很大的编辑,旨在使编写迷宫解析函数更简单)

示例(与上述5x5迷宫相同,只差了一步)

5
4 2
0 2
???????????????011??
????????????????001?
4 1
4 0,4 2

对应这样的东西,?雾在哪里:

   0 1 2 3 4
   _________
0 |????E????|
1 |?????????|
2 |?????????|
3 | ?_?_????|
4 |__C_S|_?_|

输出:有效选择列表中的元组之一

每个规划求解的分数是建筑师分数的倒数。

第四部分:山丘之王

建筑师和求解器分别获得分数,因此可能会有两个获胜者。

每对架构师和求解器将有很多机会彼此取胜。分数将在所有测试和对手中平均。与高尔夫惯例相反,最高平均分获胜!

我打算继续进行此操作,但不能保证永远继续进行测试!现在让我们说一个星期后将宣布一个赢家。

第五部分:提交

  • 我对所有提交的作品都拥有否决权-鼓励聪明,但如果它破坏了比赛或我的电脑,则不行!(如果我不知道您的代码做什么,我可能会否决它)
  • 为您的Architect / Solver对命名。发布您的代码以及有关如何运行它的说明。

即将推出:针对新格式的更新的python测试工具包。发生了很大的变化,允许任何语言提交。


10
除了不能将其限制为python之外,您不能定义参赛者创建/读取的迷宫格式吗?那可能会使更多的人感兴趣。
Geobits,2014年

我有两个限制的理由:首先是轻松安全地自动运行比赛。第二是避免需要每种语言的读写库。我想如果没有人想要使用python,我将不得不放弃一个或两个……

1
我目前正在编写一个运行子程序并通过stdin / stdout进行通信的包装器。这样,您可以使用所需的任何语言。你可以允许吗?
IchBinKeinBaum 2014年

绝对!我正在重写整个问题格式。我应该等吗?
错误

1
我不知道那是一回事。我想我暂时将其搁置
。–错

Answers:


1

BuildFun和SolveFun

好吧,这花了相当长的时间,而且我不能完全确定求解器是否在作弊。虽然它始终可以访问整个迷宫,但它只会查看它所在的单元格,周围的墙以及(如果它们之间没有墙)相邻的单元格。如果这违反规定,请告诉我,我将尝试进行更改。

无论如何,这是代码:

#Architect function
def BuildFun(size,seed):
   #Initialise grid and ensure inputs are valid
   if size<15:size=15
   if size>50:size=50
   if seed<4:seed=4
   if seed>size:seed=size
   grid=[]
   for x in range(size):
      gridbuilder=[]
      for y in range(size):gridbuilder.append([0,1,1])
      grid.append(gridbuilder)
   coords=[0,0]
   grid[0][0][0]=1
   #Generate maze
   while 1:
      #Choose a preffered direction based on location in grid and seed
      pref=((((coords[0]+coords[1]+2)*int(size/2))%seed)+(seed%(abs(coords[0]-coords[1])+1)))%4
      #Find legal moves
      opt=[]
      if coords[0]>0:opt+=[0] if grid[coords[0]-1][coords[1]][0]==0 else []
      if coords[1]<size-1:opt+=[1] if grid[coords[0]][coords[1]+1][0]==0 else []
      if coords[0]<size-1:opt+=[2] if grid[coords[0]+1][coords[1]][0]==0 else []
      if coords[1]>0:opt+=[3] if grid[coords[0]][coords[1]-1][0]==0 else []
      #There are legal moves
      if len(opt)>0:
         moved=False
         while not moved:
            #Try to move in preffered direction
            if pref in opt:
               if pref==0:
                  coords[0]-=1
                  grid[coords[0]][coords[1]][0]=1
                  grid[coords[0]][coords[1]][2]=0
               elif pref==1:
                  grid[coords[0]][coords[1]][1]=0
                  coords[1]+=1
                  grid[coords[0]][coords[1]][0]=1
               elif pref==2:
                  grid[coords[0]][coords[1]][2]=0
                  coords[0]+=1
                  grid[coords[0]][coords[1]][0]=1
               else:
                  coords[1]-=1
                  grid[coords[0]][coords[1]][0]=1
                  grid[coords[0]][coords[1]][1]=0
               moved=True
            #Change preferred direction if unable to move
            else:
               pref+=1
               if pref==4:pref=0
      #There aren't legal moves
      else:
         moved=False
         #Return to a previously visited location
         if not moved:
            try:
               if grid[coords[0]-1][coords[1]][0]==1 and grid[coords[0]-1][coords[1]][2]==0:
                  grid[coords[0]][coords[1]][0]=2
                  coords[0]-=1
                  moved=True
            except:pass
         if not moved:
            try:
               if grid[coords[0]][coords[1]+1][0]==1 and grid[coords[0]][coords[1]][1]==0:
                  grid[coords[0]][coords[1]][0]=2
                  coords[1]+=1
                  moved=True
            except:pass
         if not moved:
            try:
               if grid[coords[0]+1][coords[1]][0]==1 and grid[coords[0]][coords[1]][2]==0:
                  grid[coords[0]][coords[1]][0]=2
                  coords[0]+=1
                  moved=True
            except:pass
         if not moved:
            try:
               if grid[coords[0]][coords[1]-1][0]==1 and grid[coords[0]][coords[1]-1][1]==0:
                  grid[coords[0]][coords[1]][0]=2
                  coords[1]-=1
                  moved=True
            except:pass
      #Check if finished
      fin=True
      for x in grid:
         for y in x:
            if y[0]==0:
               fin=False
               break
         if not fin:break
      if fin:break
   for x in grid:
      for y in x:
         y[0]=0
   #Find positions for start and finish such that the route between them is as long as possible
   lsf=[[0,0],[0,0],0]
   for y in range(size):
      for x in range(size):
         #Check all start positions
         lengths=[]
         coords=[[y,x,4,0]]
         while len(coords)>0:
            #Spread tracers out from start to the rest of the maze
            for coord in coords:
               opt=[]
               if coord[0]>0:opt+=[0] if grid[coord[0]-1][coord[1]][2]==0 else []
               opt+=[1] if grid[coord[0]][coord[1]][1]==0 else []
               opt+=[2] if grid[coord[0]][coord[1]][2]==0 else []
               if coord[1]>0:opt+=[3] if grid[coord[0]][coord[1]-1][1]==0 else []
               try:opt.remove(coord[2])
               except:pass
               #Dead end, tracer dies and possible end point is recorded along with length
               if len(opt)==0:
                  lengths.append([coord[0],coord[1],coord[3]])
                  coords.remove(coord)
               else:
                  #Create more tracers at branch points
                  while len(opt)>1:
                     if opt[0]==0:coords.append([coord[0]-1,coord[1],2,coord[3]+1])
                     elif opt[0]==1:coords.append([coord[0],coord[1]+1,3,coord[3]+1])
                     elif opt[0]==2:coords.append([coord[0]+1,coord[1],0,coord[3]+1])
                     else:coords.append([coord[0],coord[1]-1,1,coord[3]+1])
                     del opt[0]
                  if opt[0]==0:
                     coord[0]-=1
                     coord[2]=2
                     coord[3]+=1
                  elif opt[0]==1:
                     coord[1]+=1
                     coord[2]=3
                     coord[3]+=1
                  elif opt[0]==2:
                     coord[0]+=1
                     coord[2]=0
                     coord[3]+=1
                  else:
                     coord[1]-=1
                     coord[2]=1
                     coord[3]+=1
         #Find furthest distance and, if it's longer than the previous one, the start/end positions get updated
         lengths=sorted(lengths,key=lambda x:x[2],reverse=True)
         if lengths[0][2]>lsf[2]:lsf=[[y,x],[lengths[0][0],lengths[0][1]],lengths[0][2]]
   #Find number of walls and output maze
   w=draw(grid,size,lsf[0],lsf[1])
   #Output maze information
   print('Start = '+str(lsf[0]))
   print('End = '+str(lsf[1]))
   print('Distance = '+str(lsf[2]))
   print('Walls = '+str(w))
   print('Score = '+str(float(lsf[2])/float(w))[:5])
   #Convert array grid to binary strings horizontal and vertical
   horizontal=vertical=''
   for y in range(size):
      for x in range(size-1):vertical+=str(grid[y][x][1])
   for y in range(size-1):
      for x in range(size):horizontal+=str(grid[y][x][2])
   #Save maze information to text file for use with SolveFun
   save=open('Maze.txt','w')
   save.write(str(size)+'\n'+str(lsf[0][0])+' '+str(lsf[0][1])+'\n'+str(lsf[1][0])+' '+str(lsf[1][1])+'\n'+horizontal+'\n'+vertical)
   save.close()
#Solver function
def SolveFun():
   try:
      #Get maze information from text file
      save=open('Maze.txt','r')
      data=save.readlines()
      save.close()
      size=int(data[0])
      s=data[1].rsplit(' ')
      start=[int(s[0]),int(s[1])]
      e=data[2].rsplit(' ')
      end=[int(e[0]),int(e[1])]
      horizontal=data[3].rstrip('\n')
      vertical=data[4]
      #Build maze from information
      grid=[]
      for y in range(size):
         grid.append([])
         for x in range(size):
            grid[y].append([0,1,1])
      for y in range(size):
         for x in range(size-1):
            grid[y][x][1]=int(vertical[y*(size-1)+x])
      for y in range(size-1):
          for x in range(size):
            grid[y][x][2]=int(horizontal[y*size+x])
      path=''
      cpath=''
      bs=0
      pos=start[:]
      grid[pos[0]][pos[1]][0]=1
      while pos!=end:
         #Want to move in direction of finish
         if end[0]<pos[0] and pos[0]-end[0]>=abs(pos[1]-end[1]):pref=0
         elif end[1]>pos[1] and end[1]-pos[1]>=abs(pos[0]-end[0]):pref=1
         elif end[0]>pos[0] and end[0]-pos[0]>=abs(pos[1]-end[1]):pref=2
         else:pref=3
         #Find legal moves
         opt=[]
         if pos[0]>0:
            if grid[pos[0]-1][pos[1]][2]==0:opt+=[0]if grid[pos[0]-1][pos[1]][0]==0 else[]
         if pos[1]>0:
            if grid[pos[0]][pos[1]-1][1]==0:opt+=[3]if grid[pos[0]][pos[1]-1][0]==0 else[]
         if grid[pos[0]][pos[1]][2]==0:opt+=[2]if grid[pos[0]+1][pos[1]][0]==0 else[]
         if grid[pos[0]][pos[1]][1]==0:opt+=[1]if grid[pos[0]][pos[1]+1][0]==0 else[]
         if len(opt)>0:
            moved=False
            while not moved:
               #Try to move in preferred direction
               if pref in opt:
                  if pref==0:
                     pos[0]-=1
                     path+='0'
                     cpath+='0'
                  elif pref==1:
                     pos[1]+=1
                     path+='1'
                     cpath+='1'
                  elif pref==2:
                     pos[0]+=1
                     path+='2'
                     cpath+='2'
                  else:
                     pos[1]-=1
                     path+='3'
                     cpath+='3'
                  grid[pos[0]][pos[1]][0]=1
                  moved=True
               #Change preferred direction by 1
               else:
                  pref=(pref+1)%4
         #No legal moves, backtrack
         else:
            bs+=1
            grid[pos[0]][pos[1]][0]=2
            if int(cpath[len(cpath)-1])==0:
               pos[0]+=1
               path+='2'
            elif int(cpath[len(cpath)-1])==1:
               pos[1]-=1
               path+='3'
            elif int(cpath[len(cpath)-1])==2:
               pos[0]-=1
               path+='0'
            else:
               pos[1]+=1
               path+='1'
            cpath=cpath[:len(cpath)-1]
      #Output maze with solution as well as total steps and wasted steps
      draw(grid,size,start,end)
      print('\nPath taken:')
      print(str(len(path))+' steps')
      print(str(bs)+' backsteps')
      print(str(bs*2)+' wasted steps')
   except:print('Could not find maze')
def draw(grid,size,start,end):
   #Build output in string d
   d='   '
   for x in range(size):d+=' '+str(x)[0]
   d+='\n   '
   for x in range(size):d+='  ' if len(str(x))==1 else ' '+str(x)[1]
   d+='\n    '+'_'*(size*2-1)
   w=0
   for y in range(size):
      d+='\n'+str(y)+'  |' if len(str(y))==1 else '\n'+str(y)+' |'
      for x in range(size):
         if grid[y][x][2]:
            if start==[y,x]:d+=UL.S+'S'+UL.E
            elif end==[y,x]:d+=UL.S+'F'+UL.E
            elif grid[y][x][0]==1:d+=UL.S+'*'+UL.E
            else:d+='_'
            w+=1
         else:
            if start==[y,x]:d+='S'
            elif end==[y,x]:d+='F'
            elif grid[y][x][0]==1:d+='*'
            else:d+=' '
         if grid[y][x][1]:
            d+='|'
            w+=1
         else:d+=' '
   #Output maze and return number of walls
   print(d)
   w-=size*2
   return w
#Underlines text
class UL:
   S = '\033[4m'
   E = '\033[0m'

我意识到这太长了,而且阅读起来并不容易,但是我很懒,所以这就是事实。

BuildFun

建筑师BuildFun是一个相当简单的迷宫生成程序,它将始终创建一个“完美的”迷宫(一个没有循环,并且任何两个点之间始终只有一条路径)。它基于种子输入的逻辑,这意味着生成的迷宫是伪随机的,通常看起来是重复的模式,并且具有相同的种子和大小,将创建相同的迷宫。

迷宫生成后,程序将尝试搜索导致两者之间最长路径的起点和终点来最大化迷宫的分数。为此,它将遍历每个起点,展开示踪剂以找到距离终点最远的终点,然后选择路径最长的组合。

之后,它绘制迷宫,计数墙壁并输出迷宫的信息。这是起点,终点,它们之间的距离,墙数和分数。它还将该信息格式化为上述尺寸,开始和结束,水平墙和垂直墙的样式,并将其保存到名为Maze.txt的文本文件中,以备后用。

解决乐趣

求解器SolveFun使用文本文件Maze.txt作为输入,并以与架构师非常相似的方式工作。对于每一步,它都会根据其相对于末端的相对位置来选择要走的方向,然后再看一下它周围的墙壁。如果墙壁不存在,它将检查它是否在与其相邻的单元中,如果没有,它将作为可能的选项添加。如果有选项,它将沿最接近其首选方向的方向移动。如果没有选项,它将回溯直到有选项为止。这一直持续到结束为止。

在移动时,它会在可变路径中记录它所走的路径,该路径最后用于输出步骤总数。最后,它还记录了为计算浪费的步骤而必须回溯的时间。当到达终点时,它将输出迷宫,迷宫的起点和终点之间的路径最短,并标有*s。

怎么跑

由于输出迷宫的方法(包括在某些字符下划线),必须从命令行以以下格式运行

python -c 'import filename;filename.BuildFun(Size, Seed)'

python -c 'import filename;filename.SolveFun()'

其中,Size是介于15和50之间(包括端点)的整数,而Seed是介于4和Size之间(包括端点)的整数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.