Развернуть сжатый мозг-зенит

26

Этот конкурс был опубликован в рамках конкурса LotM в апреле 2018 года , а также ко второму дню рождения Brain-flak


Я думал о том, каким будет наиболее эффективный способ кодирования программ мозговых атак. Очевидная вещь, которую нужно сделать, поскольку существует только 8 допустимых символов, состоит в том, чтобы сопоставить каждый символ с 3-битной последовательностью. Это, конечно, очень эффективно, но все равно очень избыточно. В коде мозгового штурма есть некоторые особенности, которые мы могли бы использовать для сокращения кодировки.

  • Нилады, которые все представлены в 2 совпадающих скобках, действительно действуют как единая единица информации, а не как 2. Если бы мы заменили каждую скобку однобайтовым символом, это сделало бы кодировки намного меньше без потери каких-либо данных.

  • Этот менее очевиден, но завершающие байты монад также избыточны. Думаю, вы могли догадаться, что '?'символы представляют в следующем фрагменте?

     {(({}?<>?<>?
    

    Если мы предположим, что введенный код является допустимым кодом «мозгового штурма», то для каждого из этих вопросительных знаков будет только один вариант. Это означает, что мы можем однозначно использовать символ закрывающей монады для представления каждой закрывающей скобки. Это дает дополнительное преимущество, заключающееся в сохранении небольшого набора символов, что очень помогло бы, если бы мы хотели использовать кодировку Хаффмана. Поскольку символ закрытой монады , скорее всего, будет наиболее распространенным символом с большим отрывом, он может быть представлен одним битом, что чрезвычайно эффективно.

Эти два трюка позволят нам сжать код мозгового штурма с помощью следующего алгоритма:

  1. Замените все закрывающие скобки монады на |. Или, другими словами, замените каждую закрывающую скобку, которой не предшествует ее открывающее совпадение, с чертой. Так...

    (({})<(()()())>{})
    

    станет

    (({}|<(()()()||{}|
    
  2. Замените каждую ниладу закрывающей скобкой. Поэтому в сопоставленных скобках, в которых нет ничего, используется следующее сопоставление:

    () --> )
    {} --> }
    [] --> ]
    <> --> >
    

    Теперь наш последний пример становится:

    ((}|<()))||}|
    
  3. Удалить висячие |символы. Поскольку мы знаем, что общее количество баров должно равняться общему количеству ({[<символов, если в конце отсутствуют бары, мы можем вывести их. Итак, пример как:

    ({({})({}[()])})
    

    станет

    ({(}|(}[)
    

Ваша задача на сегодня - переломить этот процесс.

Получив цепочку сжатых мозговых злаков, содержащих только символы (){}[]<>|, разверните ее в исходный код мозговых злаков. Вы можете предположить, что входные данные всегда будут расширяться до действительных умственных способностей. Это означает, что ни один префикс ввода никогда не будет содержать больше, |чем ({[<символы.

Ввод не будет содержать завершающие |символы. Они должны быть выведены из контекста.

Как обычно, вы можете отправить либо полную программу, либо функцию, а форматы ввода / вывода допустимы. А поскольку это , ваш код будет оцениваться по длине исходного кода в байтах, чем меньше оценка, тем лучше.

Контрольные примеры

Вот несколько тестов. Если вам нужно больше, вы можете сгенерировать свои собственные тестовые сценарии с помощью этого сценария Python и Brain-Flak Wiki , откуда и происходит большинство этих тестовых примеров.

#Compressed code
#Original code

())))
(()()()())


([([}()||||(>||{(})|>|}{((<}|||>}|}>}
([([{}(())])](<>)){({}())<>}{}{((<{}>))<>{}}{}<>{}

({(}|(}[)|||}
({({})({}[()])}{})


(((()))||(](((}}||(}([(((}))||||(]((}}|}|}}|||]||]|[))||(}))|}(}|(}]]|}
((((()()()))([]((({}{}))({}([((({}()())))]([](({}{}){}){}{})))[]))[])[()()])({}()()){}({})({}[][]){}
DJMcMayhem
источник
4
гений. абсолютно гениально Вы должны сделать производный язык.
НХ.
8
@NH. Лично я чувствую, что языки, которые отличаются только кодировкой, действительно скучны.
DJMcMayhem
1
@dj, но этот занимал бы меньше байтов и поэтому был бы лучше для игры в гольф.
НХ.
5
Brain-Flak не был предназначен для игры в гольф.
DJMcMayhem

Ответы:

32

Brain-Flak , 952 916 818 байт

{(({})[(((()()()()()){}){}){}])((){[()](<{}>)}{}){{}(({})()<>)(<>)}{}(<>)<>(({})[(((()()()){}){}()){({}[()])}{}])((){[()](<{}>)}{})({}<>{})<>(({})[((((()()()()()){}){})){}{}])((){[()](<{}>)}{})({}<>{})<>(({})[(((((()()()()()){}){}){}())){}{}])((){[()](<{}>)}{})({}<>{}){{}(<(<>({})()()<>)>)}{}<>(({})[(((()()()()()){}){}){}()])((){[()](<{}>)}{}){{}(({})[()])(<>)<>(<({}<{({}<>)<>}{}>)>)<>{({}<>)<>}{}(<>)}{}(<>)<>(({})[(((((()()()()()){})){}{}())){}{}])((){[()](<{}>)}{})({}<>{})<>(({})[((((()()()()()){}){})()){}{}])((){[()](<{}>)}{})({}<>{})<>(({})[(((((()()()()()){}){}){}())()){}{}])((){[()](<{}>)}{})({}<>{}){{}<>(<(({})[()()])(<>)<>(<({}<{({}<>)<>}{}>)>)<>{({}<>)<>}{}>)}{}<>(({})[(((((()()()()()){}){})()){}{}){}])((){[()](<{}>)}{}){{}{}(<(<>{}<>)>)}{}(<>)<>(<({}<{({}<>)<>}{}>)>)<>{({}<>)<>}{}<>}{}{({}<>)<>}<>

Экономия 360 байт путем вычисления противоположных скобок относительно, а не с нуля (например, ')'= '(' + 1вместо (((5 * 2) * 2) * 2) + 1)

Сохранено 34 байта с некоторыми прямыми заменами от DJMcMayhem

Сохранено 10 байтов путем наложения >]}кода обработки

Сохранено 118 байт за счет дедупликации рулонов

Экономия 40 байтов за счет использования пустого стека для упрощения первого броска

Сохранение 48 байтов путем пометки EOF -1, что позволяет более краткий код броска

Сохранено 36 байтов, используя логику равенства акций вместо моей

Экономия 98 байт благодаря Джо Кингу, который нашел более эффективный способ создания выходных данных.

Попробуйте онлайн!

Первый раз игра в гольф в Brain-Flak, так что, возможно, есть некоторые действительно большие улучшения, но это работает. Много копий / вставок для обработки каждого типа скобок, и большое благодаря автоматическому целочисленному генератору и фрагменту Roll отсюда .

Объяснение здесь , TIO форматирует его легче

Бонусный ответ:

Сжатый Brain-Flak 583 байта

{((}|[((()))))|}|}|}||(){[)|(<}|||}|{}((}|)>|(>||}(>|>((}|[((()))|}|})|{(}[)|||}||(){[)|(<}|||}|(}>}|>((}|[(((()))))|}|}||}}||(){[)|(<}|||}|(}>}|>((}|[((((()))))|}|}|})||}}||(){[)|(<}|||}|(}>}|{}(<(>(}|))>||||}>((}|[((()))))|}|}|})||(){[)|(<}|||}|{}((}|[)||(>|>(<(}<{(}>|>|}||||>{(}>|>|}(>||}(>|>((}|[((((()))))|}||}})||}}||(){[)|(<}|||}|(}>}|>((}|[(((()))))|}|}|)|}}||(){[)|(<}|||}|(}>}|>((}|[((((()))))|}|}|})|)|}}||(){[)|(<}|||}|(}>}|{}>(<((}|[))||(>|>(<(}<{(}>|>|}||||>{(}>|>|}|||}>((}|[((((()))))|}|}|)|}}|}||(){[)|(<}|||}|{}}(<(>}>||||}(>|>(<(}<{(}>|>|}||||>{(}>|>|}>|}{(}>|>|>

Попробуйте онлайн!

(Обратите внимание, что приведенная выше ссылка не работает, потому что TIO не имеет интерпретатора Compressed Brain-Flak. Вы можете найти транспортер для Brain-Flak здесь )

Я проверил, что это действительно так, перенеся его в Brain-Flak с помощью этого инструмента, который теперь достаточно эффективен, поэтому время ожидания маловероятно.

Камил Дракари
источник
4
Первый раз играем в гольф в Brain-Flak, и результат такой? Вау.
Эрик Outgolfer
Вы всегда можете заменить <>(<()>)на (<>). Кроме того, вы можете изменить (<>{}<>)(<()>)на(<(<>{}<>)>)
DJMcMayhem
1
@JoKing Я бы не знал, как, мне едва удалось извлечь Roll в конце цикла, вместо того, чтобы иметь дополнительный в каждом блоке If
Камил Дракари
1
Это вне игры в гольф. Это чистое безумие. Поздравляю!
Артур Аттут
1
@JoKing Изменения были и проще, и эффективнее, чем я ожидал, и теперь включены в ответ
Камил Дракари
7

Сетчатка 0.8.2 , 103 98 байт

[])}>]
$&;
T`])}>|`[({<;
r`(.*)((;)|(?<-3>.))*
$&$.1$*;
(?<=(.)((;)|(?<-3>.))*);
;$1
T`;-{`_>-}`;.

Попробуйте онлайн! Ссылка включает в себя тестовые случаи. Редактировать: 5 байт по вдохновению от @MartinEnder. Объяснение:

[])}>]
$&;
T`])}>|`[({<;

Поставьте ;после каждой закрывающей скобки и замените их на открытые скобки, а также измените |s на ;s.

r`(.*)((;)|(?<-3>.))*
$&$.1$*;

Подсчитайте количество непревзойденных открытых скобок и добавьте столько ;s.

(?<=(.)((;)|(?<-3>.))*);
;$1

Скопируйте каждую открывающую скобку в соответствие ;.

T`;-{`_>-}`;.

Переверните скопированные скобки и удалите ;s.

Нил
источник
1
Вы могли бы избежать всех сбежавших баров, если вы переводите |что-то вроде !. Это не будет даже стоить байт , если перевести >-}на <-{(который я думаю , что дает zдля |).
Мартин Эндер,
@MartinEnder Не уверен, что я понимаю вашу точку зрения по поводу, zно я все равно придумал способ сэкономить еще несколько байтов.
Нейл
5

TIS , 670 666 байт

-4 байта для прыжка вперед, чтобы вернуться назад

Код:

@0
MOV UP RIGHT
@1
MOV ANY ACC
SUB 41
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MOV ACC DOWN
@2
NOP
MOV 124 LEFT
@3
MOV ANY DOWN
@4
MOV UP ACC
JGZ O
MOV 40 LEFT
JLZ (
MOV 41 LEFT
JRO 3
O:SUB 21
MOV ACC DOWN
JRO -8
(:MOV 41 RIGHT
@5
MOV ANY DOWN
@6
MOV ANY DOWN
@7
MOV UP ACC
JGZ O
MOV 60 LEFT
JLZ <
MOV 62 LEFT
JRO 3
O:SUB 31
MOV ACC DOWN
JRO -8
<:MOV 62 RIGHT
@8
MOV ANY DOWN
@9
MOV ANY DOWN
@10
S:MOV UP ACC
JGZ O
MOV 91 LEFT
JLZ [
MOV 93 LEFT
JRO 3
O:SUB 31
MOV ACC DOWN
JRO -8
[:MOV 93 RIGHT
@11
MOV ANY DOWN
@12
MOV ANY DOWN
@13
MOV UP ACC
JEZ |
MOV 123 LEFT
JLZ {
MOV 125 LEFT
JRO 2
|:MOV DOWN LEFT
JRO -7
{:MOV 125 RIGHT
@14
MOV ANY DOWN
@15
MOV UP DOWN
@16
MOV UP LEFT

Планировка:

6 3
CCCCCCCCCCCCCCCCSC
I0 ASCII -
O0 ASCII -

Попробуйте онлайн!

Я сомневаюсь, что это самое маленькое, но я не вижу способа сделать его меньше. К сожалению, все NOPs кажутся необходимыми для синхронизации, и я не могу поместить стек там, где @14сейчас находится из-за чтения из ANYin @11.

Структура этого решения следующая:

Input
  |
  V
  0    1:synchro  2:EOF
  3    4:parens     5
  6    7:angles     8
  9   10:squares   11
 12   13:curlies   14
 15      stack     16
  |
  V
Output

Увидев открытую фигурную скобку, открытое отправляется по левому столбцу для вывода, а закрытое отправляется по правому столбцу в стек.

Увидев закрывающую фигурную скобку, оба открытия и закрытия отправляются по левому столбцу для вывода.

После просмотра канала стек извлекается и отправляется на вывод.

После EOF @1начнется чтение из @2, а не из входного потока из @0. @2производит бесконечный поток труб, поэтому стек будет осушен.

Как только вход и стек исчерпаны, программа останавливается.

Предупреждение: из-за ограничений TIS размер стека ограничен 15. Если что-то вложено глубже, эта реализация приведет к неверному результату.

Phlarx
источник
4

JavaScript (ES6), 107 байт

Принимает ввод как массив символов. Возвращает строку.

a=>a.map(c=>(n=(S='|()[]{}<>').indexOf(c))?n&1?(s=[S[n+1],...s],c):S[n-1]+c:s.shift(),s=[]).join``+s.join``

Попробуйте онлайн!

Arnauld
источник
102 байта , возвращая массив символов тоже.
Лохматый
@ Shaggy Спасибо! Но действительно ли разрешено возвращать 1-символьные и 2-символьные строки, смешанные вместе?
Арно
Хм ... да, возможно, это подталкивает к "разрешающему" выводу.
Лохматый
@DJMcMayhem Не могли бы вы взглянуть на новый формат вывода и сообщить нам, если это приемлемо?
Арно
1
@arnauld Да, по какой-то причине, которая не пинговала меня. Я думаю, что скажу нет. Массив символов или одна строка являются стандартными форматами, но массив строк мне не
подходит
3

Рубин , 104 байта

a=[];$<.chars{|c|r="|[{(<>)}]";i=r.index(c);i<1||(i<5?a:$>)<<r[-i];$>.<<i<1?a.pop: c};$><<a.reverse.join

Это полная программа, которая выводит на консоль. (i<5?a:$>)<<r[-i]должен быть одним из самых крутых гольфов, которые я когда-либо делал.

Попробуйте онлайн!

Рубин , 106 байт

->s{a=[];(s.chars.map{|c|r="|>)}][{(<";d=r[-i=r.index(c)];i<5||a<<d;i<1?a.pop: i<5?d+c:c}+a.reverse).join}

Это мое первое решение. Анонимная лямбда-функция, которая принимает и возвращает строки.

Попробуйте онлайн!

MegaTom
источник
3

Brain-Flak , 606 548 496 418 394 390 байт

{((({})))(<>)(((((((([(())()()()]){}){}){}())(()))(((())()())()){}{})){}[()])({<(({}<>{}[()]))>(){[()](<{}>)}{}<>}{}<><{}>){({}({})<>)(<>)}{}({}<>)(<>)(((((((([(())()()()]){}){}){}())(()))(((())()){}()){})){})({<(({}<>{}[()]))>[()]{()(<{}>)}{}<>}{}<>){(<({}(<()>)<>({})<{({}<>)<>}>)>)<>{({}<>)<>}}{}({}()<>){{}({}<>)((<>))}{}{}<>(<({}(<()>)<><{({}<>)<>}>)>)<>{({}<>)<>}{}<>}{}{({}{}<>)<>}<>

Попробуйте онлайн!

Я начал это с игры в гольф с ответом Камиля Дракари , но это отошло от меня до такой степени, что я решил опубликовать его как отдельный ответ.

Объяснение:

{ #While input on stack
	((({})))(<>)	#Preserve copy of the character
	(((((		#Push the differences between start bracket characters
	((([(())()()()]){}){}){}())	#Push -31, 1
	(()))				#Push -30, 1
	(((())()())()){}{})		#Push -19, 1
	){}[()])			#Push -39
	({<(({}<>{}[()]))>(){[()](<{}>)}{}<>}{}<><{}>)	#If the character is any of the start brackets
	{({}({})<>)(<>)}{}					#Push the current character + TOS to the other stack

	({}<>)(<>)
	(((((		#Push the differences between end bracket characters
	((([(())()()()]){}){}){}())	#Push -31, 1
	(()))				#Push -30, 1
	(((())()){}()){})		#Push -19, 1
	){})				#Push -40
	({<(({}<>{}[()]))>[()]{()(<{}>)}{}<>}{}<>)	#If the character is any of the end brackets
	{(<({}(<()>)<>({})<{({}<>)<>}>)>)<>{({}<>)<>}}{}	#Push the character + TOS to the output

	({}()<>)	#If the character is not a |
	{{}({}<>)((<>))}{}	#Move current character to the other stack and push a zero
	{}		#Pop the top value of the stack, either the | or a 0
	<>(<({}(<()>)<><{({}<>)<>}>)>)<>{({}<>)<>}{}<>	#And push top of other stack to the output
}{}
{({}{}<>)<>}<>	#Reverse output and append the excess end brackets

И конечно ...

Сжатый Brain-Flak, 285 байт:

{(((}|||(>|(((((((([()|)))||}|}|})|()||((()|))|)|}}||}[)||({<((}>}[)||||){[)|(<}|||}>|}><}||{(}(}|>|(>||}(}>|(>|(((((((([()|)))||}|}|})|()||((()|)|})|}||}|({<((}>}[)||||[)|{)(<}|||}>|}>|{(<(}(<)||>(}|<{(}>|>|||||>{(}>|>||}(})>|{}(}>|((>|||}}>(<(}(<)||><{(}>|>|||||>{(}>|>|}>|}{(}}>|>|>
Джо Кинг
источник
1
Очень впечатляющий гольф! Я разочарован в себе из-за того, что не заметил этого раньше, мне придется углубиться в это позже, чтобы понять, как это работает.
Камил Дракари
2

Java 10, 424 байта

s->{int i=0;for(var c:s.toCharArray()){if("(<[{".indexOf(c)>-1)i++;if(c=='|')i--;}for(;i-->0;)s+='|';s=s.replace(")","()").replace(">","<>").replace("]","[]").replace("}","{}");char[]c=s.toCharArray(),r=new char[124];r[40]=41;r[60]=62;r[91]=93;r['{']='}';var o="";for(;++i<c.length ;){if(c[i]=='|'){c[i]=o.charAt(0);o=o.substring(1);}if("(<[{".indexOf(c[i])>-1&")>]}".indexOf(i+1<c.length?c[i+1]:0)<0)o=r[c[i]]+o;}return c;}

Это довольно долго, но я не мог понять, как сократить его дальше. Это хороший вызов, хотя.

Попробуйте это онлайн здесь .

Безголовая версия:

s -> { // lambda taking a String argument and returning a char[]
    int i = 0; // used for counting the number of '|'s that have been removed at the end of the input
    for(var c : s.toCharArray()) { // look at every character
        if("(<[{".indexOf(c) > -1) // if it's an open monad character
            i++; // we will need one more '|'
        if(c == '|') // if it's a close monad character
            i--; // we will need one '|' less
    }
    for(; i-- > 0; ) // add as many '|'
        s += '|';    // as necessary
    s = s.replace(")", "()").replace(">", "<>").replace("]", "[]").replace("}", "{}"); // replace compressed nilads with their uncompressed versions
    char[] c = s.toCharArray(), // from now on working on a char[] is more efficient since we will only be comparing and replacing
    r = new char[124]; // map open monad characters to their counterparts:
    r[40] = 41;   // '(' to ')'
    r[60] = 62;   // '<' to '>'
    r[91] = 93;   // '[' to ']'
    r['{'] = '}'; // '{' to '}'
    var o = ""; // we use this String as a kind of stack to keep track of the last open monad character we saw
    for(; ++i < c.length ;) { // iterate over the length of the expanded code
        if(c[i] == '|') { // if the current character is a close monad character
            c[i] = o.charAt(0); // replace it with the top of the stack
            o = o.substring(1); // and pop the stack
        }
        if("(<[{".indexOf(c[i]) > -1 // if the current character is an open monad/nilad character
         & ")>]}".indexOf(i+1 < c.length ? c[i+1] : 0) < 0) // and it's not part of a nilad (we need to test for length here to avoid overshooting)
            o = r[c[i]]+o; // using the mapping we established, push the corresponding character onto the stack
    }
    return c; // return the uncompressed code
}
OOBalance
источник
2

Python 2, 188 184 180 177 174 173 байта

p,q='([{<',')]}>'
d,s,a=dict(zip(p,q)),[],''
for c in input():
 if c in d:a+=c;s+=[c]
 elif'|'==c:a+=d[s.pop()]
 else:a+=dict(zip(q,p))[c]+c
for c in s[::-1]:a+=d[c]
print a

Сохранено 4 байта благодаря DJMcMayhem.
Попробуйте онлайн!


источник
180
DJMcMayhem
168 байт , перебирая 2-ю до последней строки
DJMcMayhem
@DJMcMayhem Это работает, только если окажется sпустым. В противном случае вы получите дополнительные символы в неправильном конце.
1

Haskell , 152 байта

fst.p
m c="> < ] [)(} {"!!mod(fromEnum c-6)27
p(c:r)|elem c")]}>",(s,t)<-p r=(m c:c:s,t)|c/='|',(s,'|':t)<-p$r++"|",(u,v)<-p t=(c:s++m c:u,v)
p e=("",e)

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

Laikoni
источник
1
Хорошая функция, mчтобы найти подходящую скобку.
Ним,
1

Python 2 , 244 байта

s=input()
B='([{<'
C=')]}>'
Z=zip(B,C)
P=sum(map(s.count,B))-s.count('|')
for i,j in Z:s=s.replace(j,i+j)
s+=P*'|'
b=[0]
for i in s:[b.pop()for j,k in Z if j==b[-1]<k==i];b+=[i][:i in B];s=i=='|'and s.replace(i,C[B.find(b.pop())],1)or s
print s

Попробуйте онлайн!

Потребовалось больше часа или двух, чтобы это заработало ...

Эрик Outgolfer
источник