Розробіть і вирішіть лабіринт [утримуйте під час пісочниці]


14

Ваше завдання - зіграти ролі обох персонажів у цій сцені з самого початку. У цьому Кобб дає завдання Аріадне:

У вас є дві хвилини, щоб розробити лабіринт, на вирішення якого потрібна одна хвилина.

Деякі свободи будуть враховані в цьому описі. Найголовніше, що це завдання не засноване на часі, а результати базуються на ефективності ваших лабіринтів та лабіринтів.

Прошу вибачення за багато змін у цьому виклику, оскільки ми повторюємо простий та чесний формат.

Частина I: Формат лабіринту

Усі лабіринти квадратні. Клітина в лабіринті представлена ​​як нульовий індекс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: Архітектор

Програма Архітектор створює лабіринт. Він повинен грати за правилами і надавати дійсний лабіринт (той, де є рішення, а кінець не знаходиться на початку старту).

Введення: Два натуральних числа:

size [random seed]

Де sizeбуде в [15, 50]. Вам пропонується скористатися випадковим насінням, щоб матчі могли бути повтореними, хоча це не потрібно.

Вихід: дійсний лабіринт розміру x (квадрат) з використанням формату, описаного в частині I. "дійсний" означає, що рішення існує, а початкова комірка не дорівнює кінцевій комірці.

Оцінка архітектора на заданому лабіринті є

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

Тож архітекторів винагороджують за складні лабіринти, але штрафують за кожну побудовану стіну (це замінює обмеження часу Аріадни). Thedist() функції гарантує , що лабіринт без стін не отримати нескінченний рахунок. Зовнішні межі лабіринту не сприяють кількості стін.

Частина III: Розв’язувач

Solver намагається вирішити лабіринти, породжені іншими архітекторами. Існує свого роду туман війни: включені лише стіни, що прилягають до відвідуваних осередків (всі інші замінюються на "?")

вхід: той самий формат лабіринту, але з "?" де стіни невідомі, додатковий рядок для поточного місця розташування та розділений комою список дійсних варіантів цього місця. (Це велика редакція, яка покликана спростити написання функції лабіринту)

приклад (те саме, що вищезазначений лабіринт 5х5 після того, як зробили один крок зліва)

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|_?_|

вихід: один із кортежів зі списку дійсних варіантів

Кожен бал Solver є зворотним балом архітектора.

Частина IV: Король гори

Архітекторам та рішальникам даються окремі бали, тому потенційно можуть бути два переможці.

Кожна пара архітекторів і вирішувачів матиме багато шансів перехитрити один одного. Оцінки будуть усереднені за всі тести та супротивники. На противагу кодовим умовам про гольф, найвищий середній бал виграє!

Я маю намір це продовжувати, але не можу гарантувати продовження тестування назавжди! Скажімо зараз, що переможця буде оголошено через тиждень.

Частина V: Подання

  • Я підтримую право вето над усіма поданнями - розумність заохочується, але не, якщо вона порушує конкуренцію чи мій комп'ютер! (Якщо я не можу сказати, що робить ваш код, я, мабуть, накладу вето)
  • Придумайте назву для вашої пари Архітектор / Сольвер. Опублікуйте свій код разом із інструкціями щодо його запуску.

Незабаром: оновлений тестовий комплект python для нового формату. Великі зміни відбулися, щоб дозволити будь-які подання мови.


10
Замість того, щоб обмежити його python, ви не могли б визначити формат лабіринту, який повинні створювати / читати учасники? Це, мабуть, зацікавить більше людей.
Геобіць

У мене було дві причини бути обмежуючими: по-перше, це легко і безпечно автоматизувати ведення матчів. Друге - уникати необхідності використання бібліотеки для читання та письма для кожної мови. Я думаю, якщо ніхто не хоче використовувати python, мені доведеться відмовитись від одного або обох ...
неправильно

1
В даний час я пишу обгортку, яка запускає підпрограму і спілкується по stdin / stdout. Таким чином ви можете використовувати будь-яку мову, яку ви хочете. Ви дозволите це?
IchBinKeinBaum

Абсолютно! Я був у середині переписання всього формату запитань. Чи варто чекати?
неправильно

1
Я не знав, що це річ. Напевно, я зараз затримаю її ..
неправильно

Відповіді:


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

Solver, SolveFun, використовує текстовий файл Maze.txt як вхідний і працює дуже схожим на архітектора. Для кожного ходу він вибиратиме напрямок, який хоче пройти, виходячи з його відносного положення до кінця, і тоді він буде дивитися на стіни, що його оточують. Якщо стіни там немає, вона перевірить, чи була вона у сусідній до неї комірці, а якщо ні, то буде додано як можливий варіант. Потім він рухатиметься у напрямку, найближчому до бажаного напрямку, за умови, що у нього є варіанти. Якщо у нього немає варіантів, він буде відслідковуватись, поки не з'явиться. Це триває, поки не досягне кінця.

По мірі руху він записує шлях, який він проходить в змінному шляху, який використовується в кінці для виведення загальної кількості кроків. Він також записує кількість разів, яку потрібно було відхилити, щоб використати для обчислення витрачених кроків наприкінці. Коли він досягне кінця, він виведе лабіринт найкоротшим шляхом від початку до кінця, позначеним на *s.

Як бігати

Завдяки способу виведення лабіринту (який включає підкреслення певних символів), це потрібно запустити з командного рядка у формі

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

і

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

де розмір є цілим числом від 15 до 50 (включно), а насіння - ціле число між 4 і розміром (включно).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.