¿Puedes hacer que mi terminal sea menos aburrida?

11

Las terminales son muy aburridas en estos días. Solían verse así:

Terminal 1 Terminal 2

Ahora son simplemente sosos, aburridos y en blanco y negro. ¡Quiero que me escribas un programa que hará que mi terminal vuelva a ser colorida!

Descripción

Tome este ejemplo de código Ruby:

Código de ejemplo

La mayoría de los terminales de Linux admiten estas secuencias de escape ( \esignifica el carácter de escape), y Windows puede admitirlas con ANSICON . Aquí está la sintaxis de la secuencia de escape específica que puede cambiar el texto o el color de fondo de una cadena:

\e[{{COLOR}}m

donde \erepresenta el carácter de escape ( 0x1Ben ASCII) y {{COLOR}}se reemplaza por el número del color que desea usar (más detalles sobre eso más adelante). El texto que viene después de esta secuencia de escape se formateará como se indica y un valor de 0restablecerá todo el formato.

Su desafío es tomar una cadena que especifique algún texto que pueda contener color, y generar una versión colorida del mismo.

De entrada y salida

El texto normal funciona igual que el normal y se imprime literalmente. Por ejemplo, la entrada wafflesproduce la misma salida, sin color especial.

La sintaxis para especificar un color es similar a la sintaxis de Wikipedia . Por ejemplo, para colorear las palabras "el color rojo" en rojo en la oración This is the color red!, la entrada sería:

This is {{red|the color red}}!

Los colores de fondo también funcionan. Si quisieras letras negras sobre un fondo blanco, usarías esto:

{{black|white|This text is black on white}}

Para obtener solo un color de fondo, omita el primer plano:

{{|red|This text has a red background}}

Especificación

Dos llaves abiertas siempre especifican el comienzo de una directiva de color . Dos llaves de cierre especifican el final. Los corchetes siempre coincidirán; nunca habrá un {{sin un correspondiente }}, y un }}nunca vendrá antes de su correspondiente {{. Estas directivas de color no estarán anidadas, y {{nunca aparecerá dentro de una directiva de color.

Dentro de una directiva de color, siempre habrá uno o dos |símbolos. Si hay uno, el texto anterior es el color de primer plano y el texto posterior es la cadena que se mostrará en ese color. Si hay dos, el texto antes del primero es el color de primer plano, el texto después del primero pero antes del segundo es el color de fondo, y el texto después del segundo es la cadena a mostrar. Estas barras verticales pueden existir fuera de una directiva de color y deben imprimirse literalmente.

El color de primer plano o el color de fondo (pero no ambos) pueden estar vacíos, en cuyo caso debe dejarlos como predeterminados. La cadena final (la que sale) nunca estará vacía.

Estas son las instrucciones para generar texto de un determinado color:

  • Una secuencia de color se define en la sección "Descripción". Por ejemplo, una secuencia de color de 42 sería "\e[42m".

  • Para establecer un color, imprima la secuencia de colores del número que se determina a continuación:

     Color name   | Color sequence number (foreground / background)
    --------------+----------
     black        | 30 / 40
     red          | 31 / 41
     green        | 32 / 42
     yellow       | 33 / 43
     blue         | 34 / 44
     magenta      | 35 / 45
     cyan         | 36 / 46
     lightgray    | 37 / 47
     darkgray     | 90 / 100
     lightred     | 91 / 101
     lightgreen   | 92 / 102
     lightyellow  | 93 / 103
     lightblue    | 94 / 104
     lightmagenta | 95 / 105
     lightcyan    | 96 / 106
     white        | 97 / 107
    
  • Los nombres de color distinguen entre mayúsculas y minúsculas, y nunca se proporcionará un nombre de color no válido. No tiene que manejar cosas como REDo lightgrey(deletreadas con un e).

  • Después de imprimir una secuencia de colores, se aplicará a todo el texto que le sigue. Para finalizar una secuencia de color (restablecer el color predeterminado), envíe una secuencia de color de 0( "\e[0m").

Caso de prueba

 {{|yellow|     }}
{{|yellow| }}     {{|yellow| }}
{{|yellow| }} {{red|'}} {{red|'}} {{|yellow| }}
{{|yellow| }} \_/ {{|yellow| }}
{{|yellow| }}     {{|yellow| }}
 {{|yellow|     }}

Esto debería mostrar una cara sonriente ... con malvados ojos rojos.

Reglas

  • No puede utilizar ninguna biblioteca o función de su lenguaje de programación para analizar automáticamente un color. Esto significa que debe ser usted quien determine qué "red"significa; no puede hacer que una biblioteca lo haga automáticamente por usted.

  • Este es el , ¡así que el código más corto en bytes ganará!

Pomo de la puerta
fuente
En realidad se supone que es una terminal? ¿O simplemente un visor de texto colorido? ¿Se supone que debe ejecutar comandos?
Nathan Merrill
Me resulta difícil probar esto. Todo lo que envío a STDOUT usando la sintaxis especificada viene en texto plano. Mi perfil de bash usa un indicador de color, por lo que intenté robar \n\[\e[32m\]\w\n\[\e[0m\]> (nombre de directorio verde, indicador simple en la siguiente línea), pero no puedo hacer que funcione desde un programa (hasta ahora he intentado con Python y Java). ¿Algunas ideas?
Geobits
@Geobits Prueba echo -e "\e[31mtest\e[0m".
Pomo de la puerta
44
Creo que lo disfrutarías lolcat.
Anko
1
Creo que you, en sentido figurado, quiere decir your program(en oposición a una llamada a una función de biblioteca), y que toma determineen el sentido de figure out, no como en choose. Es decir, es su programa el que debe manejar la asignación: Cadena ("rojo") | -> Entero (31). redes solo 31porque él lo dice, que la información debe integrarse en el programa. Aunque se puede argumentar exactamente lo que contaría como your program: ¿podemos usar funciones de manipulación de cadenas de propósito general? - no hagas trampa / abuso descaradamente.
blutorange

Respuestas:

6

Rubí, 205 189 188 186 185 182 174 170 165 161 159 154 bytes

Poner largas cadenas de nombres de colores en su código no parece lo suficientemente nerd.

Abajo a 170 en parte gracias a rubik. ¡Ahora las barras de desplazamiento se han ido!

Una mejora obvia y una no tan obvia, gracias a la respuesta flexible, sin la mejora, ¡no habría revisado esto!

Ya no, guardé 4 bytes con #sum. No era mi intención, pero acabo de notar que esta solución tampoco distingue entre mayúsculas y minúsculas. Se procesa felizmente {{RED|Red text}}.

Volcado hexadecimal:

0000000: 7a3d 2d3e 6a7b 693d 2240 3054 2d44 1547  z=->j{i="@0T-D.G
0000010: 5155 0034 3256 2f46 1749 0b22 2e69 6e64  QU.42V/F.I.".ind
0000020: 6578 2028 415b 6a5d 2e74 6f5f 732e 7375  ex (A[j].to_s.su
0000030: 6d25 3839 292e 6368 723b 692b 3d69 3e39  m%89).chr;i+=i>9
0000040: 3f38 303a 3330 7d0a 243e 3c3c 243c 2e72  ?80:30}.$><<$<.r
0000050: 6561 642e 6773 7562 282f 7b7b 282e 2a3f  ead.gsub(/{{(.*?
0000060: 297d 7d2f 297b 413d 2431 2e73 706c 6974  )}}/){A=$1.split
0000070: 277c 273b 221b 5b25 693b 2569 6d23 7b41  '|';".[%i;%im#{A
0000080: 2e70 6f70 7d1b 5b30 6d22 255b 7a5b 305d  .pop}.[0m"%[z[0]
0000090: 2c31 302b 7a5b 315d 5d7d                 ,10+z[1]]}

Puedes convertirlo con xxd -r hex.dump.

El programa con todos los caracteres no imprimibles escapó con fines de referencia:

z=->j{i="@0T-D\x15GQU\x0042V/F\x17I\v".index (A[j].to_s.sum%89).chr;i+=i>9?80:30}
$><<$<.read.gsub(/{{(.*?)}}/){A=$1.split'|';"\x1b[%i;%im#{A.pop}\x1b[0m"%[z[0],10+z[1]]}

Esa es una línea. Úselo así

ruby colors.rb -W0 < input.txt

La -W0bandera suprime las advertencias que se enviarían a lo stderrcontrario. Sin embargo, el programa funciona bien sin ningún indicador.

Salida:

salida

blutorange
fuente
1
Ah, tuve la misma idea, ¡pero me ganaste! Creo que se podría ahorrar un char con la base 35, el módulo 98 y xor 1. La cadena sería: '1?IYU_N[(\x0c\x16&",\x1f\x01'. Sin embargo, mi cuerda tiene 16 de longitud. Veo que el tuyo es 18, así que probablemente tengas que adaptarte.
rubik
Gracias. Los dos bytes adicionales están ahí para admitir el código de color 39/49, que establece el color predeterminado de fondo / fondo. Pero gracias por el consejo, actualmente estoy en el trabajo y lo pensaré un poco más cuando regrese a casa.
blutorange
1
Bueno, observé que las únicas bases que puedes usar son 35 y 36 (al menos la int()función de Python no puede superar 36). Luego probé todas las combinaciones para el módulo (de 2 a 10000, pero en teoría uno podría ampliar la búsqueda a todo Unicode) y para el xor, que mantuve pequeño (1 a 9). Luego consideré resultados aceptables solo aquellos que no contenían caracteres duplicados.
rubik
Sí, eso es más o menos lo que hice también. Originalmente, me limitaba a los caracteres imprimibles, porque eso le da menos dolor de cabeza y se ve mejor. Pero como ya estoy usando el byte 0x1e en lugar de la secuencia de escape, también podría usar más caracteres no imprimibles. Para lograr caracteres imprimibles, solía x.to_i(base)%mod+offset. Luego reemplacé el +con ^, porque bueno, se ve más fresco. Aparte de eso, es innecesario. Descarte ^99y cambie <<a +guardado para más bytes. Gracias por el consejo, no me habría dado cuenta de lo contrario!
blutorange
4

Ruby, 329 bytes.

h={};(y=0..15).each{|i|h[%w(black red green yellow blue magenta cyan lightgray darkgray lightred lightgreen lightyellow lightblue lightmagenta lightcyan white)[i]]=y.map{|j|[30,40].map{|k|k+i%8+i/8*60}}[i]}
loop{puts gets.gsub(/{{.+?}}/){|x|"\e[#{h[(g=x.scan(/[^{}|]+/))[0]][0]}m#{(g[2]? "\e[#{h[g[1]][1]}m":'')}#{g.last}\e[0m"}}
Alex Deva
fuente
¿Qué versión de Ruby necesito para ejecutar esto? Solía ruby 2.1.2p95y tirar de error: undefined method 'gsub' for nil:NilClass (NoMethodError) .
Ray
Hola @ Ray, funciona en 2.0.0-p451. No lo intenté en 2.1.2. Aquí funciona como un script y aquí funciona en irb .
Alex Deva
Funciona cuando ingresas texto manualmente. Si lo hace ruby colors.rb < input.txt, seguirá en bucle después de que se haya leído toda la entrada. Luego getsregresa nil, que no posee un #gsubmétodo, lo que genera un error. Usar en $><<$<.readlugar de loop{puts gets, también es más corto; )
blutorange
¿Acabo de probar este script con la carita feliz (ver pregunta y la imagen de mi publicación), y no hay un borde amarillo alrededor de la carita feliz?
blutorange
4

Flex (lexer) - 226 197 192 182 168 (o 166)

Para bajarlo a 166, cambie el carácter \33a un carácter de escape real.

 int z;p(i){printf("\33[%dm",i);}
%%
"{{" z=2;
[a-z]*\| if(!z)REJECT;~-yyleng&&p("062q00t03058ns7uo0p90r4"[*(int*)&yytext[yyleng>7?4:0]%131%27]-10*z);z--;
"}}" p(z=0);

Compilar y ejecutar:

$ flex -o colour.c colour.l
$ gcc -o colour colour.c -lfl
$ ./colour < input
rici
fuente
3

Python - 351

import re,sys
R=range
E=lambda n,d=0:'\033[%dm'%(T[n]+d)if n else''
def P(m):f,b,t=m.groups();return'%s%s%s\033[0m'%(E(f),E(b,10),t)
x='!red!green!yellow!blue!magenta!cyan'.replace
T=dict(zip(('black'+x('!',' ')+' lightgray darkgray'+x('!',' light')+' white').split(),R(30,38)+R(90,98)))
print re.sub(r'{{(\w+)?\|?(\w+)?\|?(.+?)}}',P,sys.stdin.read())
Rayo
fuente
1

Cobra - 496

Casi podría ser una sola declaración impresa.

use System.Text.RegularExpressions
class P
    def main
        print Regex.replace(Console.readLine,r'\{\{('+(l=List<of String>(((m=' black red green yellow blue magenta cyan'.split).join(' ')+' lightgray darkgray'+m.join(' light')+' white').split))[1:].join('|')+r')?\|?('+l[1:].join('|')+r')?\|(.*?)\}\}',do(a as Match))
            return if(x=l.indexOf('[a.groups[1]]'),r'\e['+'[if(x>8,x+81,x+29)]m','')+if(y=l.indexOf('[a.groups[2]]'),r'\e['+'[if(y>8,y+91,y+39)]m','')+'[a.groups[3]]'+if(x+y,r'\e[0m','')
Οurous
fuente
1

Python, 561

Lee el texto para formatear desde stdin.

import re,sys
def p(f,b,t):
    p=''
    m='\033[%dm'
    if f!=0:p+=m%f
    if b!=0:p+=m%b
    return p+t+m%0
def c(n,b=0):
    s='black:30#red:31#green:32#yellow:33#blue:34#magenta:35#cyan:36#lightgray:37#darkgray:90#lightred:91#lightgreen:92#lightyellow:93#lightblue:94#lightmagenta:95#lightcyan:96#white:97'
    r=0
    for i in s.split('#'):
        (t,c)=i.split(':')
        if t==n:
            r=int(c)
            if b==1:r+=10
    return r
def r(m):
    i=m.groups()
    f=b=0
    if i[0]!='':f=c(i[0])
    if i[1]!=None:b=c(i[1],1)
    return p(f,b,i[2])
print re.sub('{{(\w*)\|(?:(\w*)\|)?([^}]+)}}',r,sys.stdin.read())
Sammitch
fuente
2
Es demasiado detallado para tener is not Noneen un codegolf. Puedes usar !=None, por ejemplo.
Ray
Además, en def p(f,b,t), su código arrojará un ZeroDivisionError. Cualquier cosa mod 0 es imposible.
Decaimiento Beta
@BetaDecay no es un número entero que se está modulando, es una cadena que se está formateando.
Sammitch
Recibo errores de sintaxis no válidos al re.subejecutar esto
ArtOfCode
(comentario tardío) ¿ Funciona este código de 499 bytes ?
Erik the Outgolfer