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
Я новичок в 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
Возможные области улучшения:
(x, y)
co- ординаты шара (в настоящее время 61 символ) Любые предложения по улучшению приветствуются.
Впечатляющий новый подход: храните лабиринт как одну нитку!
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 за идею сбрасывать мяч боком!
Возможно, все еще есть возможности для улучшения (хотя я уже многое изменил с момента первых ревизий).
Все комментарии и предложения приветствуются!
В качестве первого аргумента укажите файл карты в командной строке:
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
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}}
(не уверен, как считаются новые строки) (пробельные символы для чтения не учитываются)
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), вероятно, может быть улучшен.
Обрабатывает случай без стен.
Я возился с моим собственным экспериментом в разработке языка 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