Expandir o cérebro comprimido

26

Este desafio foi publicado como parte do desafio de abril de 2018 do LotM , bem como para o segundo aniversário do Brain-flak


Eu estava pensando sobre qual seria a maneira mais eficiente de codificar programas de ataques cerebrais. O mais óbvio a ser feito, uma vez que existem apenas 8 caracteres válidos, é mapear cada caractere para uma sequência de 3 bits. Isso certamente é muito eficaz, mas ainda é muito redundante. Existem alguns recursos do código de quebra-cérebro que poderíamos aproveitar para encurtar a codificação.

  • As nilads, todas representadas por 2 colchetes correspondentes, realmente atuam como uma única unidade de informação em vez de 2. Se substituíssemos cada colchete por um caractere de byte único, isso tornaria as codificações muito menores sem perder dados.

  • Este é menos óbvio, mas os bytes de fechamento das mônadas também são redundantes. Pense que você poderia adivinhar o que os '?'caracteres representam no snippet a seguir?

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

    Se supusermos que a entrada é um código de falha cerebral válido, existe apenas uma opção para cada um desses pontos de interrogação. Isso significa que podemos usar inequivocamente um caractere de mônada próxima para representar todos os colchetes de fechamento. Isso tem o benefício adicional de manter o conjunto de caracteres pequeno, o que ajudaria muito se desejássemos usar uma codificação huffman. Como o caractere de mônada próxima provavelmente será o caractere mais comum por uma ampla margem, ele poderá ser representado por um único bit, o que é extremamente eficiente.

Esses dois truques nos permitirão compactar o código do cérebro através do seguinte algoritmo:

  1. Substitua todos os colchetes de fechamento de uma mônada por |. Ou, em outras palavras, substitua todos os colchetes que não sejam precedidos por sua correspondência de abertura por uma barra. Tão...

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

    se tornaria

    (({}|<(()()()||{}|
    
  2. Substitua cada nilad pelo suporte de fechamento. Portanto, colchetes correspondentes sem nada neles usam o seguinte mapeamento:

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

    Agora, nosso último exemplo se torna:

    ((}|<()))||}|
    
  3. Remova os |caracteres finais . Como sabemos que o número total de barras deve ser igual ao número total de ({[<caracteres, se houver barras no final ausentes, podemos inferi-las. Então, um exemplo como:

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

    se tornaria

    ({(}|(}[)
    

Seu desafio para hoje é reverter esse processo.

Dada uma sequência de ataques cerebrais comprimidos contendo apenas os caracteres (){}[]<>|, expanda-os para o código original de ataques cerebrais. Você pode supor que a entrada sempre será expandida para um ataque cerebral válido. Isso significa que nenhum prefixo da entrada jamais conterá mais |de({[< caracteres.

A entrada não conterá à direita | caracteres . Estes devem ser inferidos a partir do contexto.

Como de costume, você pode enviar um programa completo ou uma função, e os formatos de entrada / saída são permitidos. E como esse é um , seu código será pontuado pelo tamanho do código-fonte em bytes, quanto menor a pontuação, melhor.

Casos de teste

Aqui estão alguns casos de teste. Se você quiser mais, pode gerar seus próprios casos de teste com este script python e o Brain-Flak Wiki , que é a origem da maioria desses casos de teste.

#Compressed code
#Original code

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


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

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


(((()))||(](((}}||(}([(((}))||||(]((}}|}|}}|||]||]|[))||(}))|}(}|(}]]|}
((((()()()))([]((({}{}))({}([((({}()())))]([](({}{}){}){}{})))[]))[])[()()])({}()()){}({})({}[][]){}
DJMcMayhem
fonte
4
gênio. absolutamente genial. Você deve criar uma linguagem derivada.
NH.
8
@NH. Pessoalmente, sinto que os idiomas que diferem apenas na codificação são realmente chatos.
DJMcMayhem
1
@ dj, mas este ocuparia menos bytes e, portanto, seria melhor para o golfe.
NH.
5
O Brain-Flak não foi projetado para ser bom no golfe.
DJMcMayhem

Respostas:

32

Flacidez cerebral , 952916818 bytes

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

Salva 360 bytes calculando os parênteses opostos relativamente ao invés do zero (por exemplo, ')'= em '(' + 1vez de (((5 * 2) * 2) * 2) + 1)

Salva 34 bytes com algumas substituições diretas do DJMcMayhem

Economizou 10 bytes sobrepondo o >]}código de manipulação

118 bytes salvos deduplicando rolos

Economizou 40 bytes aproveitando a pilha vazia para simplificar o primeiro rolo

Economizou 48 bytes marcando EOF com -1, permitindo um código de rolo mais conciso

Salvei 36 bytes usando a lógica de ações igual em vez da minha

Economizou 98 bytes graças a Jo King que encontrou uma maneira mais eficiente de criar a saída

Experimente online!

Jogando golfe pela primeira vez no Brain-Flak, provavelmente há grandes melhorias, mas funciona. Muita cópia / pasta para lidar com cada tipo de colchete, e muito obrigado ao gerador inteiro automático e ao snippet Roll daqui .

Explicação aqui , o TIO o formata mais facilmente

Resposta bônus:

Flacidez cerebral comprimida 583 bytes

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

Experimente online!

(Observe que o link acima não é executado porque o TIO não possui um interpretador Compress-Brain-Flak. Você pode encontrar um transpiler para o Brain-Flak aqui )

Eu verifiquei que isso é válido ao transpilar para o Brain-Flak usando essa ferramenta, agora eficiente o suficiente para que o tempo limite seja improvável.

Kamil Drakari
fonte
4
Golfe pela primeira vez em Brain-Flak, e o resultado é este? Uau.
Erik the Outgolfer
Você sempre pode substituir <>(<()>)por (<>). Além disso, você pode mudar (<>{}<>)(<()>)para(<(<>{}<>)>)
DJMcMayhem
1
@JoKing Eu não sei como, eu mal consegui extrair o rolo no final do loop em vez de ter um extra em cada Se o bloco
Kamil Drakari
1
Isso está além do golfe. Isso é pura loucura. Parabéns!
Arthur Attout 03/04
1
@JoKing A mudança foi mais fácil e mais eficaz do que eu esperava, e agora incluído na resposta
Kamil Drakari
7

Retina 0.8.2 , 103 98 bytes

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

Experimente online! O link inclui casos de teste. Editar: salvou 5 bytes com inspiração em @MartinEnder. Explicação:

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

Coloque um ;colchete depois de cada fechamento e troque-os todos para abrir colchetes, e troque também |s para ;s.

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

Conte o número de colchetes abertos inigualáveis ​​e adicione tantos ;s.

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

Copie cada colchete de abertura para a correspondência ;.

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

Vire os colchetes copiados e exclua os ;.

Neil
fonte
1
Você poderia evitar todas as barras de escape se traduzir |para algo assim !. Ele nem sequer bytes custar se você traduzir >-}para <-{(que eu acho que dá zpara |).
Martin Ender
@ MartinEnder Não tenho certeza se entendi seu ponto de vista, zmas descobri uma maneira de eliminar alguns bytes de qualquer maneira.
194 Neil
5

TIS , 670 666 bytes

-4 bytes para pular para frente para pular para trás

Código:

@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

Layout:

6 3
CCCCCCCCCCCCCCCCSC
I0 ASCII -
O0 ASCII -

Experimente online!

Duvido que seja o menor, mas não vejo uma maneira de diminuí-lo. Infelizmente, todos os NOPs parece ser necessário para o sincronismo, e eu não posso colocar a pilha, onde @14atualmente é por causa da leitura a partir ANYde@11 .

A estrutura desta solução é a seguinte:

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

Ao ver um colchete aberto, o aberto é enviado ao longo da coluna da esquerda para saída e o fechamento é enviado ao longo da coluna da direita para a pilha.

Ao ver uma chave de fechamento, o abrir e fechar são enviados ao longo da coluna esquerda para serem impressos.

Ao ver um tubo, a pilha é acionada e enviada para a saída.

No EOF, @1começará a leitura de @2, em vez do fluxo de entrada de @0. @2produz um fluxo interminável de tubos, para que a pilha seja drenada.

Quando a entrada e a pilha estiverem esgotadas, o programa será interrompido.

Aviso: devido às limitações do TIS, o tamanho da pilha é limitado a 15. Se algo estiver aninhado mais profundamente que isso, esta implementação produzirá um resultado incorreto.

Phlarx
fonte
4

JavaScript (ES6), 107 bytes

Recebe a entrada como uma matriz de caracteres. Retorna uma string.

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``

Experimente online!

Arnauld
fonte
102 bytes retornando uma matriz de caracteres também.
Shaggy
@ Shaggy Thanks! Mas é realmente permitido retornar seqüências de caracteres de 1 e 2 caracteres misturadas?
Arnauld #
Hmm ... sim, talvez isso esteja pressionando a saída "permissiva".
Shaggy
@DJMcMayhem Você poderia dar uma olhada no novo formato de saída e nos informar se isso é aceitável?
Arnauld #
1
@arnauld Huh, por algum motivo que não me deu ping. Eu acho que diria não. Uma matriz de caracteres ou uma corda são os dois formatos padrão, mas uma matriz de strings não parece válido para mim
DJMcMayhem
3

Ruby , 104 bytes

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

Este é um programa completo que gera no console. (i<5?a:$>)<<r[-i]tem que ser um dos melhores campos de golfe que eu já fiz.

Experimente online!

Ruby , 106 bytes

->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}

Esta é a minha primeira solução. Uma função lambda anônima que pega e retorna seqüências de caracteres.

Experimente online!

MegaTom
fonte
3

Flacidez cerebral , 606 548 496 418 394 390 bytes

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

Experimente online!

Comecei isso jogando a resposta de Kamil Drakari , mas ela escapou de mim até o ponto em que decidi publicá-la como uma resposta separada.

Explicação:

{ #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

E claro...

Flacidez cerebral comprimida, 285 bytes:

{(((}|||(>|(((((((([()|)))||}|}|})|()||((()|))|)|}}||}[)||({<((}>}[)||||){[)|(<}|||}>|}><}||{(}(}|>|(>||}(}>|(>|(((((((([()|)))||}|}|})|()||((()|)|})|}||}|({<((}>}[)||||[)|{)(<}|||}>|}>|{(<(}(<)||>(}|<{(}>|>|||||>{(}>|>||}(})>|{}(}>|((>|||}}>(<(}(<)||><{(}>|>|||||>{(}>|>|}>|}{(}}>|>|>
Brincadeira
fonte
1
Golfe muito impressionante! Estou decepcionado comigo mesmo por não perceber isso mais cedo, vou ter que me aprofundar mais tarde para entender como funciona.
Kamil Drakari
2

Java 10, 424 bytes

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

É meio demorado, mas não consegui descobrir como reduzi-lo ainda mais. Este é um bom desafio, no entanto.

Experimente online aqui .

Versão não destruída:

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
fonte
2

Python 2, 188 184 180 177 177 174 173 bytes

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

Guardado 4 bytes graças a DJMcMayhem.
Experimente online!


fonte
180
DJMcMayhem
168 bytes por mexer com o segundo a última linha
DJMcMayhem
@DJMcMayhem Isso só funciona se sacabar vazio. Caso contrário, você terminará com os caracteres extras no lado errado.
2

Python 3 , 122 bytes

D='()<>[]{} '
def f(x):
 a=s=''
 for c in x:i=D.find(c);a+=i<0and s[0]or D[i-(i&1):i+1];s=D[i+1][i&1:]+s[i<0:]
 return a+s

Experimente online!

RootTwo
fonte
1

Haskell , 152 bytes

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)

Experimente online! ou verifique todos os casos de teste . pimplementa um analisador recursivo, que pode ser um extermínio para a gramática simples.

Laikoni
fonte
1
Boa função mpara encontrar o suporte correspondente.
Nimi
1

Python 2 , 244 bytes

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

Experimente online!

Demorou mais de uma hora ou duas para fazer isso funcionar ...

Erik, o Outgolfer
fonte