Marquesina de desplazamiento

13

Después de mi entrada al mundo ofuscado Hello World , pensé que sería divertido compartir el código subyacente. Pero, ¿por qué solo mostrar el código, hagámoslo también un golf!

Desafío

Escriba un script que desplace una cadena por la terminal, de derecha a izquierda, estableciéndose en el lado izquierdo.

Entrada

Toma una cuerda como argumento.

Resultado

Imprime la marquesina de desplazamiento en STDOUT. Ancho máximo de ~ 50 caracteres. Comienza con 0 o 1 caracteres que se muestran. Algún espacio entre letras mientras se desplaza. Se detiene cuando se establece (sin espacio adicional entre caracteres de palabras). Desplazamiento lento, pero no demasiado lento (<1s por iteración).

Ejemplo

Ejecutar script con arg 'Hello World'

                                                   H

luego

                H    e    l    l    o         W    o

luego

H    e    l    l    o          W    o    r    l    d

luego

Hell    o         W    o    r    l    d

finalmente

Hello World

Para ver un ejemplo en ejecución, pruebe mi código del desafío "Hello World". Eventualmente publicaré el mío. Actualmente tiene 202 caracteres en Perl. Ahora que hay algunos competidores, he publicado el mío en las respuestas.

Ganador

No quiero que las restricciones sean absolutas, por eso las dejé un poco vagas. El guión más corto que siga el espíritu de mi original ganará.

Notas

Este juego asume el xtermmedio ambiente. Si otro entorno resulta útil, solo se compararán entornos similares y se puede declarar un ganador por separado para cada uno.

Anexo (25 de abril de 2012)

Para abordar algunos problemas incipientes, estoy tomando una decisión. Su recuento de caracteres debe incluir el código necesario para:

  1. Flush STDOUT (Mirándote Ruby)
  2. Implementar sleepcon un retraso de <1s (Mirándote, Perl)

Esto se puede hacer cuando la línea de comando cambia a un intérprete, pero esos caracteres cuentan en el total (sin espacios en blanco circundantes).

Joel Berger
fuente
Estoy un poco preocupado por comportamientos terminales para este tipo de cosas ... xterm, vt102, ...?
dmckee --- ex-gatito moderador
Estoy asumiendo xterm, pero no creo que importe demasiado. ¿Quizás no entiendo tu preocupación?
Joel Berger
Estos trucos generalmente se producen al depender de la forma en que varios terminales manejan algunos de los caracteres que no se imprimen, y los terminales diferían en lo que podían hacer y en qué secuencias producían los efectos. Para la reproducibilidad, podría ser bueno tener un entorno de terminal específico.
dmckee --- ex-gatito moderador
1
Te servirá bien si alguien publica una respuesta realmente corta que depende de un terminal oscuro para el que no tienes un emulador, pero está bien.
dmckee --- ex-gatito moderador
ok creo que lo tengo ahora. Gracias por los pensamientos :-)
Joel Berger

Respuestas:

5

pitón 2 - 146 caracteres

editar: lo convirtió en una función en lugar de ingresarlo a través de stdin. El primer argumento es la cadena, y el segundo argumento es la longitud que desea que sea. entonces la invocación seríaf('Hello World', 50) . También lo hice mucho más suave; cuando cada personaje 'aterrizó' hubo una pausa incómoda

import os,time
def f(x,n):
 y=' '*n+'  '.join(x);z=0
 while y:w=y[0]==x[z];y=y[1+w:];z+=w;os.system('cls');print((x[:z]+y)[:n]);time.sleep(0.1)

viejo, 158 caracteres:

import os,time,sys
x=' '.join(sys.argv[1:])
y=' '*50+'  '.join(x)
z=0
while y:z+=y[0]==x[z];y=y[1:];os.system('cls');print((x[:z]+y)[:50]);time.sleep(0.1)
Chaqueta de sport
fuente
Usando bash (al menos en una instalación reciente de MacOSX y CentOS), el comando de shell utilizado para borrar la pantalla del terminal debe ser 'clear' no 'cls'.
Paolo
'cls' para windows, 'clear' para las compilaciones de OSX / Linux, supongo
Blazer
Sería útil aconsejar cómo iniciar el programa para aquellos que no están lidiando con python a diario. Inicia Python. pegue el código, la llamada f("Hello World, 40)funcionó para mí.
usuario desconocido
@user I um. Puse la invocación allí?
Blazer
4

Ruby, 93 91 89 caracteres

u="\1";s=u*50+[*$*[0].chars]*(u*3);$><<s.tr(u," ")[0,50]+" \r"while s.sub!u,""*sleep(0.1)

El texto a mostrar se debe dar como argumento de línea de comando, p. Ej.

ruby marquee.rb "Hello World"

para el ejemplo que se muestra arriba. Desafortunadamente no puedo mostrar la animación aquí, así que debes probar el código tú mismo.

Versión previa:

s=" "*67+[*$*[0].chars]*"   ";(s.size*3/4).times{|j|s[j/3]='';$><<s[16,50]+" \r";sleep 0.1}
Howard
fuente
Tamaño impresionante Sin embargo, no es muy sencillo, ¿soy yo (estoy en una máquina bastante baja en este momento) o es así como funciona el código?
Joel Berger
Entendido, tuve que configurarlo STDOUT.sync=true;para que se enjuague automáticamente. El equivalente de Perl es $|++. Eso es 17 caracteres adicionales, pero aún está muy por debajo del mío. ¡Pues no puedo permitir que Ruby venza a Perl! Voy a tener que trabajar. Buena esa.
Joel Berger
Si llamo ruby1.8 "Hello World", recibo, no exactamente para mi sorpresa, un error que dice:ruby1.8: No such file or directory -- Hello World (LoadError)
usuario desconocido el
@userunknown tal vez deberías poner la ruta al archivo fuente allí también: ruby foo.rb args;-)
Patrick Oscity
@padde: Sí, debería hacerlo. Lamentablemente, Howard editó su publicación sin informarme sobre su cambio. Echa un vistazo a la historia para entender mi pregunta.
Usuario desconocido
3

C, 94 83 80 173 caracteres

EDITAR: Se agregó mucho código, implementa toda la funcionalidad solicitada ahora. La constante 1e8se puede ajustar para controlar la velocidad. En mi máquina, es bastante rápido como es.
Algunos personajes seguramente se pueden guardar aquí. lpuede ser estático (guarda la inicialización), cpuede convertirse en un puntero (reemplazar b+c).

char b[99],c=1;
main(a,t,w,i,l)char**t;{
    for(l=0;b[l++]=*t[1]++;b[l++]=32);
    for(w=80;i--||
        printf("\033[F\033[K%*.*s\n",w-=l<a,a++,b,i=1e8)>l+6||
        b[++c]&&memmove(b+c-1,b+c,l););
}

Versión anterior (80 caracteres), con funcionalidad parcial:
guardado un par de caracteres reemplazándolos char**tpor int*t. Funciona bien en 32 bits ( int**tadmitiría 64 bits).

main(i,t,w)
    int*t;
{
    for(w=80;i--||printf("\033[F\033[K%*s\n",w,t[1],i=1e8)*--w;);
}
Ugoren
fuente
2

K&R C - 431 416 caracteres

Respeta el estándar en alto grado. Utiliza ncurses, por lo que debería ser en gran medida independiente de la terminal. Hay un ligero tartamudeo cuando el texto toca el costado debido a algunos trucos jugados para preservar el espacio en blanco deseado en la cadena.

La cadena a usar se debe pasar como el primer argumento en la línea de comando (y se debe escapar si contiene espacios, más aún si contiene a !como lo hizo mi cadena de prueba ( Hello, World!)).

#include <ncurses.h>
#include <unistd.h>
#define T usleep(1e5),S(l)
#define U mvprintw(23,0,"%s",l),refresh()
char l[63],*p,*q,r;
S(char*s){r=0;if(*s==32)q=s++;else{for(;*s-32||*(s+1)-32;s++); 
for(q=s;*s==32;s++);(s-q)&1?s--:usleep(1e5);}
for(r=0;*s;*q++=*s++){*s-32?r=1:0;}return r;}
main(int c,char**v){initscr();curs_set(0);for(c=0;c<62;l[c++]=32);
for(p=*++v;*p;){l[52]=*p++;U;T;U;T;U;T;}for(;T;U);getch();endwin();}

En una forma más legible y comentada:

#include <ncurses.h>
#include <unistd.h>

char l[63] /* take advantage of 0 initialization */,
  *p,*q, r;

/* Remove the first unwanted space. Unwanted means at the begining of
 * the line, all of even length blocks between non-spaces, and
 * all-bu-one of odd length blocks between non-spaces.
 *
 * Return true if the removed space occurs before a non-space character.
 */
S/*lide marquee*/(char*s){
  r=0; /* initialize the return value */
  if(*s==' '){
    q=s++;
  } else {
    /* Find the start of first block of contiguous spaces */
    for(;*s-' '||*(s+1)-' ';s++); 
    for(q=s;*s==' ';s++); /* q holds the start, s finds it's end */
    /* if this block is even length remove all, if odd, all but one */
    if( (s-q)%2 )s--; else usleep(1e5);
  }
  /* copy from s to q all the way to the end */
  for(r=0;*s;*q++=*s++){ 
    if(*s-' ')r=1; /* note if we pass a non-space */
  } 
  return r;
}

main(int c,char**v){
  initscr();curs_set(0); /* setup ncurses with invisible cursor */
  for(c=0;c<62;l[c++]=' '); /* initialize l */
  for(p=*++v;*p;){ /* load the message into the marque, skipping space */
    l[52]=*p++;
    mvprintw(23,0,"%s",l),
    refresh();
    usleep(1e5),
    S(l);
    usleep(1e5),
    S(l);
    usleep(1e5),
    S(l);
  }
  for(;usleep(1e5),S(l);mvprintw(23,0,"%s",l),refresh()); /* keeping sliding until we're done. */
  getch();
  endwin();
}
dmckee --- gatito ex moderador
fuente
Hay mucho potencial para acortar, especialmente al reemplazar ifcon operadores. Por ejemplo - if((s-q)%2)s--;else usleep(1e5);-> s-q&1?s--:usleep(1e5);(o s-=s-q&1||usleep(1e5);)
ugoren
@ugoren: Sí, y me había olvidado de reemplazar la ' 's con equivalentes numéricos.
dmckee --- ex-gatito moderador
Algunos trucos más: Reemplazar x==32con x-32(invierte el significado, por lo tanto, invierta si no), o con x<33(suponiendo que 0..31 nunca se usa). Inicialice con los valores que tiene ( for(curs_set(c=0);...). *(s+1)-> s[1]. Quite las llaves innecesarias (reemplazar ;con ,ayuda).
ugoren 01 de
2

Perl 5.13.2, 96

$_=join$;x4,$;x46,split//,pop;print substr(s/$;/ /gr,0,50)." \r"while$|=s/$;//+select'','','',.1

Robando mucho de la respuesta de @ Kevin Reid , especialmente el /rtruco disponible en los nuevos Perls.

Perl, 115

Al igual que la respuesta de @ Joel Berger , esto sería mucho más corto si pudiera usarlo sleep 1y ser lento, o pasar -MTime::HiRes=sleepla línea de comando para habilitarlo sleep.1. De lo contrario, la única forma incorporada de dormir corto select'','','',.1es bastante larga.

$|=@_=(($")x45,map{($")x4,$_}split//,pop);for(0..$#_){print@_," \r";splice@_,($_-=45)<0?0:$_/4,1;select'','','',.1}

Perl, 128

$_=$"x9 .pop;s/./    $&/g;$.=-46;$\=" \r";while($|=/./g){print substr($_,0,50);pos=++$.<0?0:$./4;s/\G.//;select'','','',.1}print

Perl, 133

$|=@_=split//,pop;for$i(reverse-$#_..50){for(@_){print$"x($j||$i),$_;($i+=$j=($i++>0)*4)>50&&last}print"    \r";$j=select'','','',.1}
efímero
fuente
sí, me mordí con mi propia regla en ese! No me di cuenta de que otros langs tendrían un sueño incorporado. Oh, bueno.
Joel Berger
Algunas sugerencias, puede eliminar el espacio después de cada uno xy la forma de bloque de mapguardará algunos.
Joel Berger
1

JavaScript 180 218 caracteres

Versión de producción:

function f(){i--&&(i>50?h=h.substr(1):h=h.replace(" ",i==16?"&nbsp;":""),document.body.innerHTML="<pre>"+h.substr(0,50)+"</pre>",setTimeout(f,99))}h=(new Array(50)).join(" ")+"HelloWorld".split("").join("   "),i=80,f()

Versión sin golf:

h=new Array(50).join(" ")+("HelloWorld".split("").join("   "));
i=80;

function f(){
        if(i--){
            if(i>50){
                h=h.substr(1);
            }else{
                h=h.replace(" ",(i==16)?"&nbsp;":"");
            }
            document.body.innerHTML="<pre>"+h.substr(0,50)+"</pre>";
            setTimeout(f,99);
        }
}
f();​

Aquí hay una demostración de jsFiddle

Nota: si intenta reproducirlo, asegúrese de que el código esté debajo del cuerpo

ajax333221
fuente
No puedo decir por la demostración, ¿se "apila" en el lado izquierdo, o simplemente lo hace a la izquierda y luego muestra la cadena final? Howard definitivamente funciona si no estás seguro.
Joel Berger
@JoelBerger the hello world tiene 4 espacios entre cada letra, cuando la h es el primer carácter, estos espacios se eliminan. Esta demo es más lenta jsfiddle.net/fYvg7/1
ajax333221
Eso está cerca, pero debes eliminar cada espacio individualmente.
Joel Berger
@JoelBerger Corregido
ajax333221 27/03/12
Bueno, odio ser una molestia, pero otro problema: el tuyo comienza con todas las letras que se muestran en lugar de ingresar una por una a la derecha.
Joel Berger
1

Perl 5.13.2, 115 caracteres

$_=$"x9 .pop=~y/ /\0/r;s/./    $&/g;print(y/\0/ /r=~/(.{50})/,"\r"),select$.,$.,$.,.02while$|=s/ (\S)/$1 /g;print$/
  • Advertencia de limpieza.
  • Se puede exprimir un poco reduciendo el espacio entre los caracteres o el espacio en blanco inicial.
  • Requiere Perl 5.13.2 o posterior debido al uso de /r.
  • La sustitución a NUL para preservar espacios no es ambigua ya que POSIX argv no es NUL-clean. Sin embargo, la sustitución del bucle convertirá cualquier otro espacio en blanco en nada (eventualmente).

Créditos

Kevin Reid
fuente
Me encanta la rbandera, la mejor adición al idioma desdestate
Joel Berger
1

bash 234

w=$1
p(){
i=$1
s=$2
p=$((50+s*3-i))
((p<s+1)) && p=$((s+1));
((p<50)) && echo -en "[20;"${p}H$3"  ";
}
clear
for i in {0..99}
do
for s in $(seq 0 ${#w})
do
p $i $s ${w:s:1} 
done
sleep .1
echo -en "[20;1H  "
done
echo -en "\b\b$w\n"

Uso:

./marquee.sh "Hello, fine marquee world"

sin golf:

#!/bin/bash
w=$1
p(){
    #si String index
    it=$1
    #it=iteration
    si=$2
    pos=$((50+(si*3)-it))
    ((pos<si+1 )) && pos=$((si+1));
    ((pos<50)) && echo -en "[20;"${pos}H$3"  ";
}
clear
for it in {0..99}
do
    for si in $(seq 0 ${#w})
    do
        p $it $si ${w:si:1} 
    done
    sleep .1
    echo -en "[20;1H   "
done
echo -en "[22;1H"
usuario desconocido
fuente
1

R, 319 caracteres

Siguiendo la filosofía del ejemplo @Blazer (d es el retraso en segundos):

f=function(x,n=50,d=0.2){
    s=strsplit(x,"")[[1]];i=1;l=length
    while (i<(n+l(s)-1)){
        if(i<=l(s))cat(rep(" ", n-i),s[1:i])
        else if((i<=n)&&(i>l(s)))cat(rep(" ", n-i),s[1:l(s)])
        else cat(paste(s[1:(i-n+1)],collapse=""),s[(i-n+2):l(s)])
        Sys.sleep(d);system("clear");i=i+1
    }
    cat(paste(s[1:l(s)],collapse=""))
}

Uso:

f("Hello World",n=20,d=0.2)
Paolo
fuente
1

Perl : 144 133

$|=@s=(($")x50,map{$_,($")x4}@i=split//,pop);{$n=0;$s[$n]ne$_?last:$n++for@i;splice@s,$n,1;print"\r",@s[0..50];sleep.1;$n!=@i&&redo}

Para obtener el sueño de <1s, aunque debe ejecutarse como:

perl -MTime::HiRes=sleep scriptname 'string to print'

Como no me declararé ganador, no discutiré sobre lo que cuenta allí o no (pero realmente no puedo hacer que Ruby gane esto ;-))

Joel Berger
fuente
4 más y cabe en un tweet: D
ajax333221
4 personajes aquí: s/' '/$"/gys/shift/pop/
efímero
Sí, los había incluido, junto con la eliminación de la pushdeclaración. Simplemente no lo había publicado todavía.
Joel Berger
0

Q, 145

No cumple exactamente con los requisitos porque la línea final elimina todos los espacios que estaban en la cadena de entrada original.

{c:2_'((!)(#)a)_'a:((l:3*(#)x)#" "),\(1_(,/)b,'x,'b:" ");{(-1 x;);system"sleep ",($)y}'[-1_c,(l-1)$d(!:)[d]except\(&)(^)d:((!)(#)q)!q:last c;y];}

Se necesitan dos argumentos, cadena de entrada y velocidad de desplazamiento

q){c:2_'((!)(#)a)_'a:((l:3*(#)x)#" "),\(1_(,/)b,'x,'b:" ");{(-1 x;);system"sleep ",($)y}'[-1_c,(l-1)$d(!:)[d]except\(&)(^)d:((!)(#)q)!q:last c;y];}["hello";0.05]
             h
            h
           h
          h  e
         h  e
        h  e
       h  e  l
      h  e  l
     h  e  l
    h  e  l  l
   h  e  l  l
  h  e  l  l
 h  e  l  l  o
h  e  l  l  o
h e  l  l  o
he  l  l  o
he l  l  o
hel  l  o
hel l  o
hell  o
hell o
hello
tmartin
fuente
desafortunadamente ese es un punto importante. Sé que los scripts de Perl podrían volverse muy pequeños sin él.
Joel Berger
0

PowerShell, 135

No es muy golfista y probablemente sea un enfoque horrible, pero estoy enfermo y realmente no puedo pensar ...

for($x="`r"+' '*50;$y-ne$x){$y=$x
write-host($x=$x-replace' ([^ ])','$1 ')-n
if(!($t++%5)){$x=$x-replace'.$',"$args"[$i++]}sleep -m 99}
Joey
fuente
0

J (116)

s(echo@((50#LF)&,)@([[i.@]&2e7)@(50&{.)@;@:(([,~#&' '@])&.>))"1([-=&0@/:@\:@:~:&0)^:(i.>:+/k)k=.50,3#~<:#s=.>2{ARGV

Toma la cadena de entrada en la línea de comando, es decir jconsole marquee.ijs 'Hello, world!'

Si no necesita borrar la pantalla, es decir, salida como esta:

H  e  l  l  o
H e  l  l  o
He  l  l  o
He l  l  o
...

está permitido, sería 12 caracteres más corto.

Explicación:

  • s.=>2{ARGV: obtener la cadena de la línea de comando
  • k.=50,3#~<:#s: la cantidad inicial de espacios en blanco agregados antes de cada personaje, 50 antes del primero y 3 antes de todos los demás. (da una matriz, '50 3 3 3 ... ')
  • ([-=&0@/:@\:@~:&0): dada una matriz, disminuye el primer elemento distinto de cero en la matriz
  • ^:(i.>:+/k): esta función se aplicó N veces, donde N es 0 hasta la suma de la cantidad de espacios en blanco agregados. (da una matriz:50 3 3 3; 49 3 3 3; 48 3 3 3; ... 0 0 0 1; 0 0 0 0) .
  • "1: ejecuta la siguiente función en cada fila de la matriz
  • ;@:(([,~#&' '@])@.>): agrega la cantidad de espacios dada antes de cada carácter en la cadena
  • (50&{.): toma los primeros 50 caracteres de la cadena
  • ([[i.@]&2e7): una función que genera la lista de 0 a 2 * 10 ^ 7, y luego la tira. Esto toma aproximadamente un tercio de segundo en mi máquina, esto causa el retraso.
  • ((50#LF)&,): agregue 50 líneas nuevas antes de la cadena, para borrar la pantalla
  • echo: salida de la cadena
  • s (...): da la cadena como argumento izquierdo a la función
marinus
fuente
0

APL (70)

{⎕SM∘←1,⍨1,⍨,/D{⍺,⍨⍵⍴⍕⍬}¨P←⍵-{⍵×~×⍺}\×⍵⊣⎕DL÷8⋄0∨.≠P:∇P}1↓⎕SD,1↓3⍴⍨⍴D←⍞

Toma la entrada del teclado, la salida está en la ⎕SMventana (que sería el terminal si tuviera un APL basado en texto, supongo). El tamaño de la ventana se detecta automáticamente, si realmente desea que sea 50, cambie 1↓⎕SDa 50.

Explicación:

  • 1↓⎕SD,1↓3⍴⍨⍴D←⍞: lea la cadena y guárdela D. Genere un vector que describa cuánto espacio en blanco agregar antes de cada carácter, que es el ancho de la pantalla antes del primer carácter ( 1↓⎕SD) y 3 antes de los otros ( 1↓3⍴⍨⍴D).

  • ⎕DL÷8: espere 1/8 de segundo

  • P←⍵-{⍵×~×⍺}\×⍵: en el vector en el argumento de la derecha, reste 1 del elemento distinto de cero a la izquierda y almacene el nuevo vector en P.
  • ,/D{⍺,⍨⍵⍴⍕⍬}¨P: para cada carácter en D, prefije la cantidad de espacios en blanco dada en P.
  • ⎕SM∘←1,⍨1,⍨: se muestra en la pantalla, en la columna más a la izquierda de la fila superior
  • 0∨.≠P:∇P: si hay un elemento distinto de cero en P, repita con P.
marinus
fuente
0

PowerShell , 129 bytes

for($x=' '*52+(($args|% t*y)-join' '*4);$x-match'  '){write-host "`r$(-join($x=$x-replace'(?<!  .*)  ')[0..50])  "-n
sleep -m 99}

Pruébalo en línea!

Este script no elimina espacios de los argumentos en contraste con el script de Joey .

TIOno muestra la salida correctamente. Con la consola Powershell, obtienes la línea de marca de desplazamiento.

mazzy
fuente
0

05AB1E , 42 bytes

ð¶:S3úJ46ú[D50£¶ð:D?IQ#ðõ.;“…¢('\r')“.eт.W

Pruébelo en línea (sin dormir). NOTA: No tengo 05AB1E instalado localmente, así que no estoy 100% seguro de si el \rtruco funciona (en teoría debería funcionar, sin embargo). En TIO, \rse interpretan como nuevas líneas. Además, el TIO utiliza la versión heredada, porque .eestá deshabilitado en la nueva versión TIO (sin embargo, el programa es el mismo tanto en la versión heredada como en la nueva versión de 05AB1E).

Explicación:

ð¶:            # Replace all spaces in the (implicit) input-string with newlines
   S           # Split the string to a list of characters
    3ú         # Pad each character with 3 leading spaces
      J        # Join the characters together again
       46ú     # And pad the entire string with an additional 46 leading spaces
[              # Now start an infinite loop:
 D             #  Duplicate the string
  50£          #  And leave only the first 50 characters of this copy as substring
     ¶ð:       #  Replace the newlines back to spaces
        D?     #  Duplicate the string, and print it without trailing newline
 IQ            #  If the current string is equal to the input:
   #           #   Stop the infinite loop
 ðõ.;          #  Replace the first space with an empty string to remove it
 “…¢('\r')“    #  Push dictionary string "print('\r')"
           .e  #  Evaluate it as Python code
 т.W           #  Sleep for 100 ms

Ver este consejo 05AB1E mío (sección Cómo usar el diccionario? ) Para entender por qué “…¢('\r')“es "print('\r')".

Kevin Cruijssen
fuente
0

Python, 139 bytes

import os;P='\n'
def f(x,w):
 v=k=P*w+P.join(x);o=str.replace
 while v!=x:os.system('sleep 1;clear');k=o(k,P,'',1);v=o(k,P,' ');print v[:w]

Tiene que llamar f('Hello World', 50)para comenzar.

Sunera Avinash
fuente