Подтвердить что ты не робот

Code Golf: Вращающийся лабиринт

Код Гольф: Вращающийся лабиринт


Сделайте программу, которая берет файл, состоящий из лабиринта. Лабиринт имеет стены, обозначенные #. Лабиринт должен включать единственный шар, заданный a o и любое количество отверстий, заданных a @. Файл лабиринта может быть введен через командную строку или считаться строкой через стандартный ввод. Укажите, что в вашем решении.

Затем ваша программа выполняет следующие действия:

1: If the ball is not directly above a wall, drop it down to the nearest wall.
2: If the ball passes through a hole during step 1, remove the ball.
3: Display the maze in the standard output (followed by a newline).
   Extraneous whitespace should not be displayed.
   Extraneous whitespace is defined to be whitespace outside of a rectangle 
   that fits snugly around the maze.
4: If there is no ball in the maze, exit.
5: Read a line from the standard input. 
   Given a 1, rotate the maze counterclockwise. 
   Given a 2, rotate the maze clockwise. 
   Rotations are done by 90 degrees. 
   It is up to you to decide if extraneous whitespace is allowed.
   If the user enters other inputs, repeat this step.
6: Goto step 1.

Вы можете предположить, что все входные лабиринты закрыты. Примечание: отверстие эффективно действует как стена в этом отношении.

Вы можете предположить, что все входные лабиринты не имеют посторонних пробелов.

Побеждает самый короткий исходный код по количеству символов.


Пример, написанный в javascript: http://trinithis.awardspace.com/rotatingMaze/maze.html


Примеры лабиринтов:

######
#o  @#
######

###########
#o        #
# ####### #
###@      #
  #########

###########################
#                         #
#       #     @           #
#       #         #      ##
#       #         ####o####
 #      #                 #
  #                       #
   #              #########
    #                     @
     ######################
4b9b3361

Ответ 1

GolfScript - 97 символов

n/['']/~{;(@"zip-1%":|3*~{{." o"/"o "*"@o"/"@ "*[email protected]>}do}%|~.n*."o"/,(}{;\~(2*)|*~\}/\[n*]+n.+*])\;

Это сделано не так, как я надеялся (возможно, позже).

(Это мои заметки, а не объяснение)

n/['']/~                             #[M I]
{
;(@                                  #[I c M]
"zip-1%":|3*~                        #rotate
{{." o"/"o "*"@o"/"@ "*[email protected]>}do}%      #drop
|~                                   #rotate back
.n*                                  #"display" -> [I c M d]
."o"/,(                              #any ball? -> [I c M d ?]
}{                                   #d is collected into an array -> [I c M]
;\~(2*)|*~                           #rotate
\                                    #stack order
}/
\[n*]+n.+*])\;                       #output

Ответ 2

Perl, 143 (128) char

172 152 146 144 143 символа,

sub L{my$o;$o.=$/while s/.$/$o.=$&,""/meg;$_=$o}$_.=<>until/

/;{L;1while s/o / o/;s/[email protected]/ @/;L;L;L;print;if(/o/){1-($z=<>)||L;$z-2||L&L&L;redo}}

Значения новых строк значительны.

Использует стандартный ввод и ожидает ввода, чтобы содержать лабиринт, за которым следует пустая строка, за которой следуют инструкции (1 или 2), по одной инструкции на строку.

Пояснение:

sub L{my$o;$o.="\n"while s/.$/$o.=$&,""/meg;$_=$o}

L - это функция, которая использует регулярные выражения для поворота многострочного выражения $_ против часовой стрелки на 90 градусов. Регулярное выражение использовалось знаменитостями hobbs в моем любимом программном решении для гольфа всего времени.

$_.=<>until/\n\n/;

Завершает ввод до первой пары последовательных строк новой строки (то есть лабиринт) в $_.

L;1 while s/o / o/;s/[email protected]/ */;
L;L;L;print

Чтобы сбросить мяч, нам нужно переместить символ o на одну строку, если под ним находится пробел. Это довольно сложно сделать с одним скалярным выражением, поэтому вместо этого мы будем вращать лабиринт против часовой стрелки, переместите шар вправо. Если дыра когда-либо появляется на "правильном" шаре, тогда мяч будет падать в отверстие (это не в спецификации, но мы можем изменить @ на *, чтобы показать, какое отверстие имеет мяч попасть). Затем перед тем, как мы напечатаем, нам нужно повернуть плату по часовой стрелке на 90 градусов (или против часовой стрелки 3 раза), чтобы снова вниз вниз.

if(/o/) { ... }

Продолжайте, если в лабиринте еще есть мяч. В противном случае блок завершится, и программа выйдет.

1-($z=<>)||L;$z-2||L+L+L;redo

Прочитайте инструкцию в $z. Поверните плату против часовой стрелки один раз для инструкции "1" и три раза для инструкции "2".

Если мы использовали еще 3 символа и сказали +s/o[@*]/ */ вместо ;s/[email protected]/ */, мы могли бы поддерживать несколько шаров.

Простая версия этой программы, где инструкции "2" для поворота лабиринта по часовой стрелке и любая другая инструкция для вращения против часовой стрелки, могут выполняться в 128 символах.

sub L{my$o;$o.=$/while s/.$/$o.=$&,""/meg;$_=$o}$_.=<>until/

/;L;{1while s/o / o/+s/[email protected]/ @/;L,L,L;print;if(/o/){2-<>&&L,L;redo}}

Ответ 3

Rebmu: 298 символов

Я возился с собственным экспериментом в дизайне языка Code Golf! Я еще не выбрасывал матричные трюки в стандартную сумку, и копирование идей GolfScript, вероятно, поможет. Но сейчас я работаю над усовершенствованием основного трюка.

Во всяком случае, здесь моя первая попытка. В коде требуются четыре внутренних пробела, но разрывы строк не нужны:

.fFS.sSC L{#[email protected]}W|[l?fM]H|[l?m]Z|[Tre[wH]iOD?j[rvT]t]
Ca|[st[xY]a KrePC[[yBKx][ntSBhXbkY][ntSBhYsbWx][xSBwY]]ntJskPCmFkSk]
Ga|[rtYsZ[rtXfZ[TaRE[xY]iTbr]iTbr]t]B|[gA|[ieSlFcA[rnA]]]
MeFI?a[rlA]aFV[NbIbl?n[ut[++n/2 TfCnIEfLtBRchCbSPieTHlTbrCHcNsLe?sNsZ]]
gA|[TfCaEEfZfA[prT][pnT]nn]ulBbr JmoADjPC[3 1]rK4]

Может показаться, что кошка была на моей клавиатуре. Но как только вы пройдете мимо небольшого компактного трюка (буквально сберегающего пространства), называемого "mushing", это не так уж плохо. Идея заключается в том, что Rebmu не чувствителен к регистру, поэтому для сжатия символов используется чередование прогонов заглавных букв. Вместо выполнения FooBazBar => foo baz bar я применяю различные значения. FOObazBAR => foo: baz bar (где первый токен является целью назначения) vs FooBazBar => foo baz bar (все обычные токены).

При запуске unmush вы получаете что-то более читаемое, но расширенное до 488 символов:

. f fs . s sc l: {#[email protected]} w: | [l? f m] h: | [l? m] z: | [t: re [w h] i od? 
j [rv t] t] c: a| [st [x y] a k: re pc [[y bk x] [nt sb h x bk y] [nt sb 
h y sb w x] [x sb w y]] nt j sk pc m f k s k] g: a| [rt y s z [rt x f z 
[t: a re [x y] i t br] i t br] rn t] b: | [g a| [ie s l f c a [rn a]]] 
m: e fi? a [rl a] a fv [n: b i bl? n [ut [++ n/2 t: f c n ie f l t br 
ch c b sp ie th l t br ch c n s l e? s n s z]] g a| [t: f c a ee f z f 
a [pr t] [pn t] nn] ul b br j: mo ad j pc [3 1] r k 4]

Rebmu также может запускать его. Также есть также многословные ключевые слова (first вместо fs), и вы можете смешивать и сопоставлять. Здесь определения функций с некоторыми комментариями:

; shortcuts f and s extracting the first and second series elements
. f fs
. s sc 

; character constants are like #"a", this way we can do fL for #"#" etc
L: {#[email protected]}

; width and height of the input data
W: | [l? f m] 
H: | [l? m]

; dimensions adjusted for rotation (we don't rotate the array)
Z: | [t: re [w h] i od? j [rv t] t] 

; cell extractor, gives series position (like an iterator) for coordinate
C: a| [
    st [x y] a 
    k: re pc [[y bk x] [nt sb h x bk y] [nt sb h y sb w x] [x sb w y]] nt j 
    sk pc m f k s k
] 

; grid enumerator, pass in function to run on each cell
G: a| [rt y s z [rt x f z [t: a re [x y] i t br] i t br] t] 

; ball position function
B: | [g a| [ie sc l f c a [rn a]]]

W - это функция ширины, а H - высота исходных данных массива. Данные никогда не вращаются... но есть переменная j, которая сообщает нам, сколько поворотов на 90 градусов мы должны применять.

Функция Z дает нам скорректированный размер, когда учитывается поворот, а функция C принимает параметр пары координат и возвращает серию позиций (вроде как указатель или итератор) в данные для эта координатная пара.

Здесь есть итератор массива G, к которому вы передаете функцию, и он будет вызывать эту функцию для каждой ячейки сетки. Если функция, которую вы подаете, когда-либо возвращает значение, она остановит итерацию, и функция итерации вернет это значение. Функция B сканирует лабиринт для шара и возвращает координаты, если найден, или none.

Здесь основной цикл с комментариями:

; if the command line argument is a filename, load it, otherwise use string
m: e fi? a [rl a] a 

; forever (until break, anyway...)
fv [
    ; save ball position in n 
    n: B

    ; if n is a block type then enter a loop
    i bl? n [

        ; until (i.e. repeat until)
        ut [
            ; increment second element of n (the y coordinate)
            ++ n/2 

            ; t = first(C(n))
            t: f C n

            ; if-equal(first(L), t) then break
            ie f l t br

            ; change(C(B), space)
            ch C B sp

            ; if-equal(third(L),t) then break 
            ie th L t br 

            ; change(C(n), second(L))
            ch C n s L 

            ; terminate loop if "equals(second(n), second(z))"
            e? s n s z
         ]
     ] 

     ; iterate over array and print each line
     g a| [t: f c a ee f z f a [pr t] [pn t] nn]

     ; unless the ball is not none, we'll be breaking the loop here...
     ul b br 

     ; rotate according to input
     j: mo ad j pc [3 1] r k 4
]

Там не все, что особенно остроумно об этой программе. Это часть моей идеи, которая заключается в том, чтобы увидеть, какое сжатие можно получить на простых, скучных подходах, которые не зависят от каких-либо трюков. Я думаю, что это демонстрирует некоторые новые возможности Rebmu.

Будет интересно посмотреть, как лучшая стандартная библиотека может повлиять на краткость решений!

Последний обновленный источник комментариев доступен на GitHub: rotating-maze.rebmu

Ответ 4

Ruby 1.9.1 p243

355 353 символа

Я новичок в Ruby, поэтому я уверен, что это может быть намного короче - возможно, некоторые нюансы, которые мне не хватает.

При выполнении путь к файлу карты - это первая строка, которую он читает. Я попытался сделать его частью аргументов выполнения (сохранил бы 3 символа), но имел проблемы:)

Краткая версия:

def b m;m.each_index{|r|i=m[r].index(?o);return r,i if i}end;def d m;x,y=b m;z=x;
while z=z+1;c=m[z][y];return if c==?#;m[z-1][y]=" "; return 1 if [email protected];m[z][y]=?o;end;end;
def r m;m.transpose.reverse;end;m=File.readlines(gets.chomp).map{|x|x.chomp.split(//)};
while a=0;w=d m;puts m.map(&:join);break if w;a=gets.to_i until 0<a&&a<3;
m=r a==1?m:r(r(m));end

Вербальная версия:

(Я немного изменил сжатую версию, но вы поняли эту идею)

def display_maze m
 puts m.map(&:join)
end

def ball_pos m
  m.each_index{ |r|
    i = m[r].index("o")
    return [r,i] if i
  }
end

def drop_ball m
  x,y = ball_pos m
  z=x
  while z=z+1 do
    c=m[z][y]
    return if c=="#"
    m[z-1][y]=" "
    return 1 if c=="@"
    m[z][y]="o"
  end
end

def rot m
  m.transpose.reverse
end

maze = File.readlines(gets.chomp).map{|x|x.chomp.split(//)}

while a=0
  win = drop_ball maze
  display_maze maze
  break if win
  a=gets.to_i until (0 < a && a < 3)
  maze=rot maze
  maze=rot rot maze if a==1
end

Возможные области улучшения:

  • Чтение лабиринта в чистый 2D-массив (в настоящее время 55 символов)
  • Поиск и возвращение (x,y) координат шара (в настоящее время 61 символ)

Любые предложения по улучшению приветствуются.

Ответ 5

Haskell: 577 509 527 244 230 228 символов

Массивный новый подход: держите лабиринт в виде одной строки!

import Data.List
d('o':' ':x)=' ':(d$'o':x)
d('o':'@':x)=" *"++x
d(a:x)=a:d x
d e=e
l=unlines.reverse.transpose.lines
z%1=z;z%2=l.l$z
t=putStr.l.l.l
a z|elem 'o' z=t z>>readLn>>=a.d.l.(z%)|0<1=t z
main=getLine>>=readFile>>=a.d.l

Поклониться решению @mobrule Perl для идеи сброса мяча сбоку!

Ответ 6

Python 2.6: ~ 284 ~ символов

Возможно, у вас еще есть возможности для улучшения (хотя с первых изменений я уже многого добился).

Все комментарии или предложения более чем приветствуются!

Поставьте файл карты в командной строке в качестве первого аргумента:
python rotating_maze.py input.txt

import sys
t=[list(r)[:-1]for r in open(sys.argv[1])]
while t:
 x=['o'in e for e in t].index(1);y=t[x].index('o')
 while t[x+1][y]!="#":t[x][y],t[x+1][y]=" "+"[email protected]"[t[x+1][y]>" "];x+=1
 for l in t:print''.join(l)
 t=t[x][y]=='o'and map(list,(t,zip(*t[::-1]),zip(*t)[::-1])[input()])or 0

Ответ 7

С# 3.0 - 650 638 символов

(не уверены, как подсчитываются новые строки) (ведущие пробелы для чтения, не считаются)

using System.Linq;
using S=System.String;
using C=System.Console;
namespace R
{
class R
{
static void Main(S[]a)
{
S m=S.Join("\n",a);
bool u;
do
{
 m=L(m);
 int b=m.IndexOf('o');
 int h=m.IndexOf('@',b);
 b=m.IndexOf('#',b);
 m=m.Replace('o',' ');
 u=(b!=-1&b<h|h==-1);
 if (u)
  m=m.Insert(b-1,"o").Remove(b,1);
 m=L(L(L(m)));
 C.WriteLine(m);
 if (!u) return;
 do
 {
  int.TryParse(C.ReadLine(),out b);
  u=b==1|b==2;
  m=b==1?L(L(L(m))):u?L(m):m;
 }while(!u);
}while(u);
}
static S L(S s)
{
return S.Join("\n",
 s.Split('\n')
 .SelectMany(z => z.Select((c,i)=>new{c,i}))
 .GroupBy(x =>x.i,x=>x.c)
 .Select(g => new S(g.Reverse().ToArray()))
 .ToArray());
}
}
}

Считывается из командной строки, здесь используется тестовая строка:

"###########" "#o        #" "# ####### #" "###@      #" "  #########"

В значительной степени зависит от ответа moblle Perl для алгоритма.

Возможно, мой метод Rotation (L) может быть улучшен.

Обрабатывает корпус без стенок.