views:

2582

answers:

15

The year 2009 is coming to an end, and with the economy and all, we'll save our money and instead of buying expensive fireworks, we'll celebrate in ASCII art this year.

The challenge

Given a set of fireworks and a time, take a picture of the firework at that very time and draw it to the console.

The best solution entered before midnight on New Year's Eve (UTC) will receive a bounty of 500 rep. This is code golf, so the number of characters counts heavily; however so do community votes, and I reserve the ultimate decision as to what is best/coolest/most creative/etc.

Input Data

Note that our coordinate system is left-to-right, bottom-to-top, so all fireworks are launched at a y-coordinate of 0 (zero).

The input data consists of fireworks of the form

(x, speed_x, speed_y, launch_time, detonation_time)

where

  • x is the position (column) where the firework is launched,
  • speed_x and speed_y are the horizontal and vertical velocity of the firework at launch time,
  • launch_time is the point in time that this firework is launched,
  • detonation_time is the point in time that this firework will detonate.

The firework data may be hardcoded in your program as a list of 5-tuples (or the equivalent in your language), not counting towards your character count. It must, however, be easy to change this data.

You may make the following assumptions:

  • there is a reasonable amount of fireworks (say, fewer then a hundred)
  • for each firework, all five numbers are integers within a reasonable range (say, 16 bits would suffice for each),
  • -20 <= x <= 820
  • -20 <= speed_x <= 20
  • 0 < speed_y <= 20
  • launch_time >= 0
  • launch_time < detonation_time < launch_time + 50

The single additional piece of input data is the point of time which is supposed to be rendered. This is a non-negative integer that is given to you via standard input or command line argument (whichever you choose).

The idea is that (assuming your program is a python script called firework.py) this bash script gives you a nice firework animation:

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

(feel free to put the equivalent .BAT file here).

Life of a firework

The life of a firework is as follows:

  • Before the launch time, it can be ignored.
  • At launch time, the rocket has the position (x, 0) and the speed vector (speed_x, speed_y).
  • For each time step, the speed vector is added to the position. With a little stretch applied to Newton's laws, we assume that the speed stays constant.
  • At detonation time, the rocket explodes into nine sparks. All nine sparks have the same position at this point in time (which is the position that the rocket would have, hadn't it exploded), but their speeds differ. Each speed is based on the rocket's speed, with -20, 0, or 20 added to speed_x and -10, 0, or 10 added to speed_y. That's nine possible combinations.
  • After detonation time, gravity starts to pull: With each time step, the gravitational constant, which happens to be 2 (two), is subtracted from every spark's speed_y. The horizontal speed_x stays constant.
  • For each time step after the detonation time, you first add the speed vector to the position, then subtract 2 from speed_y.
  • When a spark's y position drops below zero, you may forget about it.

Output

What we want is a picture of the firework the way it looks at the given point in time. We only look at the frame 0 <= x <= 789 and 0 <= y <= 239, mapping it to a 79x24 character output.

So if a rocket or spark has the position (247, 130), we draw a character in column 24 (zero-based, so it's the 25th column), row 13 (zero-based and counting from the bottom, so it's line 23 - 13 = 10, the 11th line of the output).

Which character gets drawn depends on the current speed of the rocket / spark:

  • If the movement is horizontal*, i.e. speed_y == 0 or abs(speed_x) / abs(speed_y) > 2, the character is "-".
  • If the movement is vertical*, i.e. speed_x == 0 or abs(speed_y) / abs(speed_x) > 2, the character is "|".
  • Otherwise the movement is diagonal, and the character is "\" or "/" (you'll guess the right one).
  • If the same position gets drawn to more than once (even if it's the same character), we put "X" instead. So assuming you have a spark at (536, 119) and one at (531, 115), you draw an "X", regardless of their speeds.

* update: these are integer divisions, so the slope has to be at least 3, or at most 1/3, respectively

The output (written to standard output) is 24 lines, each terminated by a newline character. Trailing spaces are ignored, so you may, but don't need to, pad to a width of 79. The lines may not be longer than 79 characters (excluding the newline). All interior spacing must be space characters (ASCII 32).

Sample Data

Fireworks:

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

Output at time 33:






       \         |         /                                                   


                                                      /                   \    

       -         |         /                                                   




       -         |         -                                                   


                                                      /                   \    





Output at time 77:













                                            \                                  






                                                \                              
                      X                                                        


                      \                                                        

Output at time 93:




              \   |   /                                                        

              \   /   /                                                        

              -   -   -              \                                         




       \                                                                       







  /                               \                               \            



Update: I have uploaded the expected output at the times 0 thru 99 to firework.ü-wie-geek.de/NUMBER.html, where NUMBER is the time. It includes debug information; click on a particle to see its current position, speed, etc. And yes, it's an umlaut domain. If your browser can't handle that (as obviously neither can Stack Overflow), try firework.xn---wie-geek-p9a.de.

Another update: As hinted at in the comments below, a longer firework is now available on YouTube. It was created with a modified version of MizardX' entry, with a total fireworks count of 170 (yes, that's more than the spec asked for, but the program handled it gracefully). Except for the color, the music, and the end screen, the animation can be recreated by any entry to this code golf. So, if you're geeky enough to enjoy an ASCII art firework (you know you are): Have fun, and a happy new year to all!

+1  A: 

First Post hahaha http://zipts.com/position.php?s=0 not my final submission but could not resist

Btw: Characters 937 not counting spaces (do we count spaces? )

MindStalker
Please use a fixed-width font. <pre> or <tt> is good for this.
MizardX
Ok fixed the width, and squashed a few bugs. My rendering is very slightly different than his in some frames, but I'm going to stick with my version of reality for now! :)
MindStalker
Yes, spaces usually do count.
balpha
A: 

In your reference implementation (firework.ü-wie-geek.de/NUMBER.html), movement takes place before gravity affects velocity. Maybe this should be noted.

danju
Yea, he said that in his description
MindStalker
I sure did; I have put the relevant words in bold now rather than in italics to make it more pronounced.
balpha
this should be a comment.
Can Berk Güder
this should also be a comment - oh, wait? it is
Jan
@Jan J: no, I mean this answer should be a comment to the question.
Can Berk Güder
+8  A: 

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)

If you remove the initial fireworks declaration, compress variable-names to single characters, and whitespace to a minimum, you can get 590 characters.

MizardX
I'm getting differences starting at time 24 (i.e. after the first detonation)
balpha
It's your line 24: It should be `y = (t - launch_time) * speed_y - (travel_time) * (travel_time - 1)` (the Gauss sum formula is `sum(1,n)=n*(n+1)/2`). After that fix, it passes the test. Congrats!
balpha
+6  A: 

Haskell

import Data.List
f=[(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)]
c=filter
d=True
e=map
a(_,_,_,t,_)=t
b(_,_,_,_,t)=t
aa(_,y,_,_)=y
ab(x,t,y,_,u)=(x,0,t,y,u)
ac(x,y,t,u,_)=[(x,y,t+20,u+10),(x,y,t,u+10),(x,y,t-20,u+10),(x,y,t+20,u),(x,y,t,u),(x,y,t-20,u),(x,y,t+20,u-10),(x,y,t,u-10),(x,y,t-20,u-10)]
g(x,y,t,u,v)=(x+t,y+u,t,u,v)
h(x,y,t,u)=(x+t,y+u,t,u-2)
i=(1,f,[],[])
j s 0=s
j(t,u,v,w)i=j(t+1,c((/=t).a)u,c((> t).b)x++(e ab.c((==t).a))u,c((>0).aa)(e h w)++(concat.e ac.c((==t).b))x)(i-1)
 where x=e g v
k x y
 |x==0='|'
 |3*abs y<=abs x='-'
 |3*abs x<=abs y='|'
 |(y<0&&x>0)||(y>0&&x<0)='\\'
 |d='/'
l(x,y,t,u,_)=m(x,y,t,u)
m(x,y,t,u)=(div x 10,23-div y 10,k t u)
n(x,y,_)(u,v,_)
 |z==EQ=compare x u
 |d=z
 where z=compare y v
o((x,y,t):(u,v,w):z)
 |x==u&&y==v=o((x,y,'X'):z)
 |d=(x,y,t):(o((u,v,w):z))
o x=x
q _ y []
 |y==23=""
 |d='\n':(q 0(y+1)[])
q v u((x,y,z):t)
 |u>22=""
 |v>78='\n':(q 0(u+1)((x,y,z):t))
 |u/=y='\n':(q 0(u+1)((x,y,z):t))
 |v/=x=' ':(q(v+1)u((x,y,z):t))
 |d = z:(q(v+1)u t)
p(_,_,v,w)=q 0 0((c z.o.sortBy n)((e l v)++(e m w)))
 where z(x,y,_)=x>=0&&x<79&&y>=0
r x=do{z <- getChar;(putStr.p)x}
s=e(r.j i)[1..]
main=foldr(>>)(return())s

Not nearly as impressive as MizardX's, coming in at 1068 characters if you remove the f=… declaration, but hell, it was fun. It's been a while since I've had a chance to play with Haskell.

The (slightly) prettier version is also available.

Edit: Ack. Rereading, I don't quite meet the spec.: this version prints a new screen of firework display every time you press a key, and requires ^C to quit; it doesn't take a command line argument and print out the relevant screen.

me_and
I'm installing a Haskell interpreter right now! -- That link to the prettier version leads to an infinite recursion.
balpha
Yep, you didn't meet the spec by 100%, but other than that, the output looks correct to me. Now I just have to de-knot my brain from looking at that code for too long.
balpha
Okay, here's the result of the thorough test: The output is correct except that you only output 23 lines.
balpha
Do I? Damn. I'll correct that if/when I change it to match the spec. The link's been updated now, anyway.
me_and
+7  A: 

C:

With all unnecessary whitespace removed (632 bytes excluding the fireworks declaration):

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

And here's the exact same code with whitespace added for readability:

#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);
    }
}
Can Berk Güder
Any hints for a non-C guru? For frames 0 to 2 I get a whole lot of crap printed to the screen (I solved this by initializing C with zeros); starting at time 3, I get a segfault. GCC 4.2.2 on Fedora 12.
balpha
hmm. do you know where it segfaults?
Can Berk Güder
btw, I just fixed a small bug in function g(x,y).
Can Berk Güder
Found it: Had to initialize `p` to zero as well; works now.
balpha
strange. the initial value of non-static variables declared in a function is undefined per the C standard, but my GCC initializes them to 0, while yours doesn't.
Can Berk Güder
in any case, this only adds 6 more bytes to the code. I'll change it now.
Can Berk Güder
Output is almost correct; I'm getting differences at frames 37, 42, 50, 87 and 95. You put characters for particles that have negative coordinates (since e.g. `-4 / 10 == 0`). Other than that: +1
balpha
you're right. I fixed it now, adding another 12 bytes.
Can Berk Güder
More golfing: replace character constants by their ASCII codes (e.g. `'\\'` by 92), and use default-int declarations.
Adam Rosenfield
@Adam: nice one. =) that, and making C an int array (instead of char) saves 29 bytes.
Can Berk Güder
+7  A: 

For Python, @MizardX's solution is nice, but clearly not codegolf-optimized -- besides the "don't really count" 333 characters of the prefix, namely:

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

(the last comment is a helper for a little codegolf-aux script of mine that makes all feasible names 1-char mechanically -- it needs to be told what names NOT to minify;-), the shortest I can make that solution by squeezing whitespace is 592 characters (close enough to the 590 @MizardX claims).

Pulling out all the stops ("refactoring" the code in a codegolf mood), I get, after the prefix (I've used lowercase for single-character names I'm manually introducing or substituting, uppercase for those my codegolf-aux script substituted automatically):

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)

which measures in at 460 characters -- that's a reduction of 130, i.e. 130/590 = 22%.

Beyond 1-character names and obvious ways to minimize spacing, the key ideas include: single / for division (same as the nicer // for ints in Python 2.*), an if/else expression in lieu of an if/elif/else statement, extend with a genexp rather than a nested loop with append (allows the removal of some spaces and punctuation), not binding to a name subexpressions that occur just once, binding to a name subexpressions that would otherwise get repeated (including the .extend attribute lookup), semicolons rather than newlines where feasible (only if the separate lines would have to be indented, otherwise, counting a newline as 1 character, there is no saving).

Yep, readability suffers a bit, but that's hardly surprising in code golf;-).

Edit: after a lot more tightening, I now have a smaller program (same prefix):

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)

Still the same output, but now 360 characters -- exactly 100 fewer than my previous solution, which i've left as the first part of this answer (still well above the 320 the OP says he has, though!-).

I've taken advantage of the degree of freedom allowing the input-time value to come from stdin (input is much tighter than importing sys and using sys.argv[1]!-), eliminated the intermediate list (w/the extend calls and a final loop of it) in favor of the new function g which gets called directly and updates K as we go, found and removed some commonality, refactored the nested if/else expression into a complicated (but more concise;-) building and indexing of nested lists, used the fact that v>2.9*w is more concise than w==0 or v/w>2 (and always gives the same result in the range of values that are to be considered).

Edit: making K (the "screen image") into a 1-D list saves a further 26 characters, shrinking the following solution to 334 (still 14 above the OP's, but closing up...!-):

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),
Alex Martelli
An honor to see you here, Alex :-) Btw, my own Python solution (I'll post it after the winner is chosen) is currently aroung 320 chars--and I have yet to see a code golf won by a Python program, so I'm looking forward to more entries.
balpha
For the record: Your output is correct.
balpha
@balpha, pls see my second version (I edited the answer but also left the previous one in) -- I believe the output is still correct (identical to the previous version's for a vast range of times) and I'm now down to 360, but still far from your 320...!-)
Alex Martelli
...and now down to 334 (by making K into a 1-D list of chars)... still not as good as yours, @balpha, but, closing in...!-)
Alex Martelli
Yep, output is still correct. I count 351 chars, though.
balpha
+7  A: 

Done in F# in 957* characters, and it's ugly as sin:

Array of fireworks:

let F = [(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)]

Remaining code

let M=List.map
let C=List.concat
let P=List.partition
let L t f r=(let s=P(fun(_,_,_,u,_)->not(t=u))f
(fst s, r@(M(fun(x,v,w,_,t)->x,0,v,w,t)(snd s))))
let X d e (x,y,v,w)=C(M(fun(x,y,v,w)->[x,y,v-d,w;x,y,v,w;x,y,v+d,w])[x,y,v,w-e;x,y,v,w;x,y,v,w+e])
let D t r s=(let P=P(fun(_,_,_,_,u)->not(t=u))r
(fst P,s@C(M(fun(x,y,v,w,_)->(X 20 10(x,y,v,w)))(snd P))))
let rec E t l f r s=(
let(a,m)=L t f (M(fun(x,y,v,w,t)->x+v,y+w,v,w,t)r)
let(b,c)=D t m (M(fun(x,y,v,w)->x+v,y+w,v,w-2)s)
if(t=l)then(a,b,c)else E(t+1)l a b c)
let N=printf
let G t=(
let(f,r,s)=E 0 t F [] []
let os=s@(M(fun(x,y,v,w,_)->(x,y,v,w))r)
for y=23 downto 0 do (
for x=0 to 79 do (
let o=List.filter(fun(v,w,_,_)->((v/10)=x)&&((w/10)=y))os
let l=o.Length
if l=0 then N" "
elif l=1 then
let(_,_,x,y)=o.Head
N(
if y=0||abs(x)/abs(y)>2 then"-"
elif x=0||abs(y)/abs(x)>2 then"|"
elif y*x>0 then"/"
else"\\")
elif o.Length>1 then N"X")
N"\n"))
[<EntryPointAttribute>]
let Z a=
 G (int(a.[0]))
 0

"Pretty" code:

let fxs  = [(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)]

let movs xs = 
  List.map (fun (x, y, vx, vy) -> (x + vx, y + vy, vx, vy-2)) xs

let movr xs =
  List.map (fun (x, y, vx, vy, dt) -> (x + vx, y + vy, vx, vy, dt)) xs

let launch t fs rs =
  let split = List.partition(fun (lx, sx, sy, lt, dt) -> not (t = lt)) fs
  (fst split, rs @ (List.map(fun (lx, sx, sy, lt, dt) -> (lx, 0, sx, sy, dt)) (snd split)))

let split dx dy (x,y,sx,sy) =
  List.concat (List.map (fun (x,y,sx,sy)->[(x,y,sx-dx,sy);(x,y,sx,sy);(x,y,sx+dx,sy)]) [(x,y,sx,sy-dy);(x,y,sx,sy);(x,y,sx,sy+dy)])

let detonate t rs ss =
  let tmp = List.partition (fun (x, y, sx, sy, dt) -> not (t = dt)) rs
  (fst tmp, ss @ List.concat (List.map(fun (x, y, sx, sy, dt) -> (split 20 10 (x, y, sx, sy))) (snd tmp)))

let rec simulate t l fs rs ss =
  let (nfs, trs) = launch t fs (movr rs)
  let (nrs, nss) = detonate t trs (movs ss)
  if (t = l) then (nfs,nrs,nss)
  else 
    simulate (t+1) l nfs nrs nss

let screen t =
  let (fs, rs, ss) = simulate 0 t fxs [] []
  let os = ss @ (List.map(fun (x, y, sx, sy,_) -> (x, y, sx, sy)) rs)
  for y = 23 downto 0 do 
    for x = 0 to 79 do
      let o = List.filter(fun (px,py,_,_)->((px/10)=x) && ((py/10)=y)) os
      if o.Length = 0 then printf " "
      elif o.Length = 1 then
        let (_,_,sx,sy) = o.Head
        printf (
          if sy = 0 || abs(sx) / abs(sy) > 2 then "-"
          elif sx = 0 || abs(sy) / abs(sx) > 2 then "|"
          elif sy * sx > 0 then "/"
          else"\\"
        )
      elif o.Length > 1 then printf "X"
    printfn ""

[<EntryPointAttribute>]
let main args =
  screen (int(args.[0]))
  0

Completely stolenrewritten with new and improved logic. This is as close as I could get to Python. You can see the weakness of F# not being geared toward ad hoc scripting here, where I have to explicitly convert V and W to a float, declare a main function with an ugly attribute to get the command line args, and I have to reference the .NET System.Console.Write to get a pretty output.

Oh well, good exercise to learn a language with.

Here's the new code, at 544 bytes:

let Q p t f=if p then t else f
let K=[|for i in 1..1920->Q(i%80>0)' ''\n'|]
let g(X,S,W,V)=
 if(X>=0&&X<790&&S>=0&&S<240)then(
let (j,v,w)=(80*(23-S/10)+X/10,abs(float V),abs(float W))
Array.set K j (Q(K.[j]=' ')(Q(w>2.9*v)'-'(Q(v>2.9*w)'|'(Q(W*V>0)'/''\\')))'X'))
let a=[-10;0;10]
[<EntryPointAttribute>]
let m s=
 let Z=int s.[0]
 for (X,W,V,U,T) in F do(
if Z>=U then
 let z,R=Z-U,Z-T
 let x=X+W*z
 if(Z<T)then(g(x,V*z,W,V))else(for e in[|for i in a do for j in a->(x+j*2*R,z*V-R*(R-1)+i*R,W+j*2,V+i-2*R)|]do g e))
 System.Console.Write K
 0
What's the easiest way for me to try this without installing a whole lot of stuff? E.g. some simple changes to make it OCaml compatible, or could you put up a compiled version somewhere?
balpha
Excellent, thanks -- works great, and your output is correct.
balpha
The package you download from Microsoft can be used to extend Mono so it can compile F#.
Callum Rogers
@Callum Rogers: Yes, that's exactly what I did, and it worked like a charm.
balpha
+12  A: 

Heres my solution in 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)

Takes the time from the stdin and it has the nice number of 342 characters. I'm still trying to imagine how the OP got 320 :P

Edit: This is the best I could get, 322 chars acording to 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),
Juan
Looks good! Output is correct, and I even count only 341 characters. (Did you count the final newline? It's not neccessary in a Python script).
balpha
Got it down to 321, will be posting it soon. I'm looking at it hard to see if can remove some more chars.
Juan
Good job! Output is still correct, and I count 321 chars. You're in the lead---let's see if we get another entry today.
balpha
+5  A: 
jsoverson
Hmm... something's not right... Your program gives out lines of length 157...
balpha
Yes, forgot to modify the list separator. Was waiting until the end to see if i could squeeze that elsewhere, and forgot to add it in before posting. It adds 6 characters now, but one of the other variables could probably use the list separator as long as it gets left undefined before the print. I'll look into that when I get a chance.
jsoverson
I feel really stupid, but the little bit of perl I once used to know is totally failing me. How in the name of [insert something funny] do I declare the firework data such that I can get this to work?
balpha
Edited to add the definition. This will teach me to post in haste!
jsoverson
Or teach me Perl, for that matter :-) Anyway, output is *almost* correct, except you output one line to many, and you have the same 37, 42, 50, 87, 95 error as Can Berk Güder had in his C solution: http://stackoverflow.com/questions/1947031/code-golf-new-years-fireworks/1963639#1963639
balpha
Fixed those, and ran tests against your 99 test files and all came up good. Cut a few characters to get it down to 433.
jsoverson
Good job---no more complaints from me :-)
balpha
Nice bit on the int-only perlvars, never knew that.
jsoverson
+2  A: 

Here's a smaller Haskell implementation. It's 911 characters; minus the fireworks definition, it's 732 characters:

import System
z=789
w=239
r=replicate
i=foldl
main=do{a<-getArgs;p(f[(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)](read(a!!0)::Int));}
p[]=return()
p(f:g)=do{putStrLn f;p g}
f s t=i(a t)(r 24(r 79' '))s
a t f(x,s,y,l,d)=if t<l then f else if t<d then c f((x+s*u,y*u),(s,y))else i c f(map(v(t-d)(o(d-l)(x,0)(s,y)))[(g s,h y)|g<-[id,(subtract 20),(+20)],h<-[id,(subtract 10),(+10)]])where u=t-l
v 0(x,y)(vx,vy)=((x,y),(vx,vy))
v t(x,y)(vx,vy)=v(t-1)(x+vx,y+vy)(vx,vy-2)
o t(x,y)(vx,vy)=(x+(vx*t),y+(vy*t))
c f((x,y),(vx,vy))=if x<0||x>=z||y<0||y>=w then f else(take m f)++[(take n r)++[if d/=' 'then 'x'else if vy==0||abs(vx`div`vy)>2 then '-'else if vx==0||abs(vy`div`vx)>2 then '|'else if vx*vy>=0 then '/'else '\\']++(drop(n+1)r)]++(drop(m+1)f)where{s=w-y;n=x`div`10;m=s`div`10;r=f!!m;d=r!!n}

Here's the non-compressed version for the curious:

import System

sizeX = 789
sizeY = 239

main = do
    args <- getArgs
    printFrame (frame fireworks (read (args !! 0) :: Int))
    where 
        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)]

printFrame :: [String] -> IO ()
printFrame [] = return ()
printFrame (f:fs) = do
    putStrLn f
    printFrame fs

frame :: [(Int,Int,Int,Int,Int)] -> Int -> [String]
frame specs time = 
    foldl (applyFirework time) 
        (replicate 24 (replicate 79 ' ')) specs

applyFirework :: Int -> [String] -> (Int,Int,Int,Int,Int) -> [String]
applyFirework time frame (x,sx,sy,lt,dt) =
    if time < lt then frame
    else if time < dt then 
        drawChar frame 
            ((x + sx * timeSinceLaunch, sy * timeSinceLaunch), (sx,sy))
    else
        foldl drawChar frame 
            (
                map 
                    (
                        posVelOverTime (time - dt) 
                        (posOverTime (dt - lt) (x,0) (sx, sy))
                    ) 
                    [ 
                        (fx sx, fy sy) | 
                            fx <- [id,(subtract 20),(+20)],  
                            fy <- [id,(subtract 10),(+10)]
                    ]
            )
    where timeSinceLaunch = time - lt

posVelOverTime :: Int -> (Int,Int) -> (Int,Int) -> ((Int,Int),(Int,Int))
posVelOverTime 0 (x,y) (vx,vy) = ((x,y),(vx,vy))
posVelOverTime time (x,y) (vx,vy) = 
    posVelOverTime (time - 1) (x+vx, y+vy) (vx, vy - 2)

posOverTime :: Int -> (Int,Int) -> (Int,Int) -> (Int,Int)
posOverTime time (x,y) (vx, vy) = (x + (vx * time), y + (vy * time))

drawChar :: [String] -> ((Int,Int),(Int,Int)) -> [String]
drawChar frame ((x,y),(vx,vy)) =
    if x < 0 || x >= sizeX || y < 0 || y >= sizeY then frame
    else 
        (take mappedY frame) 
        ++ 
            [
                (take mappedX row) 
                ++ 
                    [
                        if char /= ' '                           then 'x'
                        else if vy == 0 || abs (vx `div` vy) > 2 then '-'
                        else if vx == 0 || abs (vy `div` vx) > 2 then '|'
                        else if vx * vy >= 0                     then '/'
                        else                                          '\\'
                    ]
                ++ (drop (mappedX + 1) row) 
            ] 
        ++ (drop (mappedY + 1) frame)
    where 
        reversedY = sizeY - y
        mappedX = x `div` 10
        mappedY = reversedY `div` 10
        row = frame !! mappedY
        char = row !! mappedX
Jacob
+2  A: 

First draft in Tcl8.5 913 bytes excluding fireworks definition:

set F {
    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
}

namespace import tcl::mathop::*
proc @ {a args} {interp alias {} $a {} {*}$args}
@ : proc
@ = set
@ D d p
@ up upvar 1
@ < append out
@ _ foreach
@ e info exists
@ ? if
: P {s d t l} {+ $s [* $d [- $t $l]]}
: > x {= x}
: d {P x X y Y} {up $P p
= x [/ $x 10]
= y [/ $y 10]
= p($x,$y) [? [e p($x,$y)] {> X} elseif {
$Y==0||abs($X)/abs($Y)>2} {> -} elseif {
$X==0||abs($Y)/abs($X)>2} {> |} elseif {
$X*$Y<0} {> \\} {> /}]}
: r {P} {up $P p
= out ""
for {= y 23} {$y >= 0} {incr y -1} {
for {= x 0} {$x < 79} {incr x} {? {[e p($x,$y)]} {< $p($x,$y)} {< " "}}
< "\n"}
puts $out}
: s {F t} {array set p {}
_ {x X Y l d} $F {? {$t >= $l} {? {$t < $d} {= x [P $x $X $t $l]
= y [P 0 $Y $t $l]
D $x $X $y $Y} {= x [P $x $X $d $l]
= y [P 0 $Y $d $l]
= v [- $t $d]
_ dx {-20 0 20} {_ dy {-10 0 10} {= A [+ $X $dx]
= B [- [+ $Y $dy] [* 2 $v]]
= xx [P $x $A $v 0]
= yy [P $y $B $v 0]
D $xx $A $yy $B}}}}}
r p}
s $F [lindex $argv 0]

Optimized to the point of unreadability. Still looking for room to improve. Most of the compression basically uses command aliasing substituting single characters for command names. For example, function definitions are done using Forth-like : syntax.

Here's the uncompressed version:

namespace import tcl::mathop::*

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

proc position {start speed time launch} {
    + $start [* $speed [- $time $launch]]
}

proc give {x} {return $x}

proc draw {particles x speedX y speedY} {
    upvar 1 $particles p
    set x [/ $x 10]
    set y [/ $y 10]
    set p($x,$y) [if [info exists p($x,$y)] {
            give X
        } elseif {$speedY == 0 || abs(double($speedX))/abs($speedY) > 2} {
            give -
        } elseif {$speedX == 0 || abs(double($speedY))/abs($speedX) > 2} {
            give |
        } elseif {$speedX * $speedY < 0} {
            give \\
        } else {
            give /
        }
    ]
}

proc render {particles} {
    upvar 1 $particles p
    set out ""
    for {set y 23} {$y >= 0} {incr y -1} {
        for {set x 0} {$x < 79} {incr x} {
            if {[info exists p($x,$y)]} {
                append out $p($x,$y)
            } else {
                append out " "
            }
        }
        append out "\n"
    }
    puts $out
}

proc show {fireworks time} {
    array set particles {}
    foreach {x speedX speedY launch detonate} $fireworks {
        if {$time >= $launch} {
            if {$time < $detonate} {
                set x [position $x $speedX $time $launch]
                set y [position 0 $speedY $time $launch]
                draw particles $x $speedX $y $speedY
            } else {
                set x [position $x $speedX $detonate $launch]
                set y [position 0 $speedY $detonate $launch]
                set travel [- $time $detonate]
                foreach dx {-20 0 20} {
                    foreach dy {-10 0 10} {
                        set speedXX [+ $speedX $dx]
                        set speedYY [- [+ $speedY $dy] [* 2 $travel]]
                        set xx [position $x $speedXX $travel 0]
                        set yy [position $y $speedYY $travel 0]
                        draw particles $xx $speedXX $yy $speedYY
                    }
                }
            }
        }
    }
    render particles
}
show $fireworks [lindex $argv 0]
slebetman
+9  A: 
balpha
Wow, that's some serious voodoo magic you do there :P Especially the actual_char bit.
Juan
That's purely functional too, isn't it?
Carl Smotricz
@Carl Smotrizc: I guess you can say that; although a one-line program probably fits into many categories easily. I'm not a fan of paradigm labels.
balpha
+3  A: 
Carl Smotricz
You, Sir, need help :-) Anyway, I'll be installing a Fortran compiler today...
balpha
+1  A: 

Clojure

Unindented, without input output and unnecessary whitespace, it comes to 640 characters - exactly double the best value :( Thus, I'm not providing a "blank optimized" version in an attempt to win at brevity.

(def fw [
[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]
])
(defn rr [x y u v dt g] (if (<= dt 0) [x y u v] (recur (+ x u) (+ y v) u (+ v g) (dec dt) g)))

(defn pp [t f]
  (let [y 0 [x u v a d] f r1 (rr x y u v (- (min t d) a) 0)]
    (if (< t a)
      '()
      (if (< t d)
        (list r1)
        (for [m '(-20 0 20) n '(-10 0 10)]
          (let [[x y u v] r1]
            (rr x y (+ u m) (+ v n) (- t d) -2)))))))

(defn at [x y t]
  (filter #(and (= x (quot (first %) 10)) (= y (quot (second %) 10))) (apply concat (map #(pp t %) fw))))

(defn g [h]
  (if (empty? h) \space
    (if (next h) \X
      (let [[x y u v] (first h)]
        (cond
          (or (zero? v) (> (* (/ u v) (/ u v)) 4)) \-
          (or (zero? u) (> (* (/ v u) (/ v u)) 4)) \|
          (= (neg? u) (neg? v)) \/
          :else \\
        )))))

(defn q [t]
  (doseq [r (range 23 -1 -1)]
    (doseq [c (range 0 80)]
      (print (g (at c r t))))
    (println)))

(q 93)
Carl Smotricz
Any more interpreters I should prepare to install, besides Fortran and Clojure? Are you maybe working on a COBOL solution? :-P
balpha
I was going to do COBOL as my "historic" entry, and would have easily hit the 10K character mark, but my COBOL is so rusty I wasn't sure if I'd be able to pull it off, so I opted for my "other" historic language. Also, since the deadline has passed, I don't think I'm doing any more.
Carl Smotricz
+1  A: 

My answer is at http://www.starenterprise.se/fireworks.html all done in javascript. and no I didn't bother to make it ashortap, I just wanted to see if I could.

Erik