Code Golf: Новогодний фейерверк

2009 год подходит к концу, и с экономикой и всеми, мы сэкономим наши деньги и вместо того, чтобы покупать дорогие фейерверки, в этом году мы будем отмечать в ASCII-арт.

Задача

Учитывая набор фейерверков и время, сделайте снимок фейерверка в это самое время и нарисуйте его на консоли.

Лучшее решение, введенное до полуночи в канун Нового года (UTC), получит награду в размере 500 репа. Это кодовый гольф, поэтому количество персонажей сильно зависит; однако так поступают голоса сообщества, и я оставляю за собой окончательное решение относительно того, что лучше/круто/наиболее креативно и т.д.

Входные данные

Обратите внимание, что наша система координат слева-направо, снизу-вверх, поэтому все фейерверки запускаются с y -координатом 0 (ноль).

Входные данные состоят из фейерверков формы

(x, speed_x, speed_y, launch_time, detonation_time)

где

  • x - это позиция (столбец), в которой запускается фейерверк,
  • speed_x и speed_y - горизонтальная и вертикальная скорость фейерверка во время запуска,
  • launch_time - это момент, когда этот фейерверк запущен,
  • detonation_time - это момент, когда этот фейерверк взорвется.

Данные фейерверка могут быть жестко запрограммированы в вашей программе как список из 5-ти кортежей (или эквивалент на вашем языке), не считая вашего персонажа. Однако, это должно быть легко изменить эти данные.

Вы можете сделать следующие предположения:

  • существует достаточное количество фейерверков (скажем, меньше сотни).
  • для каждого фейерверка все пять чисел являются целыми числами в разумном диапазоне (например, для каждого из них будет достаточно 16 бит),
  • -20 <= x <= 820
  • -20 <= speed_x <= 20
  • 0 < speed_y <= 20
  • launch_time >= 0
  • launch_time < detonation_time < launch_time + 50

Единственная дополнительная часть входных данных - это точка времени, которая должна быть отображена. Это неотрицательное целое число, которое предоставляется вам с помощью стандартного ввода или аргумента командной строки (в зависимости от того, что вы выберете).

Идея состоит в том, что (если ваша программа представляет собой python script, называемый firework.py), этот bash script дает вам приятную фейерверковую анимацию:

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

(не стесняйтесь помещать здесь эквивалентный .BAT файл).

Жизнь фейерверка

Жизнь фейерверка выглядит следующим образом:

  • До запуска время его можно игнорировать.
  • Во время запуска ракета имеет положение (x, 0) и вектор скорости (speed_x, speed_y).
  • Для каждого временного шага вектор скорости добавляется в позицию. С небольшим натяжением, применяемым к законам Ньютона, мы предполагаем, что скорость остается постоянной.
  • Во время детонации ракета взрывается на девять искр. Все девять искр имеют такое же положение в этот момент времени (это позиция, которую имела бы ракета, если бы она не взорвалась), но их скорости различаются. Каждая скорость основана на скорости ракеты, с -20, 0 или 20, добавленной к speed_x и -10, 0 или 10, добавленной к speed_y. Это девять возможных комбинаций.
  • После времени детонации сила тяжести начинает тянуться: с каждым шагом времени гравитационная постоянная, которая оказывается равной 2 (двум), вычитается из каждой искры speed_y. Горизонтальный speed_x остается постоянным.
  • Для каждого временного шага после времени детонации сначала добавьте вектор скорости в позицию, , затем вычитайте 2 из speed_y.
  • Когда положение искры y опускается ниже нуля, вы можете забыть об этом.

Выход

Мы хотим, чтобы картина фейерверка выглядела так, как она смотрит на данный момент времени. Мы смотрим только на фрейм 0 <= x <= 789 и 0 <= y <= 239, отображая его на вывод символов 79x24.

Итак, если ракета или искра имеют положение (247, 130), мы рисуем символ в столбце 24 (нулевой, так что это 25-й столбец), строка 13 (на основе нуля и подсчет снизу, поэтому строка 23 - 13 = 10, 11-я строка выхода).

Какой персонаж получает рисунок, зависит от текущей скорости ракеты/искры:

  • Если движение горизонтально *, то есть speed_y == 0 or abs(speed_x) / abs(speed_y) > 2, символ "-".
  • Если движение вертикально *, т.е. speed_x == 0 or abs(speed_y) / abs(speed_x) > 2, символ "|".
  • В противном случае движение диагонально, а символ "\" или "/" (вы угадаете правильный).
  • Если одна и та же позиция набирается более одного раза (даже если она имеет тот же символ), мы вместо этого ставим "x". Предположим, что у вас есть искра в (536, 119) и одна в (531, 115), вы рисуете "x", независимо от их скорости.

* update: это целые деления, поэтому наклон должен быть не менее 3 или не более 1/3 соответственно

Выход (записанный на стандартный вывод) составляет 24 строки, каждый из которых заканчивается символом новой строки. Пропущенные пробелы игнорируются, поэтому вы можете, но не нуждаться в них, набирать ширину 79. Линии могут быть не более 79 символов (исключая новую строку). Все внутренние интервалы должны быть пробелами (ASCII 32).

Примеры данных

Фейерверки:

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

Вывод в момент времени 33:





       \         |         /                                                   


                                                      /                   \    

       -         |         /                                                   




       -         |         -                                                   


                                                      /                   \    





Выход в момент времени 77:












                                            \                                  






                                                \                              
                      X                                                        


                      \                                                        

Вывод в момент времени 93:



              \   |   /                                                        

              \   /   /                                                        

              -   -   -              \                                         




       \                                                                       







  /                               \                               \            



Обновление: Я загрузил ожидаемый результат в период от 0 до 99 до firework.ü-wie-geek.de/NUMBER.html, где NUMBER - время. Он включает в себя отладочную информацию; нажмите на частицу, чтобы увидеть ее текущее положение, скорость и т.д. И да, это домен umlaut. Если ваш браузер не может справиться с этим (как, очевидно, не может переполнять стеки), попробуйте firework.xn---wie-geek-p9a.de.

Другое обновление:. Как показано в комментариях ниже, более длинный фейерверк теперь доступен на YouTube. Он был создан с измененной версией MizardX ', с общим количеством фейерверков в 170 (да, это больше, чем запрос, но программа обработала его изящно). За исключением цвета, музыки и конечного экрана, анимацию можно воссоздать с помощью любой записи в этом поле для гольфа. Итак, если вы достаточно уродливы, чтобы насладиться фейерверком ASCII (вы знаете, что вы есть): веселитесь и счастливый новый год для всех!

Ответ 1

Вот мое решение в 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)

Получает время от stdin и имеет приятное количество 342 символов. Я все еще пытаюсь представить, как OP получил 320: P

Изменить: Это лучшее, что я мог получить, 322 символа в соответствии с 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),

Ответ 2

Теперь, когда выбран победитель – поздравления Хуану; вот мое собственное решение, 304 символа в 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))

Это не очень быстро, потому что для каждой точки на дисплее 79x24 она проходит через все фейерверки, чтобы увидеть, видна ли какая-либо из них в данный момент.

Вот версия, которая пытается объяснить, что происходит:

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

Ответ 3

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)

Если вы удалите первоначальное объявление фейерверка, сжимаете имена переменных до одиночных символов и пробелы до минимума, вы можете получить 590 символов.

Ответ 4

С

С удалением всех ненужных пробелов (632 байта, исключая фейерверковую декларацию):

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

И вот тот же самый код с пробелом добавлен для удобочитаемости:

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

Ответ 5

Для Python решение @MizardX хорошо, но явно не оптимизировано для codegolf - кроме того, что "на самом деле нет" 333 символа префикса, а именно:

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

(последний комментарий является помощником для небольшого codegolf-aux script, который делает все возможные имена 1- char механически - ему нужно сказать, какие имена НЕ минимизировать;-), самый короткий Я могу сделать это решение, сжимая пробелы - 592 символа (достаточно близко к утверждениям 590 @MizardX).

Вытягивание всех остановок ( "рефакторинг" кода в настроении codegolf), я получаю после префикса (я использовал строчные буквы для односимвольных имен, которые я вручную вводил или заменял, в верхнем регистре для тех, кто мой codegolf -aux script автоматически заменяется):

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)

который измеряет на 460 символов - это уменьшение 130, то есть 130/590 = 22%.

Помимо 1-символьных имен и очевидных способов минимизации интервала, ключевые идеи включают в себя: одиночный / для деления (тот же, что и более приятный // для ints в Python 2. *), выражение if/else вместо оператора if/elif/else, extend с геном xp, а не вложенным циклом с append (позволяет удалить некоторые пробелы и пунктуацию), не привязывая к подвыражениям имени, которые происходят только один раз, привязывая к подвыражениям имени, которые (в том числе поиск атрибута .extend), точки с запятой, а не новые строки, где это возможно (только если отдельные строки должны быть отступом, в противном случае, считая новую строку как 1 символ, нет сохранения).

Да, читаемость немного страдает, но это вряд ли удивительно в кодовом гольф; -).

Изменить: после гораздо более ужесточения, теперь у меня есть небольшая программа (тот же префикс):

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)

Все тот же вывод, но теперь 360 символов - ровно на 100 меньше, чем мое предыдущее решение, которое я оставил в качестве первой части этого ответа (все еще намного выше 320 OP говорит, что он, хотя!).

Я воспользовался степенью свободы, позволяющей вводить значение времени ввода из stdin (input намного жестче импорта sys и используя sys.argv[1]! -), исключил промежуточный список (w/extend calls и конечный цикл) в пользу новой функции g, которая вызывается непосредственно и обновляет K по мере того, как мы идем, обнаруживаем и удаляем некоторую общность, реорганизуем вложенное выражение if/else в сложное (но более краткое;-) создание и индексация вложенных списков, используется тот факт, что v>2.9*w более кратким, чем w==0 or v/w>2 (и всегда дает тот же результат в диапазоне значений, которые необходимо учитывать).

Изменить: превращение K ( "экранное изображение" ) в одномерный список сохраняет еще 26 символов, сокращая следующее решение до 334 (все еще на 14 выше OP, но закрываясь... -):

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

Ответ 6

Сделано в F # в 957 * символах, и это уродливо как sin:

Массив фейерверков:

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

Оставшийся код

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, [email protected](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,[email protected](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 [email protected](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

"Довольно" код:

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

Полностью украден переписан с новой и улучшенной логикой. Это как можно ближе к Python. Вы можете видеть слабость F #, которая не предназначена для сценариев ad hoc здесь, где я должен явно преобразовывать V и W в float, объявлять основную функцию с уродливым атрибутом для получения аргументов командной строки, и я должен ссылаться на .NET System.Console.Write, чтобы получить хороший вывод.

Ну хорошо, хорошее упражнение, чтобы выучить язык.

Здесь новый код: 544 байта:

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

Ответ 7

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

Не так впечатляюще, как MizardX, входящий в 1068 символов, если вы удалите объявление f=…, но, черт возьми, это было весело. Это было какое-то время, так как у меня была возможность поиграть с Haskell.

(немного) более красивая версия также доступна.

Изменить: Ack. Перечитывая, я не совсем отвечаю спецификации: эта версия печатает новый экран фейерверка каждый раз, когда вы нажимаете клавишу, и требует ^C для выхода; он не принимает аргумент командной строки и распечатывает соответствующий экран.

Ответ 8

Perl

Предполагая, что данные фейерверка определены как:

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

$t=shift;
for(@f){
    ($x,$c,$d,$l,$e)[email protected]$_;
    $u=$t-$l;
    next if$u<0;
    $x+=$c*$u;
    $h=$t-$e;
    [email protected],$t<$e?[$x,$d*$u,$c,$d]:map{$f=$_;map{[$x+$f*$h,($u*$d-$h*($h-1))+$_*$h,$c+$f,$d+$_-2*$h]}(-10,0,10)}(-20,0,20)
}
[email protected],[($")x79]for(1..24);
for(@p){
   ($x,$y,$c,$d)[email protected]$_;
   if (0 <= $x && ($x=int$x/10) < 79 && 0 <= $y && ($y=int$y/10) < 24) {
      @$_[$x][email protected]$_[$x]ne$"?'X':!$d||abs int$c/$d>2?'-':!$c||abs int$d/$c>2?'|':$c*$d<0?'\\':'/'for$r[23 - $y]
   }
}
$"='';
print$.,map{"@$_\n"}@r

Сжатый, он входит в 433 символа. (см. правки для истории)

Это основано на фрагментах нескольких предыдущих ответов (в основном, MizardX) и может быть определенно улучшено. Вина затягивания других заданий, связанных с работой, означает, что я должен отказаться сейчас.


Прощайте редактирование - вытаскивая все трюки, которые я знаю, это может быть сжато до 356 char:

sub p{
  ($X,$=,$C,$D)[email protected]_;
  if(0<=$X&($X/=10)<79&0<=$=&($=/=10)<24){
    @$_[$X][email protected]$_[$X]ne$"?X:$D&&abs$C/$D<3?$C&&abs$D/$C<3?
    $C*$D<0?'\\':'/':'|':'-'for$r[23-$=]
  }
}
@r=map[($")x79],1..24;
$t=pop;
for(@f){
  ($x,$c,$d,$u,$e)[email protected]$_;
  $x-=$c*($u-=$t);
  $u>0?1:($h=$t-$e)<0
  ?p$x,-$d*$u,$c,$d
  :map{for$g(-10,0,10){p$x+$_*$h,$h*(1-$h+$g)-$u*$d,$c+$_,$d+$g-2*$h}}-20,0,20
}
[email protected]$_,$/[email protected]

$= - специальная переменная Perl (вместе с $%, $- и $?), которая может принимать только целочисленные значения. Использование этого метода исключает необходимость использования функции int.

Ответ 9

ФОРТРАН 77

От отдела доисторических языков, здесь моя запись – в ФОРТРАНЕ 77.

2570 символов, включая инициализацию, несколько пробелов и некоторые лишние пробелы, но я не думаю, что это может выиграть для краткости. Тем более, что, например, 6 ведущих пробелов в каждой строке являются обязательными.

Я назвал этот файл fireworks.ftn и скомпилировал его с помощью gfortran в системе Linux.

  implicit integer(a-z)
  parameter (n=10)
  integer fw(5,n) / 
 + 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
 + /
  integer p(6, 1000) / 6000 * -1 / 
  character*79 s(0:23)
  character z
c Transform input
      do 10 r=1,n
         p(1, r) = 0
         do 10 c=1,5
   10       p(c+1, r) = fw(c, r)
c Input end time
      read *, t9
c Iterate from 1 to end time
      do 62 t=1,t9
         do 61 q=1,1000
            if (p(1,q) .lt. 0 .or. t .lt. p(5,q)) goto 61
            if (p(6,q).gt.0.and.t.gt.p(5,q) .or. t.gt.abs(p(6,q))) then
               p(1,q) = p(1,q) + p(4,q)
               p(2,q) = p(2,q) + p(3,q)
            endif
            if (t .lt. abs(p(6,q))) goto 61
            if (t .gt. abs(p(6,q))) then
               p(4,q) = p(4,q) - 2
            elseif (t .eq. p(6,q)) then
c Detonation: Build 9 sparks            
               do 52 m=-1,1
                  do 51 k=-1,1
c Find a free entry in p and fill it with a spark
                     do 40 f=1,1000
                        if (p(1,f) .lt. 0) then
                           do 20 j=1,6
   20                      p(j,f) = p(j,q)
                           p(3,f) = p(3,q) + 20 * m
                           p(4,f) = p(4,q) + 10 * k
                           p(6,f) = -p(6,q)
                           goto 51
                        endif
   40                continue
   51             continue
   52          continue
c Delete the original firework
               p(1,q) = -1
            endif
   61    continue
   62 continue
c Prepare output
      do 70 r=0,23
   70 s(r) = ' '
      do 80 q=1,1000
         if (p(1,q) .lt. 0) goto 80
         if (p(5,q) .gt. t9) goto 80
         y = p(1,q) / 10
         if (y .lt. 0 .or. y .gt. 23) goto 80
         x = p(2,q) / 10
         if (x .lt. 0 .or. x .gt. 79) goto 80
         if (s(y)(x+1:x+1) .ne. ' ') then
            z = 'X'
         elseif ((p(4,q) .eq. 0) .or. abs(p(3,q) / p(4,q)) .gt. 2) then
            z = '-'
         elseif ((p(3,q) .eq. 0) .or. abs(p(4,q) / p(3,q)) .gt. 2) then
            z = '|'
         elseif (sign(1, p(3,q)) .eq. sign(1, p(4,q))) then
            z = '/'
         else
            z = '\'
         endif
         s(y)(x+1:x+1) = z
   80 continue
c Output
      do 90 r=23,0,-1
   90 print *, s(r)
      end

Ответ 10

Здесь меньшая реализация Haskell. Это 911 символов; минус определение фейерверков, это 732 символа:

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}

Здесь не сжатая версия для любопытных:

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

Ответ 11

Первый черновик в Tcl8.5 913 байт, исключая определение фейерверков:

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]

Оптимизирован до точки нечитаемости. Все еще ищут комнату для улучшения. Большая часть сжатия в основном использует псевдонимы команд, заменяя одиночные символы для имен команд. Например, определения функций выполняются с использованием синтаксиса Forth-like:

Здесь несжатая версия:

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]

Ответ 12

Первый пост хахаха http://zipts.com/position.php?s=0  не мое окончательное представление, но не могло устоять

Btw: Символы 937 не считая пробелов (будем считать пробелы?)

Ответ 13

Clojure

Неинтенсивный, без ввода и ненужных пробелов, он достигает 640 символов - ровно вдвое больше значения:( Таким образом, я не предоставляю "пустую оптимизированную" версию в попытке выиграть в краткости.

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

Ответ 14

Мой ответ на http://www.starenterprise.se/fireworks.html все сделано в javascript. и нет, я не потрудился сделать это просто, я просто хотел посмотреть, смогу ли я.