Thiết kế và giải quyết một mê cung [giữ trong khi hộp cát]


14

Nhiệm vụ của bạn là đóng vai trò của cả hai nhân vật trong cảnh này từ Inception. Trong đó, Cobb đưa ra một thách thức cho Ariadne:

Bạn có hai phút để thiết kế một mê cung mất một phút để giải quyết.

Một số tự do sẽ được thực hiện trên mô tả đó. Quan trọng nhất, thử thách này không dựa trên thời gian, thay vào đó, điểm số được dựa trên hiệu quả của mê cung và người giải quyết mê cung của bạn.

Tôi xin lỗi vì đã chỉnh sửa nhiều thử thách này khi chúng tôi lặp lại theo một định dạng dễ dàng và công bằng ..

Phần I: Định dạng mê cung

Tất cả các mê cung là hình vuông. Một tế bào trong mê cung được biểu diễn dưới dạng một tuple không có chỉ số row column.

Tường được đại diện bởi hai chuỗi nhị phân: một cho tường ngang (chặn chuyển động giữa các hàng) và tường dọc (ngược lại). Trên một NxNmê cung, có những Nx(N-1)bức tường có thể của từng loại. Hãy lấy một ví dụ 3x3 trong đó các ô được dán nhãn:

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

tất cả các bức tường thẳng đứng có thể là : AB BC DE EF GH HI. Được dịch thành một chuỗi, các bức tường được hiển thị là 011001cho các bức tường thẳng đứng và 010010cho các bức tường ngang. Ngoài ra, theo "chuỗi nhị phân", ý tôi là "các ký tự '0' và '1'".

Định dạng mê cung đầy đủ là một chuỗi chứa, theo thứ tự này:

  • chiều rộng
  • bắt đầu tế bào
  • tế bào cuối
  • tường ngang
  • tường dọc

Ví dụ, mê cung này:

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

được định dạng này:

5
4 2
0 2
00001011101110001100
10100110000100010010

Phần II: Kiến trúc sư

Chương trình Kiến trúc sư tạo ra mê cung. Nó phải chơi theo luật và cung cấp một mê cung hợp lệ (một giải pháp tồn tại và kết thúc không nằm trên đỉnh của sự bắt đầu).

Đầu vào: Hai số nguyên dương:

size [random seed]

Nơi sizesẽ ở [15, 50]. Bạn được khuyến khích sử dụng hạt giống ngẫu nhiên để các trận đấu có thể được phát lại, mặc dù không bắt buộc.

Đầu ra: Mê cung kích thước x kích thước (vuông) hợp lệ sử dụng định dạng được mô tả trong Phần I. "hợp lệ" có nghĩa là một giải pháp tồn tại và ô bắt đầu không bằng ô kết thúc.

Điểm của Kiến trúc sư trên một mê cung nhất định là

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

Vì vậy, các kiến ​​trúc sư được khen thưởng cho các mê cung phức tạp, nhưng bị phạt cho mỗi bức tường được xây dựng (đây là một thay thế cho hạn chế thời gian của Ariadne). Các dist()chức năng đảm bảo rằng một mê cung không có bức tường không có được một điểm vô hạn. Các đường viền bên ngoài của mê cung không đóng góp vào số lượng tường.

Phần III: Bộ giải

Bộ giải cố gắng giải quyết các mê cung được tạo ra bởi các kiến ​​trúc sư của người khác. Có một loại sương mù chiến tranh: chỉ bao gồm các bức tường liền kề với các ô được truy cập (tất cả các bức tường khác được thay thế bằng '?')

đầu vào: cùng định dạng mê cung, nhưng với '?' nơi các bức tường không xác định, một dòng bổ sung cho vị trí hiện tại và danh sách các lựa chọn hợp lệ được phân tách bằng dấu phẩy từ vị trí này. (Đây là một chỉnh sửa lớn có nghĩa là làm cho nó đơn giản hơn để viết một chức năng phân tích mê cung)

ví dụ (giống như mê cung 5x5 ở trên sau khi thực hiện một bước còn lại)

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

Tương ứng với cái gì đó như thế này, ?sương mù ở đâu :

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

đầu ra: Một trong các bộ dữ liệu từ danh sách các lựa chọn hợp lệ

Mỗi điểm của Bộ giải là tỷ lệ nghịch với điểm của Kiến trúc sư.

Phần IV: Vua đồi

Kiến trúc sư và Người giải quyết được cho điểm riêng biệt, vì vậy có khả năng có thể có hai người chiến thắng.

Mỗi cặp kiến ​​trúc sư và người giải quyết sẽ có nhiều cơ hội để đánh lừa lẫn nhau. Điểm số sẽ được tính trung bình trên tất cả các bài kiểm tra và đối thủ. Trái với quy ước golf mã, điểm trung bình cao nhất sẽ thắng!

Tôi dự định điều này sẽ tiếp diễn, nhưng tôi không thể đảm bảo sẽ tiếp tục thử nghiệm mãi mãi! Bây giờ hãy nói rằng một người chiến thắng sẽ được tuyên bố trong một tuần.

Phần V: Đệ trình

  • Tôi duy trì quyền phủ quyết đối với tất cả các bài nộp - sự thông minh được khuyến khích, nhưng không phải nếu nó phá vỡ sự cạnh tranh hoặc máy tính của tôi! (Nếu tôi không thể biết mã của bạn làm gì, tôi có thể sẽ phủ quyết nó)
  • Hãy đặt tên cho cặp Architect / Solver của bạn. Gửi mã của bạn cùng với hướng dẫn về cách chạy nó.

Sắp có: một bộ kiểm tra python cập nhật cho định dạng mới. Những thay đổi lớn đã xảy ra để cho phép bất kỳ đệ trình ngôn ngữ.


10
Thay vì giới hạn nó với python, bạn không thể xác định định dạng mê cung sẽ được tạo / đọc bởi các thí sinh? Điều đó có thể sẽ nhận được nhiều người quan tâm.
Geobits

Tôi có hai lý do để hạn chế: đầu tiên là tự động hóa một cách dễ dàng và an toàn các trận đấu đang chạy. Thứ hai là để tránh yêu cầu một thư viện đọc và viết cho mỗi ngôn ngữ. Tôi đoán nếu không ai muốn sử dụng python, tôi sẽ phải từ bỏ một hoặc cả hai ...
sai

1
Tôi hiện đang viết một trình bao bọc chạy chương trình phụ và giao tiếp qua stdin / stdout. Bằng cách này bạn có thể sử dụng bất kỳ ngôn ngữ nào bạn muốn. Bạn có cho phép điều đó?
IchBinKeinBaum

Chắc chắn rồi! Tôi đang ở giữa viết lại toàn bộ định dạng câu hỏi. Tôi có nên chờ đợi không?
sai

1
Tôi không biết đó là một điều. Tôi đoán bây giờ tôi sẽ giữ nó ..
sai

Câu trả lời:


1

BuildFun và SolveFun

Chà, điều này mất khá nhiều thời gian và tôi không hoàn toàn chắc chắn liệu người giải có gian lận hay không. Mặc dù nó có quyền truy cập vào toàn bộ mê cung mọi lúc, nó chỉ nhìn vào tế bào trong đó, các bức tường bao quanh nó và, nếu không có một bức tường giữa chúng, các tế bào nằm cạnh nó. Nếu điều này trái với quy tắc, xin vui lòng cho tôi biết và tôi sẽ cố gắng thay đổi nó.

Dù sao, đây là mã:

#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'

Tôi nhận ra rằng nó dài vô lý và không dễ đọc, nhưng tôi lười nên đây là cách nó tồn tại.

BuildFun

Kiến trúc sư, BuildFun, là một chương trình tạo mê cung khá đơn giản, sẽ luôn tạo ra một mê cung 'hoàn hảo' (một không có vòng lặp và trong đó hai điểm sẽ luôn có chính xác một đường giữa chúng). Nó dựa vào logic của nó từ đầu vào hạt giống, nghĩa là các mê cung được tạo ra là giả ngẫu nhiên với những gì thường xuất hiện là các mẫu lặp lại và, với cùng một hạt giống và kích thước, cùng một mê cung sẽ được tạo ra.

Khi mê cung được tạo, chương trình sẽ cố gắng tối đa hóa điểm số của mê cung bằng cách tìm kiếm điểm bắt đầu và điểm kết thúc dẫn đến con đường dài nhất giữa chúng. Để làm điều này, nó chạy qua mọi điểm bắt đầu, trải ra các bộ theo dõi để tìm điểm cuối xa nhất từ ​​nó và chọn kết hợp với con đường dài nhất.

Sau đó, nó vẽ mê cung, đếm các bức tường và đưa ra thông tin của mê cung. Đây là điểm bắt đầu, điểm kết thúc, khoảng cách giữa chúng, số lượng tường và điểm số. Nó cũng định dạng thông tin này thành kiểu được mô tả ở trên về kích thước, bắt đầu và kết thúc, tường ngang và tường dọc và lưu thông tin này vào tệp văn bản có tên Maze.txt để sử dụng sau.

Giải quyết

Bộ giải, SolveFun, sử dụng tệp văn bản Maze.txt làm đầu vào và hoạt động theo cách rất giống với kiến ​​trúc sư. Đối với mỗi di chuyển, nó sẽ chọn một hướng mà nó muốn đi dựa trên vị trí tương đối của nó đến cuối và sau đó nó sẽ nhìn vào các bức tường xung quanh nó. Nếu một bức tường không có ở đó, nó sẽ kiểm tra xem nó đã ở trong ô liền kề với nó chưa, và nếu không, nó sẽ được thêm vào tùy chọn có thể. Sau đó, nó sẽ di chuyển theo hướng gần nhất với hướng ưa thích với điều kiện nó có các tùy chọn. Nếu nó không có tùy chọn, nó sẽ quay lại cho đến khi có. Điều này tiếp tục cho đến khi nó kết thúc.

Khi nó di chuyển, nó ghi lại đường dẫn mà nó đang thực hiện trong đường dẫn biến được sử dụng ở cuối để xuất tổng số bước. Nó cũng ghi lại số lần phải quay lại được sử dụng để tính các bước lãng phí ở cuối. Khi đến điểm cuối, nó sẽ xuất ra mê cung với con đường ngắn nhất từ ​​đầu đến cuối được đánh dấu bằng *s.

Cách chạy

Do phương thức xuất ra mê cung (bao gồm gạch chân một số ký tự nhất định), điều này phải được chạy từ một dòng lệnh trong biểu mẫu

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

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

trong đó Kích thước là một số nguyên từ 15 đến 50 (đã bao gồm) và Seed là một số nguyên giữa 4 và Kích thước (đã bao gồm).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.