63 votes

Code de Golf: le Nouvel An feux d'Artifice

L'année 2009 tire à sa fin, et avec l'économie et à tous, nous allons sauver notre argent et au lieu d'acheter cher des feux d'artifice, nous allons célébrer en ASCII art cette année.

Le défi

Étant donné un ensemble de feux d'artifice et une heure, prendre une photo du feu d'artifice à ce moment et de le faire à la console.

La meilleure solution est entré avant minuit la Veille du Nouvel An (UTC) recevront une prime de 500 rep. C'est le code de golf, de sorte que le nombre de caractères compte fortement; cependant afin de faire de la communauté voix, et je me réserve la décision finale quant à ce qui est le mieux/cool/plus créatifs/etc.

Les Données D'Entrée

Notez que le système de coordonnées est de gauche à droite, de bas en haut, de sorte que tous les feux d'artifice sont lancés à un y-coordonner de 0 (zéro).

Les données d'entrée se compose de feux d'artifice de la forme

(x, speed_x, speed_y, launch_time, detonation_time)

  • x est la position (colonne) où le feu d'artifice est lancé,
  • speed_x et speed_y sont à l'horizontale et à la verticale de la vitesse du feu d'artifice au moment du lancement,
  • launch_time est le point dans le temps que ce feu d'artifice est lancé,
  • detonation_time est le point dans le temps que ce feu d'artifice explose.

Le feu d'artifice de données peut être codé en dur dans votre programme comme une liste de 5-n-uplets (ou l'équivalent dans votre langue), sans compter vers votre nombre de caractères. Il doit, cependant, être facile de modifier ces données.

Vous pouvez faire les hypothèses suivantes:

  • il existe une quantité raisonnable de feux d'artifice (disons, moins d'une centaine)
  • pour chaque feu d'artifice, tous les cinq nombres sont des nombres entiers à l'intérieur d'une fourchette raisonnable (disons, 16 bits suffirait pour chaque),
  • -20 <= x <= 820
  • -20 <= speed_x <= 20
  • 0 < speed_y <= 20
  • launch_time >= 0
  • launch_time < detonation_time < launch_time + 50

Le seul autre morceau de données d'entrée est le point de temps qui est censé être rendu. C'est un entier non négatif qui est donnée à vous par le biais de l'entrée standard, ou argument de ligne de commande (selon votre choix).

L'idée est que (en supposant que votre programme est un script python appelé firework.py) ce script bash vous donne un joli feu d'artifice animation:

#!/bin/bash
I=0
while (( 1 )) ; do
    python firework.py $I
    I=$(( $I + 1 ))
done

(n'hésitez pas à mettre l'équivalent .Fichier BAT ici).

La vie d'un feu d'artifice

La vie d'un feu d'artifice est comme suit:

  • Avant le lancement, il peut être ignoré.
  • Au moment du lancement, la fusée a la position (x, 0) et le vecteur vitesse (speed_x, speed_y).
  • Pour chaque pas de temps, le vecteur vitesse est ajouté à la position. Avec un peu de stretch appliquée à des lois de Newton, nous supposons que la vitesse reste constante.
  • À la détonation temps, la fusée explose en neuf étincelles. Tous les neuf étincelles ont la même position sur ce point dans le temps (qui est la position que la fusée serait, n'est-ce pas explosé), mais leurs vitesses différentes. Chaque vitesse est basée sur la fusée de la vitesse, avec -20, 0 ou 20 ajoutés speed_x et -10, 0, 10 ajoutés speed_y. C'est de neuf combinaisons possibles.
  • Après la détonation du temps, de la gravité commence à tirer: À chaque pas de temps, la constante gravitationnelle, qui se trouve être de 2 (deux), est soustraite de chaque étincelle de l' speed_y. L'horizontal speed_x reste constante.
  • Pour chaque pas de temps après la détonation de temps, vous d'abord ajouter le vecteur vitesse à la position, puis soustraire de 2 speed_y.
  • Quand une étincelle de l' y position descend en dessous de zéro, vous pouvez l'oublier.

Sortie

Ce que nous voulons, c'est une photo du feu d'artifice de la façon dont il regarde le point donné dans le temps. Nous nous intéressons seulement à la trame 0 <= x <= 789 et 0 <= y <= 239, de la cartographie à un 79x24 la sortie de caractères.

Donc, si une fusée ou d'étincelle a la position (247, 130), on dessine un personnage dans la colonne 24 (de base zéro, c'est donc le 25 colonne), ligne 13 (de base zéro et le comptage à partir du bas, de sorte que c'est la ligne 23 - 13 = 10, le 11e de ligne de la sortie).

Le personnage pénètre dépend de la vitesse de la fusée / spark:

  • Si le mouvement est horizontal*, c'est à dire speed_y == 0 or abs(speed_x) / abs(speed_y) > 2, le personnage est "-".
  • Si le mouvement est vertical*, c'est à dire speed_x == 0 or abs(speed_y) / abs(speed_x) > 2, le personnage est "|".
  • Sinon, le mouvement est diagonale, et le caractère "\" ou "/" (vous devinerez celui de droite).
  • Si la même situation se aspiré à plus d'une fois (même si c'est le même personnage), nous avons mis "X" à la place. Donc, en supposant que vous avez une étincelle à l' (536, 119) et une à l' (531, 115), vous dessinez un "X", quelle que soit leur vitesse.

* mise à jour: ces sont des entiers divisions, de sorte que la pente doit être au moins de 3 ans, ou au plus 1/3, respectivement

La sortie (écrit sur la sortie standard) est de 24 lignes, chacune terminée par un caractère de saut de ligne. Les espaces sont ignorés, de sorte que vous pouvez, mais n'ont pas besoin d', le pad à une largeur de 79. Les lignes ne peut être supérieure à 79 caractères (à l'exclusion du saut de ligne). Tous les espaces intérieurs doivent être en caractères d'espace (ASCII 32).

Les Données De L'Échantillon

Feux d'artifice:

fireworks = [(628, 6, 6, 3, 33),
             (586, 7, 11, 11, 23),
             (185, -1, 17, 24, 28),
             (189, 14, 10, 50, 83),
             (180, 7, 5, 70, 77),
             (538, -7, 7, 70, 105),
             (510, -11, 19, 71, 106),
             (220, -9, 7, 77, 100),
             (136, 4, 14, 80, 91),
             (337, -13, 20, 106, 128)]

Sortie à temps 33:






 \ | / 


 / \ 

 - | / 




 - | - 


 / \ 





Sortie au moment 77:













 \ 






 \ 
 X 


 \ 

Sortie au moment de 93:




 \ | / 

 \ / / 

 - - - \ 




 \ 







 / \ \ 



Mise à jour: j'ai téléchargé la sortie attendue à la fois 0 thru 99 firework.ü-wie-geek.de/NUMBER.html, où NUMÉRO est le temps. Il comprend des informations de débogage, cliquez sur une particule de voir sa position actuelle, la vitesse, etc. Et oui, c'est un tréma domaine. Si votre navigateur ne peut pas gérer (comme de toute évidence ne peut Débordement de Pile), essayez de firework.xn---wie-geek-p9a.de.

Une autre mise à jour: Comme signalé dans les commentaires ci-dessous, une plus longue feu d'artifice est maintenant disponible sur YouTube. Il a été créé avec une version modifiée de MizardX' entrée, avec un total de feux d'artifice comte de 170 (oui, c'est plus que le spec demandé, mais le programme a traité avec élégance). L'exception de la couleur, de la musique et de la fin de l'écran, l'animation peut être recréé par une entrée de ce code de golf. Donc, si vous êtes geek suffit de profiter d'un art ASCII feu d'artifice (vous savez que vous êtes): Avoir du plaisir, et une bonne et heureuse année à tous!

12voto

Juan Points 2898

Voici ma solution en Python:

c = [(628, 6, 6, 3, 33),
    (586, 7, 11, 11, 23),
    (185, -1, 17, 24, 28),
    (189, 14, 10, 50, 83),
    (180, 7, 5, 70, 77),
    (538, -7, 7, 70, 105),
    (510, -11, 19, 71, 106),
    (220, -9, 7, 77, 100),
    (136, 4, 14, 80, 91),
    (337, -13, 20, 106, 128)]
t=input()
z=' '
s=([z]*79+['\n'])*23+[z]*79
def p(x,y,i,j):
    if 0<=x<790 and 0<=y<240:p=x/10+(23-y/10)*80;I=abs(i);J=abs(j);s[p]='X-|\\/'[[s[p]!=z,I>=3*J,J>=3*I,i*j<0,1].index(1)]
for x,i,j,l,d in c:
    T=t-l;x+=i*T
    if t>=d:e=t-d;[p(x+X*e,j*T+e*(Y-e+1),i+X,j+Y-2*e)for X in -20,0,20 for Y in -10,0,10]
    elif t>=l:p(x,j*T,i,j)
print ''.join(s)

Prend le temps de le stdin et il a le bon nombre de 342 caractères. Je suis encore à essayer d'imaginer comment les OP obtenu 320 :P

Edit: C'est le meilleur que j'ai pu obtenir, 322 caractères selon wc

t=input()
s=([' ']*79+['\n'])*24
def p(x,y,i,j):
 if 790>x>-1<y<240:p=x/10+(23-y/10)*80;I=abs(i);J=abs(j);s[p]='X-|\\/'[[s[p]>' ',I>=3*J,J>=3*I,i*j<0,1].index(1)]
for x,i,j,l,d in c:
 T=t-l;x+=i*T;e=t-d
 if t>=d:[p(x+X*e,j*T+e*(Y-e+1),i+X,j+Y-2*e)for X in-20,0,20for Y in-10,0,10]
 elif t>=l:p(x,j*T,i,j)
print''.join(s),

9voto

balpha Points 18387

Maintenant que le gagnant est choisi – félicitations à Juan – voici ma propre solution, 304 caractères en Python:

t=input()
Q=range
for y in Q(24):print"".join((["\\/|-"[3*(h*h>=9*v*v)or(v*v>=9*h*h)*2or h*v>0]for X,H,V,L,D in F for s,Z,K,P,u in[(t-D,t>D,t-L,G%3*20-20,G/3*10-10)for G in[Q(9),[4]][t<D]]for h,v in[[P+H,u+V-2*s*Z]]if((X+H*K+P*s)/10,23-(V*K-s*(Z*s-Z-u))/10)==(x,y)][:2]+[" ","X"])[::3][-1]for x in Q(79))

Ce n'est pas très rapide, parce que pour chaque point de la 79x24 d'affichage, il parcourt tous les artifices pour voir si l'un d'eux est visible à ce point.

Voici une version qui tente d'expliquer ce qu'il se passe:

t=input()
Q=range
for y in Q(24):
    line = ""
    for x in Q(79):
        chars = [] # will hold all characters that should be drawn at (x, y)
        for X,H,V,L,D in F: # loop through the fireworks
            s = t - D
            Z = t > D
            K = t - L

            # if t < D, i.e. the rocket hasn't exploded yet, this is just [(0, 0)];
            # otherwise it's all combinations of (-20, 0, 20) for x and (-10, 0, 10)
            speed_deltas = [(G % 3 * 20 - 20, G / 3 * 10 -10) for G in [Q(9), [4]][t < D]]

            for P, u in speed_deltas:
                if x == (X + H*K + P*s)/10 and y == 23 - (V*K - s*(Z*s - Z - u))/10:

                    # the current horizontal and vertical speed of the particle
                    h = P + H
                    v = u + V - 2*s*Z

                    # this is identical to (but shorter than) abs(h) >= 3 * abs(v)
                    is_horizontal = h*h >= 9*v*v

                    is_vertical = v*v >= 9*h*h
                    is_northeast_southwest = h*v > 0

                    # a shorter way of saying
                    # char_index = (3 if is_horizontal else 2 if is_vertical else 1
                    #               if is_northeast_southwest else 0)
                    char_index = 3 * is_horizontal or 2 * is_vertical or is_northeast_southwest

                    chars.append("\\/|-"[char_index])

        # chars now contains all characters to be drawn to this point. So we have
        # three possibilities: If chars is empty, we draw a space. If chars has
        # one element, that's what we draw. And if chars has more than one element,
        # we draw an "X".

        actual_char = (chars[:2] + [" ", "X"])[::3][-1] # Yes, this does the trick.
        line += actual_char

    print line

8voto

Markus Jarderot Points 33893

Python:

fireworks = [(628, 6, 6, 3, 33),
             (586, 7, 11, 11, 23),
             (185, -1, 17, 24, 28),
             (189, 14, 10, 50, 83),
             (180, 7, 5, 70, 77),
             (538, -7, 7, 70, 105),
             (510, -11, 19, 71, 106),
             (220, -9, 7, 77, 100),
             (136, 4, 14, 80, 91),
             (337, -13, 20, 106, 128)]
import sys
t = int(sys.argv[1])
particles = []
for x, speed_x, speed_y, launch_time, detonation_time in fireworks:
    if t < launch_time:
        pass
    elif t < detonation_time:
        x += speed_x * (t - launch_time)
        y  = speed_y * (t - launch_time)
        particles.append((x, y, speed_x, speed_y))
    else:
        travel_time = t - detonation_time
        x += (t - launch_time) * speed_x
        y  = (t - launch_time) * speed_y - travel_time * (travel_time - 1)
        for dx in (-20, 0, 20):
            for dy in (-10, 0, 10):
                x1 = x + dx * travel_time
                y1 = y + dy * travel_time
                speed_x_1 = speed_x + dx
                speed_y_1 = speed_y + dy - 2 * travel_time
                particles.append((x1, y1, speed_x_1, speed_y_1))
rows = [[' '] * 79 for y in xrange(24)]
for x, y, speed_x, speed_y in particles:
    x, y = x // 10, y // 10
    if 0 <= x < 79 and 0 <= y < 24:
        row = rows[23 - y]
        if row[x] != ' ': row[x] = 'X'
        elif speed_y == 0 or abs(speed_x) // abs(speed_y) > 2: row[x] = '-'
        elif speed_x == 0 or abs(speed_y) // abs(speed_x) > 2: row[x] = '|'
        elif speed_x * speed_y < 0: row[x] = '\\'
        else: row[x] = '/'
print '\n'.join(''.join(row) for row in rows)

Si vous supprimez la première feux d'artifice de la déclaration, compresse variable-les noms en caractères simples, et les espaces à un minimum, vous pouvez obtenir 590 caractères.

7voto

Can Berk Güder Points 39887

C:

Avec tous les espaces inutiles supprimés (632 octets excluant les feux d'artifice de la déclaration):

#define N 10
int F[][5]={628,6,6,3,33,586,7,11,11,23,185,-1,17,24,28,189,14,10,50,83,180,7,5,70,77,538,-7,7,70,105,510,-11,19,71,106,220,-9,7,77,100,136,4,14,80,91,337,-13,20,106,128};
#define G F[i]
#define R P[p]
g(x,y){if(y==0||abs(x)/abs(y)>2)return 45;if(x==0||abs(y)/abs(x)>2)return'|';if(x*y<0)return 92;return 47;}main(int A,char**B){int a,b,c,C[24][79]={},d,i,j,p=0,P[N*9][3],Q,t=atoi(B[1]),x,y;for(i=0;i<N;i++){if(t>=G[3]){a=t-G[3];x=G[0]+G[1]*a;y=G[2]*a;if(t<G[4]){R[0]=x;R[1]=y;R[2]=g(G[1],G[2]);p++;}else{b=t-G[4];y-=b*(b-1);for(c=-20;c<=20;c+=20){for(d=-10;d<=10;d+=10){R[0]=x+c*b;R[1]=y+d*b;R[2]=g(G[1]+c,G[2]+d-2*b);p++;}}}}}Q=p;for(p=0;p<Q;p++){x=R[0]/10;y=R[1]/10;if(R[0]>=0&&x<79&&R[1]>=0&&y<24)C[y][x]=C[y][x]?88:R[2];}for(i=23;i>=0;i--){for(j=0;j<79;j++)putchar(C[i][j]?C[i][j]:32);putchar(10);}}

Et voila exactement le même code avec les espaces ajoutés pour plus de lisibilité:

#define N 10

int F[][5] = {
    628, 6, 6, 3, 33,
    586, 7, 11, 11, 23,
    185, -1, 17, 24, 28,
    189, 14, 10, 50, 83,
    180, 7, 5, 70, 77,
    538, -7, 7, 70, 105,
    510, -11, 19, 71, 106,
    220, -9, 7, 77, 100,
    136, 4, 14, 80, 91,
    337, -13, 20, 106, 128
};

#define G F[i]
#define R P[p]

g(x, y) {
    if(y == 0 || abs(x)/abs(y) > 2)
        return 45;
    if(x == 0 || abs(y)/abs(x) > 2)
        return '|';
    if(x*y < 0)
        return 92;
    return 47;
}

main(int A, char**B){
    int a, b, c, C[24][79] = {}, d, i, j, p = 0, P[N*9][3], Q, t = atoi(B[1]), x, y;

    for(i = 0; i < N; i++) {
        if(t >= G[3]) {
            a = t - G[3];
            x = G[0] + G[1]*a;
            y = G[2]*a;
            if(t < G[4]) {
                R[0] = x;
                R[1] = y;
                R[2] = g(G[1], G[2]);
                p++;
            } else {
                b = t - G[4];
                y -= b*(b-1);
                for(c = -20; c <= 20; c += 20) {
                    for(d =- 10; d <= 10; d += 10) {
                        R[0] = x + c*b;
                        R[1] = y + d*b;
                        R[2] = g(G[1] + c, G[2] + d - 2*b);
                        p++;
                    }
                }
            }
        }
    }

    Q = p;

    for(p = 0; p < Q; p++) {
        x = R[0]/10;
        y = R[1]/10;
        if(R[0] >= 0 && x < 79 && R[1] >= 0 && y < 24)
            C[y][x] = C[y][x] ? 88 : R[2];
    }

    for(i = 23; i >= 0; i--) {
        for(j = 0; j < 79; j++)
            putchar(C[i][j] ? C[i][j] : 32);
        putchar(10);
    }
}

7voto

Alex Martelli Points 330805

Pour Python, @MizardX la solution, c'est sympa, mais clairement pas codegolf-optimisé -- outre le "ne compte pas" 333 caractères du préfixe, à savoir:

fireworks = [(628, 6, 6, 3, 33),
    (586, 7, 11, 11, 23),
    (185, -1, 17, 24, 28),
    (189, 14, 10, 50, 83),
    (180, 7, 5, 70, 77),
    (538, -7, 7, 70, 105),
    (510, -11, 19, 71, 106),
    (220, -9, 7, 77, 100),
    (136, 4, 14, 80, 91),
    (337, -13, 20, 106, 128)]
f = fireworks
### int sys argv append abs join f xrange

(le dernier commentaire est un helper pour un peu de codegolf-aux script de la mine qui le rend possible des noms 1-char mécaniquement -- il doit être dit que les noms de ne PAS rapetisser;-), la plus courte, je peux rendre cette solution en serrant les espaces sont 592 caractères (assez proche de la 590 @MizardX réclamations).

Tirant tous les arrêts ("refactoring" le code dans un codegolf de l'humeur), j'obtiens, après le préfixe (j'ai utilisé des minuscules pour un seul des noms de personnage, je suis manuellement l'introduction ou à la substitution, en majuscules pour ceux de mon codegolf-aux script remplacé automatiquement):

import sys 
Z=int(sys.argv[1])
Y=[]
e=Y.extend
for X,W,V,U,T in f:
 if Z>=U:
  z=Z-U;X+=W*z
  if Z<T:e(((X,V*z,W,V),))
  else:R=Z-T;e((X+Q*R,z*V-R*(R-1)+P*R,W+Q,V+P-2*R)for Q in(-20,0,20)for P in(-10,0,10))
K=[79*[' ']for S in range(24)]
for X,S,W,V in Y:
 X,S=X/10,S/10 
 if(0<=X<79)&(0<=S<24):
  J=K[23-S];v=abs(V);w=abs(W)
  J[X]='X'if J[X]!=' 'else'-'if V==0 or w/v>2 else'|'if W==0 or v/w>2 else '\\'if W*V<0 else'/'
print '\n'.join(''.join(J)for J in K)

qui mesure à 460 caractères -- c'est une réduction de 130, c'est à dire 130/590 = 22%.

Au-delà de 1-les noms des personnages et des manières de réduire l'espacement, les idées clés comprennent: simple / pour la division (la même que sur les plus agréables // pour ints en Python 2.*), un if/else expression en lieu et place de l' if/elif/else déclaration, extend avec un genexp plutôt que d'une boucle imbriquée avec append (permet l'élimination de certains espaces et signes de ponctuation), pas de liaison à un nom de sous-expressions qui se produisent qu'une seule fois, la liaison à un nom de sous-expressions qui seraient autrement répétés (y compris l' .extend attribut de recherche), les points-virgules plutôt que des retours à la ligne lorsque cela est possible (seulement si les lignes distinctes devraient être en retrait, sinon, compter un retour à la ligne 1 de caractères, il n'y a pas de sauvegarde).

Yep, la lisibilité souffre un peu, mais ce n'est guère surprenant dans le code de golf;-).

Edit: après beaucoup plus de serrage, j'ai maintenant un petit programme (même préfixe):

Z=input()
K=[79*[' ']for S in range(24)];a=-10,0,10
def g(X,S,W,V):
 X/=10;S/=10
 if(0<=X<79)&(0<=S<24):J=K[23-S];v=abs(V);w=abs(W);J[X]=[[['/\\'[W*V<0],'|'][v>2.9*w],'-'][w>2.9*v],'X'][J[X]!=' ']
for X,W,V,U,T in f:
 if Z>=U:
  z=Z-U;X+=W*z
  if Z<T:g(X,V*z,W,V)
  else:R=Z-T;[g(X+Q*2*R,z*V-R*(R-1)+P*R,W+Q*2,V+P-2*R)for Q in a for P in a]
print'\n'.join(''.join(J)for J in K)

Toujours le même résultat, mais maintenant 360 caractères -- exactement 100 de moins que ma solution précédente, que j'ai quitté la première partie de cette réponse (toujours bien au-dessus de 320 l'OP dit qu'il a, tout de même!-).

J'ai profité du degré de liberté permettant l'entrée de valeur de temps à venir à partir de stdin (input est beaucoup plus serré que d'importer sys et à l'aide de sys.argv[1]!-), éliminé l'intermédiaire de la liste (w/étendre les appels et une dernière boucle) en faveur de la nouvelle fonction g qui est appelée directement et mises à jour K que nous allons, a trouvé et enlevé quelques points communs, remaniée imbriquée si/d'autre expression dans un complexe (mais plus concis;-) la construction et l'indexation des listes imbriquées, utilisé le fait que l' v>2.9*w est plus concis qu' w==0 or v/w>2 (et donne toujours le même résultat dans la gamme de valeurs qui doivent être pris en compte).

Edit: rendre K (l'écran "image") dans un 1-D de la liste permet d'économiser plus de 26 caractères, rétrécissement de la solution suivante pour 334 (encore 14 au-dessus de l'OP, mais la fermeture...!-):

Z=input()
K=list(24*(' '*79+'\n'))
a=-10,0,10
def g(X,S,W,V):
 if(0<=X<790)&(0<=S<240):j=80*(23-S/10)+X/10;v=abs(V);w=abs(W);K[j]=[[['/\\'[W*V<0],'|'][v>2.9*w],'-'][w>2.9*v],'X'][K[j]!=' ']
for X,W,V,U,T in f:
 if Z>=U:
  z=Z-U;X+=W*z
  if Z<T:g(X,V*z,W,V)
  else:R=Z-T;[g(X+Q*2*R,z*V-R*(R-1)+P*R,W+Q*2,V+P-2*R)for Q in a for P in a]
print ''.join(K),

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X