Синусоидальный ASCII-арт анимированный текст

11

Я немного скучаю по тем старым демонстрациям, показывающим возможности компьютеров, когда они назывались x86 вместо i3, i5 и i7. Одним из первых, которые я посмотрел в своих 386, была демо-версия Unreal от Future Crew, которая сейчас празднует свое 25-летие. В минуту 0:43 начинается первая часть демонстрации, и мы видим прокручиваемый текст по синусоидальному пути. Попробуем подражать этому эффекту в искусстве ASCII!

Соревнование

Учитывая этот путь:

***                                ***
   ***                          ***
      **                      **
        *                    *
         *                  *
         *                  *
          *                *
           **            **
             ***      ***
                ******

и входной текст, нарисуйте текст по этому пути, например так:

Thi                                Golf! 
   s i                          de       Yay
      s                       Co            !
        P                     
         r                  d
         o                  n
          g                a
           ra            s 
             mmi      zle
                ng Puz

Обратите внимание, что пробелы считаются символами в пути, и что путь повторяется, если текст длиннее образца.

Анимационная часть

После того как вы нарисовали текст, подождите 100 мс (чтобы создать анимацию около 10 кадров в секунду) и снова нарисуйте текст, но начиная со следующей позиции пути. Таким образом, для фрейма #nрассчитайте n modulo 40и начните рисовать в следующей позиции пути с текстом, всегда выровненным по левому краю холста:

***                                ***
|  ***                          ***  |
|     **                      **     |
|       *                    *       |
|        *                  *        |
|        *                  *        |
|         *                *         |
|          **            **          |
|            ***      ***            |
|               ******               |
Position 0                 Position 39

Таким образом, для кадра 10 мы будем иметь:

                           and Co
                        es       de 
                      zl            Go
                     z                l
                    u                  f
T                   P                  !
 h                                       
  is             ng                       Ya
     is       mmi                           y!
        Progra

Заметки

  • Ввод будет одним string(или charмассивом) с текстом для анимации, и всегда будет содержать как минимум 1 символ.
  • Допустимые символы для анимации - те, которые находятся в наборе ASCII для печати .
  • Путь для следования будет в точности как указано.
  • Текст всегда будет выровнен по левому краю холста, поэтому эффект будет заключаться в том, что текст будет развеваться, как флаг, без смещения текста. И под холстом я подразумеваю экран или что-то, что вы будете использовать, чтобы показать текст . ;-)
  • Кадры должны быть очищены от любых символов / пикселей из предыдущих кадров, если символ / пиксель не совпадают в обоих кадрах.
  • Скорость анимации не имеет значения, если она работает плавно или так быстро, как ваше устройство (мы можем установить минимум 5 кадров в секунду, но это не является обязательным требованием). Просто отрегулируйте скорость, чтобы сделать ее плавной, и не беспокойтесь, если время ожидания не совпадает.
  • Анимация будет повторяться бесконечно.

Это , поэтому может выиграть самая короткая программа или функция, способная оживить текст!

Чарли
источник
1
Я могу сосчитать 38 столбцов, а не 40.
Арно
1
@Arnauld это потому, что учитываются позиции на пути, а не столбцы.
Чарли
О, я вижу. Имеет смысл.
Арно
Является ли это хорошо , как выход? Он отображает вход в виде синусоидальной волны и зацикливается бесконечно. Конечно, так как видео в формате Graphics Interchange, оно на самом деле быстрее.
Р. Кап

Ответы:

9

HTML + ES6, 241 244 237 байт

Сломать:

  • HTML: 16 байт
  • Функция JS: 221 байт

let f =

s=>setInterval(_=>o.innerHTML=[...s].map((c,i)=>([j,y]=[...Array(40)].map((_,k)=>[j=k%20,y,y+=77732>>j&1?k<20||-1:0],y=0)[(i+p)%40],a[y]=a[y]||[...s].fill` `)[x]=(x+=j!=9,c),x=0,a=[],p++)&&a.map(r=>r.join``).join`
`,p=30)

f("This is Programming Puzzles and Code Golf! Yay!")
<pre id=o></pre>

Как?

Строить путь

Следующий код строит путь:

[...Array(40)].map((_, k) =>
  [
    j = k % 20,
    y,
    y += 77732 >> j & 1 ? k < 20 || -1 : 0
  ],
  y = 0
)

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

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

001
   001
      01
        1
         1
         1
          1
           01
             001
                000

Когда первая позиция отображается в младший значащий бит, это дает:

00010010111110100100 as binary = 77732 as decimal

Поскольку это двоичное число само по себе также симметрично, мы можем прочитать его в том же порядке для второй половины.

Отсюда и формула:

y += (77732 >> j) & 1 ? (k < 20 ? 1 : -1) : 0

который также может быть записан как:

y += (77732 >> j) & 1 ? k < 20 || -1 : 0

где k - позиция, а j - позиция по модулю 20.

Обновление x намного проще: у нас есть только один особый случай, который нужно определить, сравнив позицию по модулю 20 с 9.

Рисование текста

В следующем коде описанный выше код построения пути был заменен pathпеременной для удобства чтения.

s => setInterval(                       // set a periodic timer:
  _ =>                                  //   with an anonymous callback
    o.innerHTML =                       //   which updates the content of 'o'
      [...s].map((c, i) => (            //   for each character c of the string s
          [j, y] = path[(i + p) % 40],  //     retrieve j and y from the path
          a[y] = a[y] || [...s].fill` ` //     initialize a[y] if it's undefined
        )[x] = (x += j! = 9, c),        //     update a[y][x] / update x
        x = 0,                          //     initialize x
        a = [],                         //     initialize a[]
        p++                             //     increment the phase
      ) &&                              //   end of map()
      a.map(r => r.join``).join`\n`,    //   join everything together
  p = 30                                //   initial phase = interval period = 30
)                                       // end of timer definition
Arnauld
источник
Это блестяще и довольно близко к заданному пути, но путь не совсем тот, который я нарисовал. Два столбца, которые содержат два *по вертикали, не выровнены (на одной высоте), и еще один маленький придира. Но я должен сказать, что до сих пор не знаю, как ваш код создает волновой эффект (что делает y+=155464?). Поздравляем!
Чарли
@CarlosAlejo Я думаю, что путь должен быть исправлен. Не могли бы вы перепроверить? Я добавлю объяснение используемого метода.
Арно
1
Путь проверен, и большое спасибо за объяснение!
Чарли