Recréez l'économiseur d'écran Windows ME en ASCII

19

Ce défi est inspiré de cette réponse sur Ask Ubuntu Stack Exchange.

Intro

Rappelez-vous l' économiseur d'écran Windows ME avec les tuyaux ? Il est temps de ramener la nostalgie!

entrez la description de l'image ici

Défi

Vous devez écrire un programme ou une fonction qui produira une représentation ASCII de l'économiseur d'écran. Dans l'économiseur d'écran, il devrait y avoir un seul tuyau qui se développera dans des directions semi-aléatoires.
Le début du tuyau sera placé au hasard sur l'une des bordures de l'écran et le morceau de tuyau doit être perpendiculaire à la bordure (les premiers tuyaux d'angle peuvent être horizontaux ou verticaux). À chaque tique, le tuyau se développera dans la direction à laquelle il fait face (horizontal / vertical) 80%ou prendra un coin au 20%hasard.

Représentation des tuyaux

Pour créer la pipe, 6 caractères Unicode seront utilisés

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

Contribution

Le programme / la fonction prendra 3 valeurs d'entrée, qui peuvent être rassemblées via les paramètres de fonction ou demandées à l'utilisateur.

  • Nombre de ticks
  • Largeur de l'écran
  • Hauteur de l'écran

Nombre de ticks

Pour chaque tick, un morceau de pipe sera ajouté à l'écran. Les tuyaux remplaceront les vieux morceaux de tuyaux s'ils apparaissent à la même position.

Par exemple, prenez un écran de taille 3x3

ticks == 3
─┐ 
 ┘ 


ticks == 4
─┐ 
└┘ 


ticks == 5
│┐ 
└┘ 

Chaque fois qu'un tuyau quitte l'écran, comme dans le dernier exemple à 5 ticks, un nouveau tuyau apparaîtra à une bordure aléatoire. Par exemple:

ticks == 6
│┐ 
└┘ 
  ─

Le nouveau tuyau devrait avoir 50% de chances d'être horizontal ou vertical.

Largeur / hauteur de l'écran

La largeur et la hauteur de l'écran peuvent être combinées en une seule valeur si cela est préférable dans la langue de votre choix. La largeur et la hauteur de l'écran auront toujours une valeur minimale de 1 et une valeur maximale de 255. Si la langue de votre choix prend en charge une console ou un écran de sortie qui est plus petit qu'une grille de 255x255 caractères, vous pouvez supposer que la largeur et la hauteur seront ne dépassez jamais les limites de votre console. (Exemple: fenêtre Windows 80x25 cmd)

Production

La sortie de votre programme / fonction doit être imprimée à l'écran ou renvoyée par une fonction. Pour chaque exécution de votre programme, un ensemble différent de canaux doit être généré.

Cas de test

Les cas de test suivants sont tous des exemples aléatoires de sorties valides

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

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

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

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

De toute évidence, plus il y a de tiques, plus il devient difficile de prouver la validité de votre programme. Par conséquent, la publication d'un gif de votre sortie en cours d'exécution sera préférée. Si cela n'est pas possible, veuillez publier une version de votre code qui comprend l'impression de la sortie. De toute évidence, cela ne comptera pas dans votre score.

Règles

  • Ceci est le , le plus petit nombre d'octets gagne
  • Des échappatoires standard s'appliquent
  • Si vous utilisez les caractères de canal unicode dans votre code source, vous pouvez les compter comme un seul octet

C'est un défi assez difficile qui peut éventuellement être résolu de nombreuses manières créatives, vous êtes encouragé à écrire une réponse dans une langue plus verbeuse même s'il existe déjà des réponses dans de courts esolangs. Cela créera un catalogue des réponses les plus courtes par langue. Upvotes bonus pour les gifs colorés fantaisie;)

Bon golf!

Avertissement: je suis conscient que les caractères Unicode ne sont pas ASCII, mais en l'absence d'un meilleur nom, je l'appelle simplement de l'art ASCII. Les suggestions sont les bienvenues :)

Bassdrop Cumberwubwubwub
la source
9
Les caractères Unicode que vous souhaitez dans la sortie ne sont pas ASCII.
Wheat Wizard
2
Je pense que cela devrait être étiqueté ascii-artau lieu de graphical-output- référence
AdmBorkBork
13
Nostalgie et Windows ME ne cadrent pas bien sur la même ligne
Luis Mendo
1
L'économiseur d'écran 3D Pipes était antérieur à Windows ME.
Neil
1
@Jordan Je pensais qu'il voulait dire tuples.
KarlKastor

Réponses:

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

Compter les caractères de dessin Unicode comme 1 octet chacun. (Comme spécifié par OP)

Moins 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 animé

Remarque: en essayant de garder le temps d'animation sous 30 secondes, plus d'épaisseurs accélèrent le rythme de l'animation

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
la source
Juste au moment où je pensais que QBasic pourrait réellement gagner un défi de golf. ;) Avoir un vote positif.
DLosc
12

Rien ne dit autant de nostalgie ...

QBasic, 332 octets

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 est le bon langage pour la tâche car:

  • Son encodage inclut des caractères de dessin de boîte - pas besoin d'Unicode
  • LOCATE vous permet d'imprimer à n'importe quel endroit de l'écran, en remplaçant ce qui s'y trouvait auparavant
  • Microsoft ®

Détails

Il s'agit de golf QBasic, écrit et testé sur QB64 avec la mise en forme automatique désactivée. Si vous tapez / coller dans l'QBasic IDE réel, il ajoutera un tas d'espaces et d' élargir ?dans PRINT, mais il devrait fonctionner exactement la même chose.

Le programme entre trois valeurs séparées par des virgules: les graduations, la largeur et la hauteur. Il demande ensuite une graine de nombre aléatoire. (Si ce comportement n'est pas acceptable, remplacez la deuxième ligne RANDOMIZE TIMERpar +6 octets.) Enfin, il dessine les canaux à l'écran.

Les dimensions maximales pouvant être saisies sont 80 (largeur) par 25 (hauteur). Si vous donnez une hauteur de 25, la ligne du bas sera coupée lorsque QBasic dit «Appuyez sur n'importe quelle touche pour continuer».

Comment?

TL; DR: Beaucoup de mathématiques.

La ligne et la colonne actuelles sont ret c; la direction actuelle est det la direction précédente est b. Les valeurs de direction 0-3 sont en bas, à droite, en haut, à gauche. L'arithmétique traduit ces valeurs en valeurs de pas correctes pour ret c, ainsi que les coordonnées de bord correctes pour commencer.

Les caractères de dessin de boîte │┐└─┘┌sont les points de code 179, 191, 192, 196, 217 et 218 dans QBasic. Celles-ci semblent assez aléatoires, mais il a toujours utilisé moins de caractères pour générer les nombres avec des mathématiques (assez compliquées, je ne suis pas sûr-même-je-comprends-le) que de faire un tas d'instructions conditionnelles.

Le code de changement de direction génère un nombre aléatoire compris entre -0,125 et 1,125 et prend sa parole. Cela donne -110% du temps, 080% du temps et 110% du temps. Nous ajoutons ensuite cela à la valeur actuelle de d, mod 4. L'ajout de 0 conserve la direction actuelle; l'ajout de +/- 1 fait un tour.

Quant au flux de contrôle, WHILE t ... WENDc'est la boucle principale; la section qui la précède, en commençant par le numéro de ligne 1( 1b=INT(RND*4)), redémarre le tuyau à un bord aléatoire. Chaque fois ret csont à l'extérieur de la fenêtre, nous GOTO 1.

Montrez-moi le GIF!

Voici:

Pipes!

Cela a été généré par une version quelque peu non golfée avec animation, couleur et une graine aléatoire automatique:

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
la source
Je l'ai tapé dans ma machine virtuelle MS-DOS v6.22 :-)
Neil
9

Python 2.7, 624 616 569 548 552 octets

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

Le premier paramètre est une graine, les mêmes graines généreront la même sortie, imprimant chaque étape avec un retard de 500 ms.

  • -10 octets grâce à @TuukkaX

le replacer

Exemple d'exécution

f(5,6,3,3)

affichera

   

 ─┐ 
   

──┐ 
   

┘─┐ 
   
┐  
┘─┐ 

version verbeuse

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
Barre
la source
1
Il y a un espace blanc inutile if x*y<0 or. 0.5peut être réduit à .5. import *pourrait être import*. ''.join(k) fora un espace blanc inutile. Vous devriez également pouvoir conserver dictune variable et l'appeler chaque fois que vous l'utilisez. Je n'ai pas testé combien cela économise, mais en enregistrant le dict(zip(a,b))dans un lambda qui fait le travail pour deux chaînes (a, b), il devrait en couper. +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édit à edc65 pour avoir enregistré la direction dans un seul numéro à 4 bits.

Lit une largeur / hauteur sur stdin avant de boucler l'économiseur d'écran pour toujours. Par exemple:

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

Ou pour un économiseur d'écran plein écran:

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

Pour plus de lisibilité, j'ai ajouté des nouvelles lignes. Nécessite une machine Linux avec un terminal respectant les codes ANSI. A des couleurs! Si vous supprimez la prise en charge des couleurs, cela coûte 17 octets de moins.

exemple

orlp
la source
5

Rubis, 413 403 396 octets

Tuyaux en rubis

Une fonction qui prend un certain nombre de graduations et une largeur en entrée et renvoie l'écran final sous forme de chaîne. Pourrait sans doute être plus joué au 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]}

Voir sur repl.it: https://repl.it/Db5h/4

Pour le voir en action, insérez ce qui suit après la ligne qui commence b[n*w+n+m]=:

puts b; sleep 0.2

... puis affectez le lambda à une variable par exemple pipes=->...et appelez-le comme pipes[100,20](pour 100 ticks et un écran 20x20).

Non golfé et explication

# 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]
}
Jordan
la source