Гольф кода: вращение лабиринта

20
задан 6 revs, 2 users 99% 10 January 2013 в 11:23
поделиться

7 ответов

GolfScript - 97 символов

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

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

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

n/['']/~                             #[M I]
{
;(@                                  #[I c M]
"zip-1%":|3*~                        #rotate
{{." o"/"o "*"@o"/"@ "*.@>}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
8
ответ дан 29 November 2019 в 23:40
поделиться

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 c==?@;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) co- ординаты шара (в настоящее время 61 символ)

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

6
ответ дан 29 November 2019 в 23:40
поделиться

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

Кивает Perl-решению @mobrule за идею сбрасывать мяч боком!

3
ответ дан 29 November 2019 в 23:40
поделиться

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]=" "+"o@"[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
3
ответ дан 29 November 2019 в 23:40
поделиться

Perl, 143 (128) char

172 152 146 144 143 chars,

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

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

Newlines are significant.

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

Пояснение:

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

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

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

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

L;1 while s/o / o/;s/o@/ */;
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/o@/ */, то мы могли бы поддерживать несколько шаров.

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

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

/;L;{1while s/o / o/+s/o@/ @/;L,L,L;print;if(/o/){2-<>&&L,L;redo}}
14
ответ дан 29 November 2019 в 23:40
поделиться

C# 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        #" "# ####### #" "###@      #" "  #########"

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

Мой метод вращения (L), вероятно, может быть улучшен.

Обрабатывает случай без стен.

1
ответ дан 29 November 2019 в 23:40
поделиться

Ребму : 298 символов

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

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

.fFS.sSC L{#o@}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]

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

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

. f fs . s sc l: {#o@} 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: {#o@}

; 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 сканирует лабиринт на предмет шара и возвращает координаты, если они найдены, или нет .

Вот основной цикл с некоторыми комментариями:

; 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
]

В этой программе нет ничего особенного. Это часть моей идеи - посмотреть, какое сжатие можно получить с помощью простых, скучных подходов, не основанных на каких-либо уловках. Я думаю, что это демонстрирует некоторый новаторский потенциал Ребму.

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

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

8
ответ дан 29 November 2019 в 23:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: