Recree el protector de pantalla de Windows ME como ASCII

19

Este desafío está inspirado en esta respuesta en Ask Ubuntu Stack Exchange.

Introducción

¿Recuerdas el protector de pantalla de Windows ME con las tuberías ? ¡Es hora de recuperar la nostalgia!

ingrese la descripción de la imagen aquí

Desafío

Debe escribir un programa o función que genere una representación ASCII del protector de pantalla. En el protector de pantalla debe haber una sola tubería que crecerá en direcciones semi aleatorias.
El inicio de la tubería se colocará al azar en cualquiera de los bordes de la pantalla y la pieza de la tubería debe ser perpendicular al borde (las primeras tuberías de la esquina pueden ser horizontales o verticales). Cada tictac la tubería crecerá en la dirección que está mirando (horizontal / vertical) en una 80%oportunidad o tomar una esquina en una 20%oportunidad.

Representación de tubería

Para crear la tubería, se utilizarán 6 caracteres unicode

─    \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

El programa / función tomará 3 valores de entrada, que se pueden recopilar a través de parámetros de función o solicitar al usuario.

  • Cantidad de garrapatas
  • Ancho de pantalla
  • Altura de la pantalla

Cantidad de garrapatas

Por cada tic, se agregará un trozo de tubería a la pantalla. Las tuberías sobrescribirán las piezas de tubería viejas si se generan en la misma posición.

Por ejemplo, tome una pantalla de tamaño 3x3

ticks == 3
─┐ 
 ┘ 


ticks == 4
─┐ 
└┘ 


ticks == 5
│┐ 
└┘ 

Cada vez que una tubería sale de la pantalla, como en el último ejemplo con 5 ticks, se generará una nueva tubería en un borde aleatorio. Por ejemplo:

ticks == 6
│┐ 
└┘ 
  ─

La nueva tubería debe tener un 50% de posibilidades de ser horizontal o vertical.

Ancho / alto de pantalla

El ancho y la altura de la pantalla se pueden combinar en un solo valor si eso es preferible en el idioma que elija. El ancho y la altura de la pantalla siempre tendrán un valor mínimo de 1 y un valor máximo de 255. Si su idioma de elección admite una consola o pantalla de salida que sea más pequeña que una cuadrícula de caracteres 255x255, entonces puede suponer que el ancho y la altura serán nunca exceda los límites de su consola. (Ejemplo: ventana de Windows 80x25 cmd)

Salida

El resultado de su programa / función debe imprimirse en la pantalla o devolverse desde una función. Para cada ejecución de su programa, se debe generar un conjunto diferente de tuberías.

Casos de prueba

Los siguientes casos de prueba son ejemplos aleatorios de resultados válidos

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

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

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

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

Obviamente, cuantos más ticks hayan ocurrido, más difícil será probar la validez de su programa. Por lo tanto, se preferirá publicar un gif de su salida en ejecución. Si esto no es posible, publique una versión de su código que incluya la impresión de la salida. Obviamente, esto no contará para su puntaje.

Reglas

  • Este es el , gana la menor cantidad de bytes
  • Se aplican lagunas estándar
  • Si usa los caracteres de canalización Unicode en su código fuente, puede contarlos como un solo byte

Este es un desafío bastante difícil que puede resolverse de muchas maneras creativas, se le recomienda escribir una respuesta en un lenguaje más detallado, aunque ya hay respuestas en breves palabras. Esto creará un catálogo de respuestas más cortas por idioma. Votos de bonificación para gifs de colores elegantes;)

¡Feliz golf!

Descargo de responsabilidad: soy consciente de que los caracteres Unicode no son ASCII, pero a falta de un nombre mejor, simplemente lo llamo arte ASCII. Las sugerencias son bienvenidas :)

Bassdrop Cumberwubwubwub
fuente
99
Los caracteres unicode que desea en la salida no son ASCII.
Wheat Wizard
2
Creo que esto debería etiquetarse en ascii-artlugar de graphical-output- referencia
AdmBorkBork
13
La nostalgia y Windows ME no encajan bien en la misma línea
Luis Mendo
1
El protector de pantalla 3D Pipes es anterior a Windows ME.
Neil
1
@ Jordan pensé que se refería a las tuplas.
KarlKastor

Respuestas:

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 dibujo unicode como 1 byte cada uno. (Según lo especificado por OP)

Menos golf

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

Prueba animada

Nota: al tratar de mantener el tiempo de animación por debajo de 30 segundos, más grosores hacen que la animación avance más 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
fuente
Justo cuando pensaba que QBasic podría ganar un desafío de golf. ;) Tener un voto a favor.
DLosc
12

Nada dice 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 es el lenguaje correcto para la tarea porque:

  • Su codificación incluye caracteres de dibujo de recuadro: no es necesario usar Unicode
  • LOCATE le permite imprimir en cualquier lugar de la pantalla, sobrescribiendo lo que estaba allí anteriormente
  • Microsoft ®

Detalles específicos

Este es QBasic golfizado, escrito y probado en QB64 con autoformato desactivado. Si escribe / pega en el IDE real QBasic, añadirá un montón de espacios y ampliar ?en PRINT, pero debería funcionar exactamente igual.

El programa ingresa tres valores separados por comas: ticks, ancho y alto. Luego pide una semilla de número aleatorio. (Si este comportamiento no es aceptable, cambie la segunda línea a RANDOMIZE TIMER+6 bytes). Finalmente, dibuja las tuberías en la pantalla.

Las dimensiones máximas que se pueden ingresar son 80 (ancho) por 25 (alto). Al dar una altura de 25, la fila inferior se cortará cuando QBasic diga "Presione cualquier tecla para continuar".

¿Cómo?

TL; DR: Muchas matemáticas.

La fila y columna actuales son ry c; la dirección actual es dy la dirección anterior es b. Los valores de dirección 0-3 son abajo, derecha, arriba, izquierda. La aritmética los traduce a los valores de paso correctos para ry c, así como a las coordenadas de borde correctas para comenzar.

Los caracteres del cuadro de dibujo │┐└─┘┌son puntos de código 179, 191, 192, 196, 217 y 218 en QBasic. Esos parecen bastante aleatorios, pero aún usaba menos caracteres para generar los números con algunas matemáticas (bastante complicadas, no estoy seguro, ni siquiera lo entiendo) que para hacer un montón de declaraciones condicionales.

El código para cambiar de dirección genera un número aleatorio entre -0.125 y 1.125 y toma su piso. Esto da el -110% del tiempo, el 080% del tiempo y el 110% del tiempo. Luego agregamos esto al valor actual de d, mod 4. Agregar 0 mantiene la dirección actual; Agregar +/- 1 hace un giro.

En cuanto al flujo de control, el WHILE t ... WENDes el bucle principal; la sección anterior, comenzando con el número de línea 1( 1b=INT(RND*4)), reinicia la tubería en un borde aleatorio. Cuando ry cestamos fuera de la ventana, nosotros GOTO 1.

¡Muéstrame el GIF!

Aqui tienes:

¡Tubería!

Esto fue generado por una versión un tanto descuidada con animación, color y una semilla aleatoria 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
fuente
Escribí esto en mi VM MS-DOS v6.22 :-)
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

El primer parámetro es una semilla, las mismas semillas generarán la misma salida, imprimiendo cada paso con un retraso de 500 ms.

  • -10 bytes gracias a @TuukkaX

responde

Ejecución de ejemplo

f(5,6,3,3)

saldrá

   

 ─┐ 
   

──┐ 
   

┘─┐ 
   
┐  
┘─┐ 

versión detallada

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
varilla
fuente
1
Hay un espacio en blanco inútil en if x*y<0 or. 0.5se puede reducir a .5. import *podría ser import*. ''.join(k) fortiene un espacio en blanco inútil. También debe poder mantener dictuna variable y llamarla cada vez que la use. No he probado cuánto ahorra esto, pero al guardarlo dict(zip(a,b))en una lambda que hace el trabajo para dos cadenas (a, b), debería cortar un poco. +1.
Yytsi
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;}

Crédito a edc65 por almacenar la dirección en un solo número de 4 bits.

Lee un ancho / alto en stdin antes de repetir el protector de pantalla para siempre. P.ej:

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

O para un protector de pantalla a pantalla completa:

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

Para facilitar la lectura, agregué nuevas líneas. Requiere una máquina Linux con una terminal que respete los códigos ANSI. Tiene colores! Si elimina el soporte de color, cuesta 17 bytes menos.

ejemplo

orlp
fuente
5

Ruby, 413 403 396 bytes

Tubos de rubí

Una función que toma una serie de ticks y un ancho como entrada y devuelve la pantalla final como una cadena. Sin duda podría jugar más golf.

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

Véalo en repl.it: https://repl.it/Db5h/4

Para verlo en acción, inserte lo siguiente después de la línea que comienza b[n*w+n+m]=:

puts b; sleep 0.2

... luego asigne el lambda a una variable, por ejemplo, pipes=->...y llámelo como pipes[100,20](para 100 ticks y una pantalla de 20x20).

Sin golfos y explicación

# 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án
fuente