Ricrea lo screensaver di Windows ME come ASCII


19

Questa sfida è ispirata da questa risposta al Ask Stack Ubuntu Exchange.

Intro

Ricordi lo screensaver di Windows ME con le pipe ? È ora di riportare la nostalgia!

inserisci qui la descrizione dell'immagine

Sfida

Dovresti scrivere un programma o una funzione che genererà una rappresentazione ASCII dello screensaver. Nello screensaver dovrebbe esserci un singolo tubo che crescerà in direzioni semi-casuali.
L'inizio del tubo verrà posizionato casualmente su uno qualsiasi dei bordi dello schermo e il pezzo di tubo dovrebbe essere perpendicolare al bordo (i primi tubi angolari possono essere orizzontali o verticali). Ogni segno di spunta il tubo crescerà nella direzione in cui è rivolto (orizzontale / verticale) per 80%caso o in un angolo per 20%caso.

Rappresentazione di tubi

Per creare la pipe, verranno utilizzati 6 caratteri 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

Ingresso

Il programma / funzione prenderà 3 valori di input, che possono essere raccolti attraverso i parametri della funzione o richiesti all'utente.

  • Quantità di zecche
  • Larghezza dello schermo
  • Altezza dello schermo

Quantità di zecche

Per ogni segno di spunta, un pezzo di tubo verrà aggiunto allo schermo. I tubi sovrascriveranno i vecchi pezzi di tubo se si generano nella stessa posizione.

Ad esempio, prendi uno schermo di dimensioni 3x3

ticks == 3
─┐ 
 ┘ 


ticks == 4
─┐ 
└┘ 


ticks == 5
│┐ 
└┘ 

Ogni volta che una pipe esce dallo schermo, come nell'ultimo esempio con 5 tick, allora una nuova pipe si genererà su un bordo casuale. Per esempio:

ticks == 6
│┐ 
└┘ 
  ─

Il nuovo tubo dovrebbe avere il 50% di probabilità di essere orizzontale o verticale.

Larghezza / altezza dello schermo

La larghezza e l'altezza dello schermo possono essere combinate in un unico valore se è preferibile nella tua lingua preferita. La larghezza e l'altezza dello schermo avranno sempre un valore minimo di 1 e un valore massimo di 255. Se la tua lingua preferita supporta una console o uno schermo di output che è più piccolo di una griglia di 255x255 caratteri, puoi supporre che la larghezza e l'altezza siano non superare mai i confini della tua console. (Esempio: finestra cmd 80x25 di Windows)

Produzione

L'output del programma / funzione deve essere stampato sullo schermo o restituito da una funzione. Per ogni esecuzione del programma, dovrebbe essere generato un diverso set di pipe.

Casi test

I seguenti casi di test sono tutti esempi casuali di output validi

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

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

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

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

Ovviamente, più segni di spunta si sono verificati, più diventa difficile dimostrare la validità del programma. Pertanto, è preferibile pubblicare un gif dell'output in esecuzione. Se ciò non è possibile, si prega di pubblicare una versione del codice che includa la stampa dell'output. Ovviamente, questo non conta per il tuo punteggio.

Regole

  • Questo è , vince il minor numero di byte
  • Si applicano scappatoie standard
  • Se usi i caratteri di pipe unicode nel tuo codice sorgente, puoi contarli come un singolo byte

Questa è una sfida piuttosto difficile che può essere risolta in molti modi creativi, sei incoraggiato a scrivere una risposta in un linguaggio più dettagliato anche se ci sono già risposte in brevi termini. Questo creerà un catalogo delle risposte più brevi per lingua. Voti bonus per gif colorate fantasia;)

Buon golf!

Disclaimer: sono consapevole che i caratteri Unicode non sono ASCII, ma in mancanza di un nome migliore lo chiamo solo arte ASCII. I suggerimenti sono benvenuti :)


9
I caratteri unicode che desideri nell'output non sono ASCII.
Wheat Wizard

2
Penso che questo dovrebbe essere taggato ascii-artinvece di graphical-output- riferimento
AdmBorkBork

13
Nostalgia e Windows ME non si adattano bene alla stessa linea
Luis Mendo,

1
Lo screensaver 3D Pipes ha preceduto Windows ME.
Neil,

1
@Jordan Pensavo intendesse le tuple.
KarlKastor,

Risposte:


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 i caratteri di disegno Unicode come 1 byte ciascuno. (Come specificato da OP)

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

Test animato

Nota: cercando di mantenere il tempo di animazione al di sotto di 30 secondi, più spessi accelerano il ritmo dell'animazione

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>


Proprio quando pensavo che QBasic potesse effettivamente vincere una sfida di golf. ;) Avere un voto.
DLosc,

12

Niente dice nostalgia abbastanza come ...

QBasic, 332 byte

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 è la lingua giusta per l'attività perché:

  • La sua codifica include caratteri di disegno a scatola - non è necessario Unicode
  • LOCATE ti consente di stampare in qualsiasi posizione sullo schermo, sovrascrivendo ciò che c'era in precedenza
  • Microsoft ®

specifiche

Questo è QBasic da golf, scritto e testato su QB64 con autoformatting disattivato. Se si digita / incolla nella reale QBasic IDE, si aggiungerà un po 'di spazi e di espandere ?in PRINT, ma dovrebbe funzionare esattamente lo stesso.

Il programma inserisce tre valori separati da virgola: tick, larghezza e altezza. Quindi chiede un seme a numero casuale. (Se questo comportamento non è accettabile, modificare la seconda riga inRANDOMIZE TIMER per +6 byte.) Infine, disegna i tubi sullo schermo.

Le dimensioni massime che è possibile inserire sono 80 (larghezza) per 25 (altezza). Dare un'altezza di 25 comporterà l'interruzione della riga inferiore quando QBasic dice "Premi un tasto qualsiasi per continuare".

Come?

TL; DR: molta matematica.

La riga e la colonna correnti sono re c; la direzione corrente è de la direzione precedente è b. I valori di direzione 0-3 sono in basso, a destra, in alto, a sinistra. L'aritmetica li traduce nei valori di passo corretti per rec , così come nelle coordinate dei bordi corretti per iniziare.

La scatola che disegna personaggi │┐└─┘┌ sono i punti di codice 179, 191, 192, 196, 217 e 218 in QBasic. Sembrano piuttosto casuali, ma ha comunque usato meno caratteri per generare i numeri con un po 'di matematica (piuttosto contorta, non sono sicuro-perfino-capisco-che) piuttosto che fare un mucchio di affermazioni condizionali.

Il codice per cambiare direzione genera un numero casuale compreso tra -0,125 e 1,125 e prende il suo valore. Ciò fornisce il -110% delle volte, l' 080% delle volte e il 110% delle volte. Aggiungiamo quindi questo al valore corrente di d, mod 4. L'aggiunta di 0 mantiene la direzione corrente; l'aggiunta di +/- 1 fa un turno.

Per quanto riguarda il flusso di controllo, WHILE t ... WENDè il loop principale; la sezione prima di essa, a partire dal numero di riga 1( 1b=INT(RND*4)), riavvia la pipe in modo casuale. Ogni volta rche csiamo fuori dalla finestra, noiGOTO 1 .

Mostrami la GIF!

Ecco qui:

Pipes!

Questo è stato generato da una versione in qualche modo ungolfed con animazione, colore e un seme casuale automatico:

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

Ho digitato questo nella mia VM v6.22 MS-DOS :-)
Neil

9

Python 2.7, 624 616 569 548 552 byte

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

Il primo parametro è un seme, gli stessi semi genereranno lo stesso output, stampando ogni passo con un ritardo di 500 ms.

  • -10 byte grazie a @TuukkaX

sostituirlo

Esempio di esecuzione

f(5,6,3,3)

uscirà

   

 ─┐ 
   

──┐ 
   

┘─┐ 
   
┐  
┘─┐ 

versione dettagliata

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

1
C'è uno spazio bianco inutile a if x*y<0 or. 0.5può essere ridotto a .5. import *potrebbe essere import*. ''.join(k) forha uno spazio bianco inutile. Dovresti anche essere in grado di mantenere dictuna variabile e chiamarla ogni volta che la usi. Non ho testato quanto questo salva, ma salvando dict(zip(a,b))in un lambda che fa il lavoro per due stringhe (a, b), dovrebbe tagliarne alcune. +1.
Yytsi,

7

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

#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;}

Ringraziamo edc65 per aver memorizzato la direzione in un singolo numero a 4 bit.

Legge una larghezza / altezza su stdin prima di eseguire il looping dello screensaver per sempre. Per esempio:

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

O per uno screensaver a schermo intero:

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

Per leggibilità ho aggiunto nuove righe. Richiede una macchina linux con un terminale che rispetti i codici ANSI. Ha i colori! Se si rimuove il supporto colore, costa 17 byte in meno.

esempio


5

Rubino, 413 403 396 byte

Tubi Di Rubino

Una funzione che accetta un numero di tick e una larghezza come input e restituisce la schermata finale come stringa. Senza dubbio si potrebbe giocare a golf di più.

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

Guardalo su repl.it: https://repl.it/Db5h/4

Per vederlo in azione, inserire quanto segue dopo la riga che inizia b[n*w+n+m]=:

puts b; sleep 0.2

... quindi assegnare lambda a una variabile, ad es pipes=->... e chiamalo come pipes[100,20](per 100 tick e uno schermo 20x20).

Ungolfed e spiegazione

# 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]
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.