迷路の設計と解決[サンドボックス中に保留]


14

あなたの仕事は、インセプションからこのシーン両方のキャラクターの役割を果たすことです。その中で、コブはアリアドネに課題を与えます:

解決に1分かかる迷路を設計するのに2分あります。

その説明にはいくつかの自由があります。最も重要なことは、この課題は時間ベースではなく、スコアが迷路と迷路ソルバーの有効性に基づいていることです。

簡単で公正なフォーマットに向けて反復する際に、この課題に対する多くの編集をおforびします。

パートI:迷路形式

すべての迷路は正方形です。迷路内のセルは、インデックスがゼロのタプルとして表されますrow column

壁は2つのバイナリ文字列で表されます。1つは水平壁(行間の移動をブロックする)と1つは垂直壁(その逆)用です。上に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プログラムは迷路を作成します。ルールに従ってプレイし、有効な迷路を提供する必要があります(ソリューションが存在し、終わりが始まりの上にないもの)。

入力: 2つの正の整数:

size [random seed]

どこsizeになります[15, 50]。ランダムシードを使用して、一致を再生できるようにすることをお勧めしますが、必須ではありません。

出力:パートIで説明した形式を使用した有効なサイズxサイズ(正方形)の迷路。「有効」とは、解が存在し、開始セルが終了セルと等しくないことを意味します。

特定の迷路の建築家のスコアは

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

そのため、建築家は複雑な迷路に対して報酬を受け取りますが、構築された壁ごとにペナルティを科されます(これはアリアドネの時間制限の代わりになります)。このdist()関数は、壁のない迷路が無限のスコアを獲得しないようにします。迷路の外側の境界は、壁の数に影響しません。

パートIII:ソルバー

ソルバーは、他の建築家によって生成された迷路を解決しようとします。ある種の戦争の霧があります:訪問したセルに隣接する壁のみが含まれます(他のすべては「?」に置き換えられます)

入力:同じ迷路形式、ただし「?」壁が不明である場合、現在の場所の追加行、およびこの場所からの有効な選択肢のカンマ区切りリスト。(これは、迷路解析関数の記述を簡単にするための大きな編集です)

例(左に一歩進んだ後の上記の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|_?_|

出力:有効な選択肢のリストからのタプルの1つ

各ソルバーのスコアは、アーキテクトのスコアの逆です。

パートIV:キングオブザヒル

アーキテクトとソルバーには別々のスコアが与えられるため、2人の勝者がいる可能性があります。

アーキテクトとソルバーの各ペアは、お互いを裏切る多くの機会を持ちます。すべてのテストと対戦相手のスコアが平均化されます。ゴルフ規則のコーディングとは異なり、最高の平均スコアが勝ちます!

私はこれを継続するつもりですが、永久に継続的なテストを保証することはできません!今のところ、勝者は1週間で宣言されるとしましょう。

パートV:送信

  • 私はすべての提出物に対して拒否権を維持します-賢明さは奨励されますが、それが競争や私のコンピューターを破るのではありません!(あなたのコードが何をするのかわからない場合は、おそらく拒否します)
  • アーキテクト/ソルバーペアの名前を考えます。コードを実行方法の説明と一緒に投稿してください。

近日公開:新しい形式用の更新されたPythonテストキット。大きな変更により、言語の送信が許可されました。


10
pythonに制限するのではなく、出場者が作成/読む迷路のフォーマットを定義できませんでしたか?それはおそらくより多くの人々が興味を持つようになるでしょう。
ジオビット

制限する理由は2つありました。1つは、実行中の試合を簡単かつ安全に自動化することです。2つ目は、各言語の読み書きライブラリを必要としないことです。私は誰のpythonを使用することを望んでいない場合、私は、1つまたは両方を...放棄する必要がありますね
wrongu

1
現在、サブプログラムを実行し、stdin / stdoutを介して通信するラッパーを作成しています。このようにして、必要な言語を使用できます。それを許可しますか?
IchBinKeinBaum

絶対に!私は質問形式全体を書き直している最中でした。待つべきですか?
間違った

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は、「完璧な」迷路(ループのない、2つのポイント間で常に1つのパスが常に存在する迷路)を常に作成する、非常にシンプルな迷路生成プログラムです。シード入力に基づいてロジックを作成します。つまり、生成される迷路は、繰り返しパターンのように見えることが多い擬似ランダムであり、同じシードとサイズで同じ迷路が作成されます。

迷路が生成されると、プログラムは、それらの間の最長パスをもたらす開始点と終了点を検索することにより、迷路のスコアを最大化しようとします。これを行うために、すべての開始点を実行し、トレーサーを広げてそこから最も遠い終了点を見つけ、最も長いパスとの組み合わせを選択します。

この後、迷路を描き、壁を数え、迷路の情報を出力します。これは、開始点、終了点、それらの間の距離、壁の数、およびスコアです。また、この情報をサイズ、開始と終了、水平壁および垂直壁について上記のスタイルにフォーマットし、後で使用するために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.