미로 설계 및 해결 [샌드 박스 처리시 보류 중]


14

너의 임무는 Inception 에서이 장면 에서 캐릭터 의 역할을 하는 것 입니다. 그것에서, Cobb는 Ariadne에게 도전을줍니다 :

해결하는 데 1 분이 걸리는 미로를 설계하는 데 2 ​​분이 걸립니다.

그 설명에 일부 자유가 취해질 것입니다. 가장 중요한 점은이 도전이 시간을 기준으로하는 것이 아니라 점수는 미로와 미로 해결의 효과에 기초한 것입니다.

우리는 쉽고 공정한 형식으로 반복하면서이 과제에 대한 많은 수정 사항에 대해 사과드립니다.

1 부 : 미로 형식

모든 미로는 정사각형입니다. 미로의 셀은 인덱스가없는 튜플로 표시됩니다 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

파트 II : 건축가

Architect 프로그램은 미로를 만듭니다. 그것은 규칙에 따라 연주하고 유효한 미로를 제공해야합니다 (솔루션이 존재하고 끝이 시작 위에 있지 않은).

입력 : 두 개의 양의 정수 :

size [random seed]

size있을 것입니다 [15, 50]. 필요하지 않더라도 일치하는 시드를 재생할 수 있도록 무작위 시드를 사용하는 것이 좋습니다.

출력 : 파트 I에 설명 된 형식을 사용하는 유효한 크기 x 크기 (제곱) 미로입니다. "유효"는 솔루션이 존재하고 시작 셀이 종료 셀과 같지 않음을 의미합니다.

주어진 미로에서 건축가의 점수는

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

따라서 건축가는 복잡한 미로에 대해 보상을 받지만, 건축 된 각 벽에 대해 벌칙을받습니다 (이것은 Ariadne의 시간 제한을 대체합니다). 이 dist()기능은 벽이없는 미로가 무한한 점수를 얻지 못하게합니다. 미로의 외부 경계는 벽 수에 영향을 미치지 않습니다.

파트 III : 솔버

Solver는 다른 건축가가 생성 한 미로를 해결하려고 시도합니다. 전쟁의 안개가 있습니다 : 방문한 세포에 인접한 벽 만 포함됩니다 (다른 모든 것은 '?'로 대체됩니다)

입력 : 동일한 미로 형식이지만 '?' 벽을 알 수없는 곳, 현재 위치에 대한 추가 선,이 위치에서 쉼표로 구분 된 유효한 선택 목록. (이것은 미로 파싱 기능을 작성하는 것을 더 간단하게하기위한 큰 편집입니다)

예 (한 걸음 남은 후 위의 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|_?_|

출력 : 유효한 선택 목록에서 튜플 중 하나

각 솔버 점수는 건축가 점수의 역수입니다.

IV 부 : 언덕의 왕

건축가와 솔버에게는 별도의 점수가 부여되므로 두 명의 승자가있을 수 있습니다.

각 건축가와 솔버 쌍은 서로를 이길 수있는 많은 기회를 가질 것입니다. 점수는 모든 테스트와 상대에 대한 평균입니다. 코드 골프 컨벤션과 달리 최고 평균 점수가 승리합니다!

나는 이것이 계속되기를 원하지만, 지속적인 테스트를 영원히 보장 할 수는 없습니다! 당첨자가 1 주일 내에 선언된다고 가정 해 보겠습니다.

파트 V : 제출

  • 모든 제출물에 대해 거부권을 행사합니다. 영리함이 권장되지만 경쟁이나 내 컴퓨터를 어기는 경우에는 아닙니다. (코드가 무엇인지 알 수 없다면 거부 할 것입니다)
  • Architect / Solver 쌍의 이름을 제시하십시오. 코드 실행 방법에 대한 지침과 함께 코드를 게시하십시오.

출시 예정 : 새로운 형식의 업데이트 된 Python 테스트 키트. 모든 언어 제출을 허용하기 위해 큰 변화가 일어났습니다.


10
파이썬으로 제한하지 않고 참가자가 작성 / 읽을 미로 형식을 정의 할 수 없습니까? 아마 더 많은 사람들이 관심을 가질 것입니다.
Geobits

제한적인 두 가지 이유가있었습니다. 첫 번째는 달리기 경기를 쉽고 안전하게 자동화하는 것입니다. 두 번째는 각 언어마다 읽기 및 쓰기 라이브러리가 필요하지 않도록하는 것입니다. 아무도 파이썬을 사용하고 싶지 않다면 하나 또는 둘 다를 포기해야 할 것 같습니다.
wrongu

1
현재 서브 프로그램을 실행하고 stdin / stdout을 통해 통신하는 래퍼를 작성 중입니다. 이 방법으로 원하는 언어를 사용할 수 있습니다. 허락하겠습니까?
IchBinKeinBaum

물론! 나는 전체 질문 형식을 다시 쓰는 중이었다. 기다릴까요?
wrongu

1
그게 뭔지 몰랐어요 지금 당장 보류 할 것 같아 ..
wrongu

답변:


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

솔버 인 SolveFun은 텍스트 파일 Maze.txt를 입력으로 사용하며 설계자와 매우 유사한 방식으로 작동합니다. 모든 움직임에 대해 상대 위치를 기준으로 끝까지 가고 싶은 방향을 선택한 다음 주변 벽을 봅니다. 벽이 없으면 벽에 인접한 셀에 있는지 확인하고 그렇지 않은 경우 가능한 옵션으로 추가됩니다. 그런 다음 옵션이있는 경우 원하는 방향과 가장 가까운 방향으로 이동합니다. 옵션이없는 경우 옵션이 없을 때까지 역 추적합니다. 이것은 끝에 도달 할 때까지 계속됩니다.

이동함에 따라 변수 경로에 걸리는 경로를 기록하여 마지막 단계에서 총 단계 수를 출력합니다. 또한 마지막 단계에서 낭비 된 단계를 계산하는 데 사용 된 역 추적 시간도 기록합니다. 끝에 도달하면 *s 로 표시된 처음부터 끝까지 가장 짧은 경로로 미로를 출력합니다 .

실행하는 방법

미로를 출력하는 방법 (특정 문자 밑줄 포함)으로 인해 명령 행에서 다음 형식으로 실행해야합니다.

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

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

여기서 Size는 15와 50 사이의 정수이고, Seed는 4와 Size 사이의 정수입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.