Recrie o protetor de tela do Windows ME como ASCII

19

Esse desafio é inspirado nesta resposta no Ask Ubuntu Stack Exchange.

Introdução

Lembre-se do protetor de tela do Windows ME com os canos ? Hora de trazer de volta a nostalgia!

insira a descrição da imagem aqui

Desafio

Você deve escrever um programa ou função que produza uma representação ASCII do protetor de tela. No protetor de tela, deve haver um único tubo que crescerá em direções semi-aleatórias.
O início do tubo será colocado aleatoriamente em qualquer uma das bordas da tela e a peça do tubo deverá ser perpendicular à borda (os primeiros tubos de canto podem ser horizontais ou verticais). Cada tique que o cano crescerá na direção em que está voltado (horizontal / vertical) com uma 80%chance ou fará um canto com uma 20%chance.

Representação de tubulação

Para criar o canal, 6 caracteres unicode serão usados

─    \u2500    horizontal pipe
│    \u2502    vertical pipe
┌    \u250C    upper left corner pipe
┐    \u2510    upper right corner pipe
└    \u2514    lower left corner pipe
┘    \u2518    lower right corner pipe

Entrada

O programa / função terá 3 valores de entrada, que podem ser coletados através de parâmetros de função ou solicitados ao usuário.

  • Quantidade de carrapatos
  • Largura da tela
  • Altura da tela

Quantidade de carrapatos

Para cada escala, um pedaço de tubo será adicionado à tela. Os canos substituirão os pedaços de canos antigos se aparecerem na mesma posição.

Por exemplo, pegue uma tela de tamanho 3x3

ticks == 3
─┐ 
 ┘ 


ticks == 4
─┐ 
└┘ 


ticks == 5
│┐ 
└┘ 

Sempre que um tubo sai da tela, como no último exemplo, com 5 tiques, um novo tubo aparecerá em uma borda aleatória. Por exemplo:

ticks == 6
│┐ 
└┘ 
  ─

O novo tubo deve ter 50% de chance de ser horizontal ou vertical.

Largura / altura da tela

A largura e a altura da tela podem ser combinadas em um único valor, se preferir no seu idioma preferido. A largura e a altura da tela sempre terão um valor mínimo de 1 e um valor máximo de 255. Se o idioma de sua escolha suportar um console ou uma tela de saída menor que uma grade de caracteres de 255x255, você poderá assumir que a largura e a altura serão nunca exceda os limites do seu console. (Exemplo: janela Windows 80x25 cmd)

Resultado

A saída do seu programa / função deve ser impressa na tela ou retornada de uma função. Para cada execução do seu programa, um conjunto diferente de pipes deve ser gerado.

Casos de teste

Os seguintes casos de teste são todos exemplos aleatórios de saídas válidas

f(4, 3, 3)
 │
─┘
  │

f(5, 3, 3)
 │
─┘┌
  │

f(6, 3, 3)
─│
─┘┌
  │

f(7, 3, 3)
──
─┘┌
  │

Obviamente, quanto mais ticks ocorrerem, mais difícil será provar a validade do seu programa. Portanto, é preferível postar um gif da sua saída em execução. Se isso não for possível, publique uma versão do seu código que inclua a impressão da saída. Obviamente, isso não conta para a sua pontuação.

Regras

  • Este é o , a menor quantidade de bytes ganha
  • Aplicam-se brechas padrão
  • Se você usar os caracteres de canal unicode no seu código-fonte, poderá contá-los como um único byte

Esse é um desafio bastante difícil que pode ser resolvido de várias maneiras criativas; você deve escrever uma resposta em uma linguagem mais detalhada, mesmo que já haja respostas em pequenos intervalos. Isso criará um catálogo de respostas mais curtas por idioma. Upvotes de bônus para gifs coloridos de fantasia;)

Feliz golfe!

Isenção de responsabilidade: Estou ciente de que os caracteres Unicode não são ASCII, mas, na falta de um nome melhor, chamo apenas de arte ASCII. Sugestões são bem-vindas :)

Bassdrop Cumberwubwubwub
fonte
9
Os caracteres unicode que você deseja na saída não são ASCII.
Assistente de trigo
2
Eu acho que isso deve ser marcado em ascii-artvez de graphical-output- referência
AdmBorkBork 16/16
13
Nostalgia e Windows ME não se encaixam bem na mesma linha
Luis Mendo
11
O protetor de tela 3D Pipes é anterior ao Windows ME.
Neil
11
@ Jordan Eu pensei que ele quis dizer tuplas.
KarlKastor 17/09/16

Respostas:

9

JavaScript (ES6), 264 266 274 281

(t,w,h,r=n=>Math.random()*n|0,g=[...Array(h)].map(x=>Array(w).fill` `))=>((y=>{for(x=y;t--;d&1?y+=d-2:x+=d-1)x<w&y<h&&~x*~y?0:(d=r(4))&1?x=r(w,y=d&2?0:h-1):y=r(h,x=d?0:w-1),e=d,d=r(5)?d:2*r(2)-~d&3,g[y][x]="─└ ┌┐│┌  ┘─┐┘ └│"[e*4|d]})(w),g.map(x=>x.join``).join`
`)

Contando caracteres de desenho unicode como 1 byte cada. (Conforme especificado pelo OP)

Menos golfe

(t,w,h)=>{
  r=n=>Math.random()*n|0; // integer range random function
  g=[...Array(h)].map(x=>Array(w).fill(' ')); // display grid
  for (x=y=w;t--;)
    x<w & y<h && ~x*~y||( // if passed boundary
      d = r(4), // select random direction
      d & 1? (x=r(w), y=d&2?0:h-1) : (y=r(h), x=d?0:w-1) // choose start position 
    ),
    e=d, d=r(5)?d:2*r(2)-~d&3, // change direction 20% of times
    g[y][x]="─└ ┌┐│┌  ┘─┐┘ └│"[e*4|d], // use char based on current+prev direction
    d&1 ? y+=d-2 : x+=d-1 // change x,y position based on direction
  return g.map(x=>x.join``).join`\n`
}

Teste animado

Nota: ao tentar manter o tempo da animação em menos de 30 segundos, mais truques tornam o ritmo da animação mais rápido

f=(t,w,h,r=n=>Math.random()*n|0,g=[...Array(h)].map(x=>Array(w).fill` `))=>
{
  z=[]
  for(x=y=w;t--;d&1?y+=d-2:x+=d-1)
    x<w&y<h&&~x*~y?0:(d=r(4))&1?x=r(w,y=d&2?0:h-1):y=r(h,x=d?0:w-1),
    e=d,d=r(5)?d:2*r(2)-~d&3,g[y][x]="─└ ┌┐│┌  ┘─┐┘ └│"[e*4|d],
    z.push(g.map(x=>x.join``).join`\n`)
  return z
}

function go() {
  B.disabled=true
  var [t,w,h]=I.value.match(/\d+/g)
  var r=f(+t,+w,+h)
  O.style.width = w+'ch';
  var step=0
  var animate =_=>{
    S.textContent = step
    var frame= r[step++]
    if (frame) O.textContent = frame,setTimeout(animate, 30000/t);
    else   B.disabled=false
  }
  
  animate()
}

go()
#O { border: 1px solid #000 }
Input - ticks,width,height
<input value='600,70,10' id=I><button id=B onclick='go()'>GO</button>
<span id=S></span>
<pre id=O></pre>

edc65
fonte
Apenas quando pensei que o QBasic poderia realmente vencer um desafio de golfe. ;) Tenha um voto positivo.
DLosc 17/09/16
12

Nada diz nostalgia como ...

QBasic, 332 bytes

INPUT t,w,h
RANDOMIZE
CLS
1b=INT(RND*4)
d=b
IF b MOD 2THEN c=(b-1)/2*(w-1)+1:r=1+INT(RND*h)ELSE c=1+INT(RND*w):r=b/2*(h-1)+1
WHILE t
LOCATE r,c
m=(b+d)MOD 4
IF b=d THEN x=8.5*m ELSE x=13*m+(1<((b MOD m*3)+m)MOD 5)
?CHR$(179+x);
r=r-(d-1)MOD 2
c=c-(d-2)MOD 2
b=d
d=(4+d+INT(RND*1.25-.125))MOD 4
t=t-1
IF(r<=h)*(c<=w)*r*c=0GOTO 1
WEND

QBasic é o idioma certo para a tarefa porque:

  • Sua codificação inclui caracteres de desenho de caixa - sem necessidade de Unicode
  • LOCATE permite imprimir em qualquer local da tela, substituindo o que havia anteriormente
  • Microsoft ®

Específicos

Esse é o QBasic, escrito e testado no QB64 com o autoformatting desativado. Se você digitar / colá-lo no real QBasic IDE, ele irá adicionar um monte de espaços e expandir ?em PRINT, mas ele deve ser executado exatamente o mesmo.

O programa insere três valores separados por vírgula: ticks, largura e altura. Ele então pede uma semente de número aleatório. (Se esse comportamento não for aceitável, altere a segunda linha RANDOMIZE TIMERpara +6 bytes.) Finalmente, ele desenha os pipes na tela.

As dimensões máximas que podem ser inseridas são 80 (largura) por 25 (altura). Dar uma altura de 25 resultará no corte da linha inferior quando o QBasic disser "Pressione qualquer tecla para continuar".

Quão?

TL; DR: Muita matemática.

A linha e coluna atuais são re c; a direção atual é de a direção anterior é b. Os valores de direção 0-3 estão abaixo, direita, cima, esquerda. A aritmética converte esses valores nos valores de passo corretos para re c, assim como nas coordenadas de arestas corretas para iniciar.

Os caracteres de desenho da caixa │┐└─┘┌são os pontos de código 179, 191, 192, 196, 217 e 218 no QBasic. Eles parecem bastante aleatórios, mas ainda assim usaram menos caracteres para gerar os números com alguma matemática (bastante complicada, não sei ao certo se compreendo) do que para fazer um monte de instruções condicionais.

O código para mudar de direção gera um número aleatório entre -0,125 e 1,125 e toma seu andar. Isso fornece -110% do tempo, 080% do tempo e 110% do tempo. Em seguida, adicionamos isso ao valor atual de d, mod 4. Adicionar 0 mantém a direção atual; adicionar +/- 1 faz uma curva.

Quanto ao fluxo de controle, o WHILE t ... WENDé o loop principal; a seção anterior a ela, começando com o número da linha 1( 1b=INT(RND*4)), reinicia o tubo em uma aresta aleatória. Sempre re cestá fora da janela, nós GOTO 1.

Mostre-me o GIF!

Aqui está:

Tubos!

Isso foi gerado por uma versão não-destruída com animação, cor e uma semente aleatória automática:

INPUT t, w, h
RANDOMIZE TIMER
CLS

restart:
' Calculate an edge to start from

b = INT(RND * 4)
'0: top edge (moving down)
'1: left edge (moving right)
'2: bottom edge (moving up)
'3: right edge (moving left)
d = b

' Calculate column and row for a random point on that edge
IF b MOD 2 THEN
    c = (b - 1) / 2 * (w - 1) + 1
    r = 1 + INT(RND * h)
ELSE
    c = 1 + INT(RND * w)
    r = b / 2 * (h - 1) + 1
END IF
COLOR INT(RND * 15) + 1

WHILE t
    ' Mathemagic to generate the correct box-drawing character
    m = (b + d) MOD 4
    IF b = d THEN
        x = 17 * m / 2
    ELSE
        x = 13 * m + (1 < ((b MOD m * 3) + m) MOD 5)
    END IF
    LOCATE r, c
    PRINT CHR$(179 + x);

    ' Update row and column
    r = r - (d - 1) MOD 2
    c = c - (d - 2) MOD 2
    ' Generate new direction (10% turn one way, 10% turn the other way,
    ' 80% go straight)
    b = d
    d = (4 + d + INT(RND * 1.25 - .125)) MOD 4

    ' Pause
    z = TIMER
    WHILE TIMER < z + 0.01
        IF z > TIMER THEN z = z - 86400
    WEND

    t = t - 1
    IF r > h OR c > w OR r = 0 OR c = 0 THEN GOTO restart
WEND
DLosc
fonte
Eu digitei isso em meus MS-DOS v6.22 VM :-)
Neil
9

Python 2,7, 624 616 569 548 552 bytes

from random import*
from time import*
i=randint
z=lambda a,b:dict(zip(a,b))
c={'u':z('lur',u'┐│┌'),'d':z('ldr',u'┘│└'),'l':z('uld',u'└─┌'),'r':z('urd',u'┘─┐')}
m=z('udlr',[[0,-1],[0,1],[-1,0],[1,0]])
def f(e,t,w,h):
 seed(e);s=[w*[' ',]for _ in' '*h]
 while t>0:
  _=i(0,1);x,y=((i(0,w-1),i(0,1)*(h-1)),(i(0,1)*(w-1),i(0,h-1)))[_];o=('du'[y>0],'rl'[x>0])[_]
  while t>0:
   d=c[o].keys()[i(7,16)//8];s[y][x]=c[o][d];x+=m[d][0];y+=m[d][1];t-=1;sleep(.5);print'\n'.join([''.join(k)for k in s]);o=d
   if(x*y<0)+(x>=w)+(y>=h):break

O primeiro parâmetro é uma semente, as mesmas sementes geram a mesma saída, imprimindo cada etapa com um atraso de 500 ms.

  • -10 bytes graças a @TuukkaX

substitua

Exemplo de execução

f(5,6,3,3)

irá produzir

   

 ─┐ 
   

──┐ 
   

┘─┐ 
   
┐  
┘─┐ 

versão detalhada

import random as r
from time import *
char={
'u':{'u':'│','l':'┐','r':'┌'},
'd':{'d':'│','l':'┘','r':'└'},
'l':{'u':'└','d':'┌','l':'─'},
'r':{'u':'┘','d':'┐','r':'─'}
}
move={'u':[0,-1],'d':[0,1],'l':[-1,0],'r':[1,0]}
def f(seed,steps,w,h):
 r.seed(seed)
 screen=[[' ',]*w for _ in ' '*h]
 while steps > 0:
  if r.randint(0,1):
   x,y=r.randint(0,w-1),r.randint(0,1)*(h-1)
   origin='du'[y>0]  
  else:
   x,y=r.randint(0,1)*(w-1),r.randint(0,h-1)
   origin = 'rl'[x>0]
  while steps > 0:
   direction = char[origin].keys()[r.randint(0,2)]
   screen[y][x]=char[origin][direction]
   x+=move[direction][0]
   y+=move[direction][1]
   steps-=1
   sleep(0.5)
   print '\n'.join([''.join(k) for k in screen]),''
   if x<0 or y<0 or x>=w or y>=h:
    break
   origin=direction
Cajado
fonte
11
Há um espaço em branco inútil em if x*y<0 or. 0.5pode ser reduzido para .5. import *poderia ser import*. ''.join(k) forpossui um espaço em branco inútil. Você também deve poder manter dictuma variável e chamá-la sempre que usá-la. Ainda não testei quanto isso economiza, mas salvando o dict(zip(a,b))em um lambda que faz o trabalho por duas strings (a, b), ele deve cortar um pouco. +1.
Yytsi 16/09/16
7

C (GCC / linux), 402 353 352 302 300 298 296 288 bytes

#define R rand()%
x,y,w,h,r;main(c){srand(time(0));scanf(
"%d%d",&w,&h);for(printf("\e[2J");x%~w*
(y%~h)||(c=R 8,(r=R 4)&1?x=1+R w,y=r&2
?1:h:(y=1+R h,x=r&2?1:w));usleep('??'))
printf("\e[%dm\e[%d;%dH\342\224%c\e[H\n",
30+c,y,x,2*"@J_FHAF__L@HL_JA"[r*4|(r^=R 5
?0:1|R 4)]),x+=--r%2,y+=~-r++%2;}

Agradecemos a edc65 por armazenar a direção em um único número de 4 bits.

Lê uma largura / altura no stdin antes de fazer o loop do protetor de tela para sempre. Por exemplo:

gcc -w golf.c && echo "25 25" | ./a.out

Ou para um protetor de tela em tela cheia:

gcc -w golf.c && resize | sed 's/[^0-9]*//g' | ./a.out

Para facilitar a leitura, adicionei novas linhas. Requer uma máquina Linux com um terminal respeitando os códigos ANSI. Tem cores! Se você remover o suporte de cores, custará 17 bytes a menos.

exemplo

orlp
fonte
5

Rubi, 413 403 396 bytes

Tubos de rubi

Uma função que recebe vários ticks e uma largura como entrada e retorna a tela final como uma string. Sem dúvida, poderia ser jogado mais.

->t,w{k=[-1,0,1,0,-1]
b=(" "*w+$/)*w
f=->t,a=[[0,m=rand(w),2],[w-1,m,0],[m,0,1],[m,w-1,3]].sample{n,m,i=a
d=k[i,2]
q=->n,m,i{_,g,j=rand>0.2?[[1,0],[3,0],[0,1],[2,1]].assoc(i):"021322033132243140251350".chars.map(&:to_i).each_slice(3).select{|c,|c==i}.sample
v,u=k[j||=i,2]
y=n+v
x=m+u
[g,y,x,j]}
g,y,x,j=q[n,m,i]
b[n*w+n+m]="─│┌┐┘└"[g]
y>=0&&y<w&&x>=0&&x<w ?t>1?f[t-1,[y,x,j]]:b:f[t]}
f[t]}

Veja-o em repl.it: https://repl.it/Db5h/4

Para vê-lo em ação, insira o seguinte após a linha que começa b[n*w+n+m]=:

puts b; sleep 0.2

... então atribua o lambda a uma variável, por exemplo, pipes=->...e chame-a comopipes[100,20] (para 100 ticks e uma tela 20x20).

Ungolfed & explicação

# Anonymous function
# t - Number of ticks
# w - Screen width
->t,w{
  # The cardinal directions ([y,x] vectors)
  # Up = k[0..1], Right = k[1..2] etc.
  k = [-1, 0, 1, 0, -1]

  # An empty screen as a string
  b = (" " * w + $/) * w

  # Main tick function (recursive)
  # t - The number of ticks remaining
  # a - The current position and vector index; if not given is generated randomly
  f = ->t,a=[[0,m=rand(w),2], [w-1,m,0], [m,0,1], [m,w-1,3]].sample{
    # Current row, column, and vector index
    n, m, i = a
    d = k[i,2] # Get vector by index

    # Function to get the next move based on the previous position (n,m) and direction (d)
    q = ->n,m,i{
      # Choose the next pipe (`g` for glyph) and get the subsequent vector index (j)
      _, g, j = (
        rand > 0.2 ?
          [[1,0], [3,0], [0,1], [2,1]].assoc(i) : # 80% of the time go straight
          "021322033132243140251350".chars.map(&:to_i).each_slice(3)
            .select{|c,|c==i}.sample
      )

      # Next vector (`v` for vertical, `u` for horizontal)
      # If straight, `j` will be nil so previous index `i` is used
      v, u = k[j||=i, 2]

      # Calculate next position
      y = n + v
      x = m + u

      # Return next glyph, position and vector index
      [g, y, x, j]
    }

    # Get next glyph, and subsequent position and vector index
    g, y, x, j = q[n, m, i]

    # Draw the glyph
    b[n * w + n + m] = "─│┌┐┘└"[g]

    # Check for out-of-bounds
    y >= 0 && y < w && x >=0 && x < w ?
      # In bounds; check number of ticks remaining
      t > 1 ?
        f[t-1, [y,x,j]] : # Ticks remain; start next iteration
        b : # No more ticks; return final screen

      # Out of bounds; repeat tick with new random start position
      f[t]
  }
  f[t]
}
Jordânia
fonte