Espandi le scorie cerebrali compresse


26

Questa sfida è stata pubblicata come parte della sfida LotM di aprile 2018 , nonché per il 2 ° compleanno di Brain-flak


Stavo pensando a quale sarebbe stato il modo più efficace per codificare i programmi di scoria cerebrale. La cosa ovvia da fare, dato che ci sono solo 8 caratteri validi, è mappare ogni personaggio in una sequenza a 3 bit. Questo è certamente molto efficace, ma è ancora molto ridondante. Ci sono alcune caratteristiche del codice brain-flak che potremmo sfruttare per abbreviare la codifica.

  • I nilad, che sono tutti rappresentati da 2 parentesi abbinate, agiscono in realtà come una singola unità di informazioni anziché 2. Se sostituiamo ogni parentesi con un carattere a byte singolo, questo renderebbe le codifiche molto più piccole senza perdere alcun dato.

  • Questo è meno ovvio, ma anche i byte di chiusura delle monadi sono ridondanti. Pensi di poter indovinare cosa '?'rappresentano i personaggi nel seguente frammento?

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

    Se assumiamo che l'input sia un codice valido per il cervello, allora c'è solo un'opzione per ciascuno di quei punti interrogativi. Ciò significa che possiamo usare inequivocabilmente un carattere di monade vicino per rappresentare ogni parentesi di chiusura. Ciò ha l'ulteriore vantaggio di mantenere piccolo il set di caratteri, il che sarebbe di grande aiuto se volessimo usare una codifica huffman. Poiché il personaggio della monade stretta sarà molto probabilmente il personaggio più comune con un ampio margine, potrebbe essere rappresentato da un singolo bit, che è estremamente efficiente.

Questi due trucchi ci permetteranno di comprimere il codice del cervello-flak tramite il seguente algoritmo:

  1. Sostituisci ogni staffa di chiusura di una monade con |. O in altre parole, sostituisci ogni barra di chiusura che non è preceduta dalla relativa apertura con una barra. Così...

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

    potrebbe diventare

    (({}|<(()()()||{}|
    
  2. Sostituisci ogni nilad con la sua staffa di chiusura. Pertanto, le parentesi corrispondenti che non contengono nulla utilizzano la seguente mappatura:

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

    Ora il nostro ultimo esempio diventa:

    ((}|<()))||}|
    
  3. Rimuovi i |caratteri finali . Poiché sappiamo che il numero totale di barre dovrebbe essere uguale al numero totale di ({[<caratteri, se alla fine mancano delle barre, possiamo dedurle. Quindi un esempio come:

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

    potrebbe diventare

    ({(}|(}[)
    

La tua sfida per oggi è invertire questo processo.

Data una stringa di scoria cerebrale compressa contenente solo i caratteri (){}[]<>|, espanderla nel codice originale di scoria cerebrale. Si può presumere che l'input si espanderà sempre fino a essere valido. Ciò significa che nessun prefisso dell'input conterrà mai più |di ({[<caratteri.

L'input non conterrà |caratteri finali . Questi devono essere dedotti dal contesto.

Come al solito, è possibile inviare un programma completo o una funzione e i formati di input / output sono permissivi. E poiché si tratta di un , il tuo codice verrà assegnato in base alla lunghezza del codice sorgente in byte, più piccolo è il punteggio, migliore è.

Casi test

Ecco alcuni casi di test. Se desideri di più, puoi generare i tuoi casi di test con questo script Python e il Wiki Brain-Flak , che è la fonte da cui provengono la maggior parte di questi casi di test.

#Compressed code
#Original code

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


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

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


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

4
genio. assolutamente geniale. Dovresti creare una lingua derivata.
NH.

8
@NH. Personalmente, mi sento come se le lingue che differiscono solo nella codifica siano davvero noiose.
DJMcMayhem

1
@dj ma questo richiederebbe meno byte e quindi sarebbe meglio per giocare a golf.
NH.

5
Brain-Flak non è stato progettato per essere bravo a giocare a golf.
DJMcMayhem

Risposte:


32

Brain-Flak , 952 916 818 byte

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

360 byte salvati calcolando le parentesi contrapposte relativamente anziché da zero (ad es. ')'= '(' + 1Anziché (((5 * 2) * 2) * 2) + 1)

Salvato 34 byte con alcune sostituzioni dirette da DJMcMayhem

10 byte salvati sovrapponendo il >]}codice di gestione

Salvato 118 byte deducendo i rotoli

40 byte salvati sfruttando lo stack vuoto per semplificare il primo lancio

48 byte salvati contrassegnando EOF con -1, consentendo un roll code più conciso

Ho salvato 36 byte usando la logica uguale allo stock anziché la mia

Risparmiato 98 byte grazie a Jo King che ha trovato un modo più efficiente per creare l'output

Provalo online!

Giocare a golf per la prima volta in Brain-Flak, quindi probabilmente ci sono alcuni miglioramenti davvero grandi, ma funziona. Un sacco di copia / incolla per gestire ogni tipo di parentesi, e grande grazie al generatore di numeri interi automatico e allo snippet di rollio da qui .

Spiegazione qui , TIO lo formatta più facilmente

Risposta bonus:

Compressed Brain-Flak 583 byte

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

Provalo online!

(Si noti che il link di cui sopra non eseguito perché TIO non ha un interprete compressa Brain-Flak. È possibile trovare una transpiler a Brain-Flak qui )

Ho verificato che ciò sia valido eseguendo la traspilazione in Brain-Flak usando questo strumento, ora abbastanza efficiente da rendere improbabile il timeout.


4
Giocare a golf per la prima volta in Brain-Flak, e il risultato è questo? Wow.
Erik the Outgolfer,

Puoi sempre sostituire <>(<()>)con (<>). Inoltre, puoi passare (<>{}<>)(<()>)a(<(<>{}<>)>)
DJMcMayhem

1
@JoKing Non saprei come, a malapena sono riuscito a estrarre il Roll alla fine del loop invece di averne uno in più in ogni blocco If
Kamil Drakari

1
Questo va oltre il golf .. Questa è pura follia. Complimenti!
Arthur Attout,

1
@JoKing La modifica è stata sia più semplice che più efficace di quanto mi aspettassi, e ora inclusa nella risposta
Kamil Drakari,

7

Retina 0.8.2 , 103 98 byte

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

Provalo online! Il link include casi di test. Modifica: salvato 5 byte con ispirazione da @MartinEnder. Spiegazione:

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

Metti un ;dopo ogni parentesi chiusa e cambiali tutti in parentesi aperte, e cambia anche la |s in ;s.

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

Conta il numero di parentesi aperte senza eguali e aggiungi tante ;s.

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

Copia ogni parentesi aperta nella sua corrispondenza ;.

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

Capovolgi le parentesi copiate ed elimina ;i messaggi.


1
Potresti evitare tutte le barre di escape se traduci |in qualcosa di simile !. Non sarebbe nemmeno costare byte se si traduce >-}a <-{(che credo dà zper |).
Martin Ender,

@MartinEnder Non sono sicuro di aver capito il tuo punto a riguardo, zma mi è venuto in mente di radere qualche byte in più comunque.
Neil,

5

TIS , 670 666 byte

-4 byte per saltare in avanti per tornare indietro

Codice:

@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

Disposizione:

6 3
CCCCCCCCCCCCCCCCSC
I0 ASCII -
O0 ASCII -

Provalo online!

Dubito che questo sia il più piccolo, ma non vedo un modo per ridurlo. Sfortunatamente, tutti NOPi messaggi sembrano necessari per il timing e non riesco a mettere lo stack dove @14attualmente è a causa della lettura da ANYdentro @11.

La struttura di questa soluzione è la seguente:

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

Dopo aver visto un controvento aperto, l'apertura viene inviata lungo la colonna di sinistra per essere emessa e la chiusura viene inviata lungo la colonna di destra allo stack.

Dopo aver visto una parentesi chiusa, sia open che close vengono inviati lungo la colonna di sinistra per essere emessi.

Dopo aver visto una pipe, lo stack viene visualizzato e inviato all'output.

Su EOF, @1inizierà la lettura da @2, anziché il flusso di input da @0. @2produce un flusso infinito di tubi, quindi lo stack verrà scaricato.

Una volta esauriti sia l'input che lo stack, il programma si interrompe.

Avvertenza: a causa delle limitazioni di TIS, la dimensione dello stack è limitata a 15. Se qualcosa è nidificato più in profondità, questa implementazione produrrà un risultato errato.


4

JavaScript (ES6), 107 byte

Accetta input come una matrice di caratteri. Restituisce una stringa.

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

Provalo online!


102 byte restituendo anche una matrice di caratteri.
Shaggy,

@Shaggy Grazie! Ma è davvero consentito restituire stringhe di 1 carattere e 2 caratteri mescolate insieme?
Arnauld,

Hmm ... sì, forse lo sta spingendo sull'uscita "permissiva".
Shaggy,

@DJMcMayhem Per favore, dai un'occhiata al nuovo formato di output e facci sapere se è accettabile?
Arnauld,

1
@arnauld Huh, per qualche motivo che non mi ha fatto rumore. Penso che direi di no. Una matrice di caratteri o una stringa sono entrambi formati standard, ma una matrice di stringhe non mi sembra valida
DJMcMayhem


3

Rubino , 104 byte

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

Questo è un programma completo che esce sulla console. (i<5?a:$>)<<r[-i]deve essere uno dei golf più belli che abbia mai fatto.

Provalo online!

Rubino , 106 byte

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

Questa è la mia prima soluzione. Una funzione lambda anonima che accetta e restituisce stringhe.

Provalo online!


3

Brain-Flak , 606 548 496 418 394 390 byte

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

Provalo online!

Ho iniziato a giocare golfando la risposta di Kamil Drakari , ma è andata via da me al punto in cui ho deciso di pubblicarla come risposta separata.

Spiegazione:

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

Ed ovviamente...

Compressed Brain-Flak, 285 byte:

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

1
Giocare a golf molto impressionante! Sono deluso di me stesso per non essermene accorto prima, dovrò approfondire più tardi per capire come funziona.
Kamil Drakari,

2

Java 10, 424 byte

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

È un po 'lungo, ma non sono riuscito a capire come accorciarlo ulteriormente. Questa è comunque una bella sfida.

Provalo online qui .

Versione non golfata:

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
}

2

Python 2, 188 184 180 177 174 173 byte

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

Salvato 4 byte grazie a DJMcMayhem.
Provalo online!



168 byte giocando con la seconda all'ultima riga
DJMcMayhem

@DJMcMayhem Funziona solo se sfinisce vuoto. Altrimenti, finisci con i personaggi extra dalla parte sbagliata.


1

Haskell , 152 byte

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)

Provalo online! o verifica tutti i casi di test . pimplementa un parser ricorsivo, che potrebbe essere over kill per la semplice grammatica.


1
Bella funzione mper trovare la parentesi corrispondente.
nimi,

1

Python 2 , 244 byte

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

Provalo online!

Ci sono voluti più di un'ora o due per farlo funzionare ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.