Développer la compression cérébrale compressée

26

Ce défi a été publié dans le cadre du défi LotM d'avril 2018 , ainsi que pour le deuxième anniversaire de Brain-flak


Je réfléchissais à la façon la plus efficace de coder les programmes de brain-flak. La chose évidente à faire, puisqu'il n'y a que 8 caractères valides, est de mapper chaque caractère sur une séquence de 3 bits. C'est certainement très efficace, mais c'est toujours très redondant. Il existe certaines fonctionnalités du code brain-flak dont nous pourrions profiter pour raccourcir l'encodage.

  • Les nilades, qui sont toutes représentées par 2 parenthèses appariées, agissent vraiment comme une seule unité d'information plutôt que 2. Si nous remplaçions chaque parenthèse par un caractère à un octet, cela rendrait les encodages beaucoup plus petits sans perdre de données.

  • Celui-ci est moins évident, mais les octets de fermeture des monades sont également redondants. Vous pensez pouvoir deviner ce que les '?'personnages représentent dans l'extrait de code suivant?

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

    Si nous supposons que l'entrée est un code de flak de cerveau valide, il n'y a qu'une seule option pour chacun de ces points d'interrogation. Cela signifie que nous pouvons sans ambiguïté utiliser un caractère de monade proche pour représenter chaque parenthèse fermante. Cela a l'avantage supplémentaire de garder le jeu de caractères petit, ce qui aiderait grandement si nous voulions utiliser un encodage huffman. Étant donné que le caractère de monade proche sera très probablement le caractère le plus courant sur une large marge, il pourrait être représenté par un seul bit, ce qui est extrêmement efficace.

Ces deux astuces nous permettront de compresser le code brain-flak via l'algorithme suivant:

  1. Remplacez chaque support de fermeture d'une monade par |. Ou en d'autres termes, remplacez chaque parenthèse fermante qui n'est pas précédée par son match d'ouverture par une barre. Alors...

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

    deviendrait

    (({}|<(()()()||{}|
    
  2. Remplacez chaque nilad par son support de fermeture. Par conséquent, les crochets assortis sans rien utilisent le mappage suivant:

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

    Maintenant, notre dernier exemple devient:

    ((}|<()))||}|
    
  3. Supprimez les |caractères de fin . Parce que nous savons que le nombre total de barres doit être égal au nombre total de ({[<caractères, s'il manque des barres à la fin, nous pouvons les déduire. Donc, un exemple comme:

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

    deviendrait

    ({(}|(}[)
    

Votre défi pour aujourd'hui est d'inverser ce processus.

Étant donné une chaîne de brain-flak compressée contenant uniquement les caractères (){}[]<>|, développez-la dans le code original de brain-flak. Vous pouvez supposer que l'entrée se développera toujours en un flak de cerveau valide. Cela signifie qu'aucun préfixe de l'entrée ne contiendra jamais plus |de ({[<caractères.

L'entrée ne contiendra pas de |caractères de fin . Ceux-ci doivent être déduits du contexte.

Comme d'habitude, vous pouvez soumettre un programme complet ou une fonction, et les formats d'entrée / sortie sont permissifs. Et comme il s'agit d'un , votre code sera marqué par la longueur du code source en octets, plus le score est petit, mieux c'est.

Cas de test

Voici quelques cas de test. Si vous en voulez plus, vous pouvez générer vos propres cas de test avec ce script python et le Wiki Brain-Flak , d'où provient la majorité de ces cas de test.

#Compressed code
#Original code

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


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

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


(((()))||(](((}}||(}([(((}))||||(]((}}|}|}}|||]||]|[))||(}))|}(}|(}]]|}
((((()()()))([]((({}{}))({}([((({}()())))]([](({}{}){}){}{})))[]))[])[()()])({}()()){}({})({}[][]){}
DJMcMayhem
la source
4
génie. absolument génial. Vous devez créer un langage dérivé.
NH.
8
@NH. Personnellement, j'ai l'impression que les langages qui ne diffèrent que par l'encodage sont vraiment ennuyeux.
DJMcMayhem
1
@dj mais celui-ci prendrait moins d'octets et serait donc meilleur pour le golf.
NH.
5
Brain-Flak n'a pas été conçu pour être bon au golf.
DJMcMayhem

Réponses:

32

Brain-Flak , 952 916 818 octets

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

360 octets enregistrés en calculant les parenthèses opposées relativement plutôt qu'à partir de zéro (par exemple ')'= '(' + 1au lieu de (((5 * 2) * 2) * 2) + 1)

Enregistré 34 octets avec des remplacements directs de DJMcMayhem

10 octets enregistrés en chevauchant le >]}code de gestion

118 octets enregistrés en dédoublonnant les rouleaux

Économie de 40 octets en profitant de la pile vide pour simplifier le premier rouleau

Sauvegardé 48 octets en marquant EOF avec -1, permettant un code de roulement plus concis

Économisé 36 octets en utilisant le stock équivaut à la logique au lieu de la mienne

98 octets enregistrés grâce à Jo King qui a trouvé un moyen plus efficace de construire la sortie

Essayez-le en ligne!

Golf pour la première fois à Brain-Flak, il y a donc probablement de très grandes améliorations, mais cela fonctionne. Beaucoup de copier / coller pour gérer chaque type de support, et gros grâce au générateur d'entiers automatique et à l'extrait Roll d' ici .

Explication ici , TIO le formate plus facilement

Réponse bonus:

Brain-Flak compressé 583 octets

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

Essayez-le en ligne!

(Notez que le lien ci-dessus ne fonctionne pas parce que TIO n'a pas d'interpréteur Brain-Flak compressé. Vous pouvez trouver un transpilateur vers Brain-Flak ici )

J'ai vérifié que cela est valide en transpilant vers Brain-Flak à l'aide de cet outil, maintenant suffisamment efficace pour que le délai d'attente soit peu probable.

Kamil Drakari
la source
4
Golf pour la première fois à Brain-Flak, et le résultat est-ce? Sensationnel.
Erik the Outgolfer
Vous pouvez toujours remplacer <>(<()>)par (<>). Vous pouvez également passer (<>{}<>)(<()>)à(<(<>{}<>)>)
DJMcMayhem
1
@JoKing Je ne saurais pas comment, j'ai à peine réussi à extraire le rouleau à la fin de la boucle au lieu d'en avoir un supplémentaire dans chaque bloc If
Kamil Drakari
1
C'est au-delà du golf. C'est de la pure folie. Toutes nos félicitations !
Arthur Attout
1
@JoKing Le changement a été à la fois plus facile et plus efficace que prévu, et maintenant inclus dans la réponse
Kamil Drakari
7

Retina 0.8.2 , 103 98 octets

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

Essayez-le en ligne! Le lien inclut des cas de test. Edit: enregistré 5 octets avec l'inspiration de @MartinEnder. Explication:

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

Mettez un ;crochet après chaque parenthèse fermée et changez-les tous en parenthèses ouvertes, et changez également le |s en ;s

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

Comptez le nombre de parenthèses ouvertes inégalées et ajoutez autant de ;s.

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

Copiez chaque support d'ouverture dans sa correspondance ;.

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

Retournez les crochets copiés et supprimez le ;s.

Neil
la source
1
Vous pouvez éviter toutes les barres d'échappement si vous traduisez |quelque chose comme !. Il ne serait pas même octets frais si vous traduisez >-}à <-{(qui je pense donne zpour |).
Martin Ender
@MartinEnder Je ne suis pas sûr de comprendre votre point de vue sur le zmais j'ai quand même trouvé un moyen de raser quelques octets de plus.
Neil
5

TIS , 670 666 octets

-4 octets pour sauter en avant pour sauter en arrière

Code:

@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

Disposition:

6 3
CCCCCCCCCCCCCCCCSC
I0 ASCII -
O0 ASCII -

Essayez-le en ligne!

Je doute que ce soit le plus petit, mais je ne vois pas de moyen de le réduire. Malheureusement, tous les NOPs semblent nécessaires pour le moment, et je ne peux pas mettre la pile où @14est actuellement en raison de la lecture de ANYdans @11.

La structure de cette solution est la suivante:

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

En voyant une accolade ouverte, l'ouverture est envoyée le long de la colonne de gauche pour être sortie, et la fermeture est envoyée le long de la colonne de droite à la pile.

En voyant une accolade fermée, l'ouverture et la fermeture sont toutes deux envoyées le long de la colonne de gauche pour être sorties.

En voyant un tuyau, la pile est sautée et envoyée en sortie.

Lors de l'EOF, @1commencera la lecture de @2, au lieu du flux d'entrée de @0. @2produit un flux sans fin de tuyaux, de sorte que la pile sera drainée.

Une fois l'entrée et la pile épuisées, le programme s'arrête.

Avertissement: en raison des limitations de TIS, la taille de la pile est limitée à 15. Si quelque chose est imbriqué plus profondément que cela, cette implémentation produira un résultat incorrect.

Phlarx
la source
4

JavaScript (ES6), 107 octets

Prend l'entrée comme un tableau de caractères. Renvoie une chaîne.

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

Essayez-le en ligne!

Arnauld
la source
102 octets en renvoyant également un tableau de caractères.
Shaggy
@Shaggy Merci! Mais est-il vraiment permis de renvoyer des chaînes de 1 caractère et 2 caractères mélangées ensemble?
Arnauld
Hmm ... ouais, peut-être que ça le pousse sur la sortie "permissive".
Shaggy
@DJMcMayhem Pourriez-vous s'il vous plaît jeter un œil au nouveau format de sortie et nous faire savoir si cela est acceptable?
Arnauld
1
@arnauld Huh, pour une raison qui ne m'a pas piqué. Je pense que je dirais non. Un tableau de caractères ou une chaîne sont tous deux des formats standard, mais un tableau de chaînes ne me semble pas valide
DJMcMayhem
3

Rubis , 104 octets

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

Il s'agit d'un programme complet qui sort sur la console. (i<5?a:$>)<<r[-i]doit être l'un des golfs les plus cool que j'aie jamais fait.

Essayez-le en ligne!

Ruby , 106 octets

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

Ceci est ma première solution. Une fonction lambda anonyme qui prend et renvoie des chaînes.

Essayez-le en ligne!

MegaTom
la source
3

Brain-Flak , 606 548 496 418 394 390 octets

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

Essayez-le en ligne!

J'ai commencé par jouer à la réponse de Kamil Drakari , mais cela m'a échappé au point où j'ai décidé de l'afficher comme une réponse distincte.

Explication:

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

Et bien sûr ...

Brain-Flak compressé, 285 octets:

{(((}|||(>|(((((((([()|)))||}|}|})|()||((()|))|)|}}||}[)||({<((}>}[)||||){[)|(<}|||}>|}><}||{(}(}|>|(>||}(}>|(>|(((((((([()|)))||}|}|})|()||((()|)|})|}||}|({<((}>}[)||||[)|{)(<}|||}>|}>|{(<(}(<)||>(}|<{(}>|>|||||>{(}>|>||}(})>|{}(}>|((>|||}}>(<(}(<)||><{(}>|>|||||>{(}>|>|}>|}{(}}>|>|>
Jo King
la source
1
Golf très impressionnant! Je suis déçu de ne pas l'avoir remarqué plus tôt, je devrai m'y plonger plus tard pour comprendre comment cela fonctionne.
Kamil Drakari
2

Java 10, 424 octets

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

C'est un peu long, mais je n'arrivais pas à comprendre comment le raccourcir davantage. C'est un beau défi cependant.

Essayez-le en ligne ici .

Version non golfée:

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
la source
2

Python 2, 188 184 184 180 177 174 173 octets

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

Enregistré 4 octets grâce à DJMcMayhem.
Essayez-le en ligne!


la source
180
DJMcMayhem
168 octets en jouant avec l'avant-dernière ligne
DJMcMayhem
@DJMcMayhem Cela ne fonctionne que si sse termine vide. Sinon, vous vous retrouvez avec les caractères supplémentaires à la mauvaise extrémité.
2

Python 3 , 122 octets

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

Essayez-le en ligne!

RootTwo
la source
1

Haskell , 152 octets

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)

Essayez-le en ligne! ou vérifiez tous les cas de test . pimplémente un analyseur récursif, qui pourrait être trop efficace pour la grammaire simple.

Laikoni
la source
1
Belle fonction mpour trouver le support correspondant.
nimi
1

Python 2 , 244 octets

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

Essayez-le en ligne!

Il a fallu plus d'une heure ou deux pour que cela fonctionne ...

Erik le Outgolfer
la source