Letreiro de rolagem

13

Após minha entrada no Hello World Ofuscado, pensei que seria divertido compartilhar o código subjacente. Mas por que apenas mostrar o código, vamos torná-lo também um golfe!

Desafio

Escreva um script que role uma corda pelo terminal, da direita para a esquerda, estabelecendo-se no lado esquerdo.

Entrada

Leva uma string como argumento.

Resultado

Imprime a marca de rolagem em STDOUT. Largura máxima de ~ 50 caracteres. Começa com 0 ou 1 caractere sendo exibido. Algum espaço entre as letras durante a rolagem. Pára quando resolvido (sem espaço extra entre caracteres de palavras). Rolagem lenta, mas não muito lenta (<1s por iteração).

Exemplo

Executando script com arg 'Hello World'

                                                   H

mais tarde

                H    e    l    l    o         W    o

mais tarde

H    e    l    l    o          W    o    r    l    d

mais tarde

Hell    o         W    o    r    l    d

finalmente

Hello World

Para um exemplo em execução, tente meu código no desafio "Hello World". Eventualmente eu vou postar o meu. Atualmente, são 202 caracteres em Perl. Agora que existem alguns concorrentes, postei o meu nas respostas.

Vencedora

Não quero que as restrições sejam absolutas, por isso as deixei um pouco vagas. O roteiro mais curto que segue o espírito do meu original vencerá.

Notas

Este jogo assume um xtermambiente. Caso outro ambiente seja útil, apenas ambientes semelhantes serão comparados e um vencedor separado poderá ser declarado para cada um.

Adendo (25 de abril de 2012)

Para resolver alguns problemas, estou tomando uma decisão. Sua contagem de caracteres deve incluir o código necessário para:

  1. Flush STDOUT (olhando para você, Ruby)
  2. Implementar sleepcom atraso de tempo <1s (olhando para você, Perl)

Isso pode ser feito quando a linha de comando alterna para um intérprete, mas esses caracteres contam no total (sem o espaço em branco).

Joel Berger
fonte
Estou um pouco preocupado com comportamentos terminais para esse tipo de coisa ... xterm, vt102...?
dmckee --- ex-moderador gatinho
Estou assumindo o xterm, mas não acho que isso importe muito. Talvez eu não entenda sua preocupação?
Joel Berger
Esses truques geralmente são produzidos com base na maneira como vários terminais lidam com alguns dos caracteres que não são de impressão, e os terminais diferem no que eles podem fazer e em quais sequências produzem os efeitos. Para reprodutibilidade, pode ser bom ter um ambiente de terminal especificado.
dmckee --- ex-moderador gatinho
1
Isso ajudará você se alguém postar uma resposta muito curta que depende de um terminal obscuro para o qual você não tem um emulador, mas tudo bem.
dmckee --- gatinho ex-moderador
ok, acho que tenho agora. Graças para os pensamentos :-)
Joel Berger

Respostas:

5

python 2 - 146 caracteres

edit: transformou uma função em vez de entrada através de stdin. o primeiro argumento é a sequência e o segundo argumento é o comprimento que você deseja que seja. então a invocação seria f('Hello World', 50). Eu também fiz muito mais suave; quando cada personagem 'pousou' houve uma pausa estranha

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)

velho, 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)
Blazer
fonte
Usando o bash (pelo menos em uma instalação recente do MacOSX e CentOS), o comando shell usado para limpar a tela do terminal deve ser 'clear' e não 'cls'.
Paolo
'cls' para windows, 'clear' para compilações OSX / Linux, eu acho
Blazer
Aconselhamento sobre como iniciar o programa para aqueles que não lidam com python diariamente, seria útil. Inicie o python. colar código, ligar f("Hello World, 40)funcionou para mim.
usuário desconhecido
@user eu hum. Eu coloquei invocação lá em cima?
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)

O texto a ser exibido deve ser fornecido como argumento de linha de comando, por exemplo

ruby marquee.rb "Hello World"

para o exemplo mostrado acima. Infelizmente não posso mostrar a animação aqui, então você deve experimentar o código por conta própria.

Versão anterior:

s=" "*67+[*$*[0].chars]*"   ";(s.size*3/4).times{|j|s[j/3]='';$><<s[16,50]+" \r";sleep 0.1}
Howard
fonte
Tamanho impressionante. Não é muito bom, porém, sou eu (estou em uma máquina de baixa potência no momento) ou é assim que o código funciona?
Joel Berger
Descobri isso, eu tinha que definir STDOUT.sync=true;para que ele autoflushes. O Perl equiv é $|++. São 17 caracteres adicionais, mas ainda bem abaixo do meu. Bem, eu não posso ter Ruby batendo Perl! Vou ter que trabalhar. Agradável.
Joel Berger
Se eu ligar ruby1.8 "Hello World", recebo, não exatamente para minha surpresa, um erro dizendo:ruby1.8: No such file or directory -- Hello World (LoadError)
usuário desconhecido
@userunknown talvez você deve colocar o caminho para o arquivo de origem lá, também: ruby foo.rb args;-)
Patrick Oscity
@padde: Sim, eu deveria. Infelizmente, Howard editou seu post sem me informar sobre sua alteração. Dê uma olhada na história para entender minha pergunta.
usuário desconhecido
3

C, 94 83 80 173 caracteres

EDIT: Adicionado muito código, implementa toda a funcionalidade solicitada agora. A constante 1e8pode ser ajustada para controlar a velocidade. Na minha máquina, é bastante rápido.
Alguns personagens certamente podem ser salvos aqui. lpode ser estático (salva a inicialização), cpode se tornar um ponteiro (substituindo 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););
}

Versão antiga (80 caracteres), com funcionalidade parcial:
Salva alguns caracteres substituindo char**tpor int*t. Funciona bem em 32 bits ( int**tsuportaria 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
fonte
2

K&R C - 431 416 caracteres

Respeita o padrão a um alto grau. Usa ncurses, portanto deve ser amplamente independente do terminal. Há uma leve gagueira quando o texto atinge o lado devido a alguns truques executados para preservar o espaço em branco pretendido na string.

A string a ser usada deve ser passada como o primeiro argumento na linha de comando (e deve ser escapada se contiver espaços, mais ainda se contiver a !como minha string de teste ( 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();}

De uma forma mais legível e 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 --- gatinho ex-moderador
fonte
Há muito potencial para encurtar, principalmente substituindo ifpor operadores. Por exemplo - if((s-q)%2)s--;else usleep(1e5);-> s-q&1?s--:usleep(1e5);(ou s-=s-q&1||usleep(1e5);)
ugoren 28/03
@ugoren: Sim, e eu tinha me esquecido de substituir os ' 's por equivalentes numéricos.
dmckee --- ex-moderador gatinho
Mais alguns truques: Substitua x==32por x-32(inverte o significado, então inverta o if-else) ou por x<33(assumindo que 0..31 nunca foi usado). Inicialize com os valores que você tem ( for(curs_set(c=0);...). *(s+1)-> s[1]. Remova os aparelhos desnecessários (substituir ;por ,ajudará).
Ugoren
2

Perl 5.13.2, 96

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

Roubando muito da resposta de @ Kevin Reid , especialmente o /rtruque disponível nos Perls mais recentes.

Perl, 115

Como a resposta de @ Joel Berger , isso ficaria muito mais curto se eu pudesse usar sleep 1e ser lento, ou passar -MTime::HiRes=sleepna linha de comando para ativar sleep.1. Caso contrário, a única maneira incorporada de dormir pouco select'','','',.1é a bastante longa.

$|=@_=(($")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
fonte
sim, eu me mordi com minha própria regra nessa! Eu não sabia que outros langs teriam um sono embutido. Oh, bem.
Joel Berger
algumas sugestões, você pode remover o espaço após cada uma delas xe a forma de bloco mapeconomizará algumas.
Joel Berger
1

Javascript 180 218 caracteres

Versão de produção:

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()

Versão Ungolfed:

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();​

Aqui está uma demonstração do jsFiddle

Nota: se você tentar reproduzi-lo, verifique se o código está abaixo do corpo

ajax333221
fonte
Não posso dizer pela demo, ela "se empilha" no lado esquerdo ou simplesmente faz para a esquerda e mostra a sequência final? O Howard definitivamente funciona se você não tiver certeza.
Joel Berger
@JoelBerger, o hello world tem 4 espaços entre cada letra; quando h é o primeiro caractere, esses espaços são removidos. Esta demo é mais lenta jsfiddle.net/fYvg7/1
ajax333221
Isso está próximo, mas você deve remover cada espaço individualmente.
Joel Berger
@JoelBerger Fixed
ajax333221
Bem, eu odeio ser um problema, mas um outro problema: o seu começa com todas as letras exibidas, em vez de digitar uma a uma à direita.
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$/
  • Aviso de limpeza.
  • Pode ser espremido um pouco, reduzindo o espaço entre os caracteres ou o espaço em branco inicial.
  • Requer Perl 5.13.2 ou mais recente devido ao uso de /r.
  • A substituição para NUL para preservar espaços é inequívoca, pois o POSIX argv não é limpo por NUL. No entanto, a substituição do loop transformará qualquer outro espaço em branco em nada (eventualmente).

Créditos:

Kevin Reid
fonte
Eu amo a rbandeira, melhor além da língua desdestate
Joel Berger
1

festança 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"

ungolfed:

#!/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"
Usuário desconhecido
fonte
1

R, 319 caracteres

Seguindo a filosofia do exemplo do @Blazer (d é o atraso em 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
fonte
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 obter o sono <1s, você precisa executar o seguinte:

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

Como não vou me declarar vencedor, não discutirei sobre o que conta lá ou não (mas realmente não posso fazer com que Ruby ganhe isso ;-))

Joel Berger
fonte
4 mais e ele se encaixa em um tweet: D
ajax333221 31/03
4 caracteres aqui: s/' '/$"/ges/shift/pop/
ephemient 29/03
Sim, eu os incluí, além de remover a pushdeclaração. Eu ainda não tinha postado ainda.
Joel Berger
0

Q, 145

Não atende exatamente aos requisitos porque a linha final remove todos os espaços que estavam na cadeia 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];}

São necessários dois argumentos: string de entrada e velocidade de rolagem

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
fonte
infelizmente esse é um ponto importante. Eu sei que os scripts Perl podem ficar muito pequenos sem ele.
Joel Berger
0

PowerShell, 135

Não sou muito golfista e provavelmente uma abordagem horrível, mas estou doente e não consigo 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
fonte
0

J (116)

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

Toma a string de entrada na linha de comando, ou seja jconsole marquee.ijs 'Hello, world!'

Se não precisar limpar a tela, ou seja, faça a seguinte saída:

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

é permitido, seriam 12 caracteres mais curtos.

Explicação:

  • s.=>2{ARGV: obtém a string na linha de comando
  • k.=50,3#~<:#s: a quantidade inicial de espaço em branco adicionada antes de cada caractere, 50 antes do primeiro e 3 antes de todos os outros. (fornece uma matriz, '50 3 3 3 ... ')
  • ([-=&0@/:@\:@~:&0): dado um array, diminui o primeiro item diferente de zero no array
  • ^:(i.>:+/k): esta função aplicada N vezes, em que N é 0 até a soma da quantidade de espaço em branco adicionado. (dá uma matriz: 50 3 3 3; 49 3 3 3; 48 3 3 3; ... 0 0 0 1; 0 0 0 0).
  • "1: execute a seguinte função em cada linha da matriz
  • ;@:(([,~#&' '@])@.>): adicione a quantidade especificada de espaços antes de cada caractere na sequência
  • (50&{.): pega os primeiros 50 caracteres da sequência
  • ([[i.@]&2e7): uma função que gera a lista de 0 a 2 * 10 ^ 7 e a joga fora. Isso leva cerca de um terço de segundo na minha máquina, isso causa o atraso.
  • ((50#LF)&,): adicione 50 novas linhas antes da sequência, para limpar a tela
  • echo: gera a string
  • s (...): fornece a string como argumento esquerdo para a função
marinus
fonte
0

APL (70)

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

Pega a entrada do teclado, a saída está na ⎕SMjanela (que seria o terminal se você tivesse um APL baseado em texto, eu acho). O tamanho da janela é detectado automaticamente, se você realmente quiser 50, mude 1↓⎕SDpara 50.

Explicação:

  • 1↓⎕SD,1↓3⍴⍨⍴D←⍞: leia a string e armazene D. Gere um vetor descrevendo quanto espaço em branco será adicionado antes de cada caractere, que é a largura da tela antes do primeiro caractere ( 1↓⎕SD) e 3 antes dos outros ( 1↓3⍴⍨⍴D).

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

  • P←⍵-{⍵×~×⍺}\×⍵: no vetor no argumento certo, subtraia 1 do item diferente de zero à esquerda e armazene o novo vetor em P.
  • ,/D{⍺,⍨⍵⍴⍕⍬}¨P: para cada caractere em D, prefixe a quantidade de espaço em branco fornecida em P.
  • ⎕SM∘←1,⍨1,⍨: exibido na tela, na coluna mais à esquerda da linha superior
  • 0∨.≠P:∇P: se houver um elemento diferente de zero em P, repita com P.
marinus
fonte
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}

Experimente online!

Este script não remove espaços dos argumentos em contraste com o script de Joey .

TIOnão exibe a saída corretamente. Com o console do Powershell, você obtém a marca de rolagem.

confuso
fonte
0

05AB1E , 42 bytes

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

Experimente online (sem dormir). NOTA: Não tenho o 05AB1E instalado localmente, por isso não tenho 100% de certeza se o \rtruque funciona (em teoria, ele deve funcionar). No TIO, eles \rsão interpretados como novas linhas. Além disso, o TIO usa a versão herdada, porque .eestá desativado na nova versão do TIO (o programa é o mesmo na versão herdada e na nova versão do 05AB1E).

Explicação:

ð¶:            # 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

Veja esta dica 05AB1E meu (seção Como usar o dicionário? ) Para entender por que “…¢('\r')“é "print('\r')".

Kevin Cruijssen
fonte
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]

Tem que ligar f('Hello World', 50)para começar.

Sunera Avinash
fonte