Sinusoidalny tekst animowany ASCII-art

11

Trochę tęsknię za tymi starymi wersjami demonstrującymi możliwości komputerów, które nazywały się x86 zamiast i3, i5 i i7. Jednym z pierwszych, które obejrzałem w moim 386, było demo Unreal od Future Crew, które obchodzi 25-lecie istnienia. W minucie 0:43 rozpoczyna się pierwsza część wersji demo i widzimy przewijany tekst podążający ścieżką sinusoidalną. Spróbujmy naśladować ten efekt w sztuce ASCII!

Wyzwanie

Biorąc pod uwagę tę ścieżkę:

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

i tekst wejściowy, narysuj tekst podążając tą ścieżką:

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

Zauważ, że spacje liczą się jako znaki na ścieżce i że ścieżka powtarza się, jeśli tekst jest dłuższy niż próbka.

Część animacji

Po narysowaniu tekstu poczekaj 100 ms (aby utworzyć animację około 10 fps) i narysuj tekst ponownie, zaczynając od następnej pozycji ścieżki. Tak więc w przypadku ramki #noblicz n modulo 40i rozpocznij rysowanie w następującej pozycji ścieżki z tekstem zawsze wyrównanym do lewej strony obszaru roboczego:

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

Tak więc dla klatki 10 będziemy mieli:

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

Notatki

  • Wejściowy będzie pojedynczy string(lub chartablica, cokolwiek) z tekstem do animacji i zawsze będzie miał co najmniej 1 znak.
  • Prawidłowe znaki do animowania to te z zestawu drukowalnego ASCII .
  • Ścieżka do podążania będzie dokładnie taka, jak podana.
  • Tekst zawsze będzie wyrównywany do lewej strony obszaru roboczego, więc efektem będzie tekst falujący jak flaga, bez przesunięcia tekstu. I przez płótno mam na myśli ekran lub cokolwiek, czego użyjesz do wyświetlenia tekstu . ;-)
  • Ramki muszą być wolne od znaków / pikseli z poprzednich klatek, chyba że znak / piksel jest taki sam w obu klatkach.
  • Szybkość animacji nie ma znaczenia, o ile działa płynnie lub tak szybko, jak to tylko możliwe (możemy ustawić minimum 5 klatek na sekundę, ale nie jest to wymagane). Po prostu dostosuj prędkość, aby płyn był płynny i nie martw się, jeśli czasy oczekiwania nie będą dokładnie takie same.
  • Animacja będzie się powtarzać w nieskończoność.

To jest , więc może wygrać najkrótszy program lub funkcja animująca tekst!

Charlie
źródło
Związane . Piaskownica .
Charlie
1
Mogę policzyć 38 kolumn zamiast 40.
Arnauld
1
@Arnauld to dlatego, że liczy się pozycja na ścieżce, a nie kolumny.
Charlie
Rozumiem. Ma sens.
Arnauld
Czy to jest w porządku jako wyjście? Wyświetla dane wejściowe w postaci fali sinusoidalnej i zapętla się w nieskończoność. Oczywiście, ponieważ wideo ma format Graphics Interchange, w rzeczywistości jest szybsze.
R. Kap.

Odpowiedzi:

9

HTML + ES6, 241 244 237 bajtów

Awaria:

  • HTML: 16 bajtów
  • Funkcja JS: 221 bajtów

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>

W jaki sposób?

Budowanie ścieżki

Poniższy kod buduje ścieżkę:

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

Daje to tablicę tablic, w [j, y, z]których j oznacza modulo pozycji 20, y jest współrzędną y w tej pozycji, a z nie jest później używane (po prostu oblicza się tutaj, aby zapisać niektóre bajty).

Ponieważ ścieżka jest symetryczna, musimy jedynie zakodować 20 pierwszych pozycji. Robimy to za pomocą liczby binarnej, w której każdy 1bit oznacza, że y musi zostać zaktualizowany (+1 dla pierwszej połowy, -1 dla drugiej połowy).

001
   001
      01
        1
         1
         1
          1
           01
             001
                000

Dzięki odwzorowaniu pierwszej pozycji na najmniej znaczący bit daje to:

00010010111110100100 as binary = 77732 as decimal

Ponieważ sama liczba binarna jest również symetryczna, możemy ją odczytać w tej samej kolejności dla drugiej połowy.

Stąd wzór:

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

które można również zapisać jako:

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

gdzie k jest pozycją, a j jest pozycją modulo 20.

Aktualizacja x jest znacznie łatwiejsza: mamy tylko jeden specjalny przypadek do wykrycia, porównując modulo pozycji 20 z 9.

Rysowanie tekstu

W poniższym kodzie opisany powyżej kod budowania ścieżki został zastąpiony pathzmienną zapewniającą czytelność.

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
źródło
To jest genialne i dość blisko ścieżki, o którą prosiłem, ale ścieżka nie jest dokładnie tą, którą narysowałem. Dwie kolumny zawierające dwie *w pionie nie są wyrównane (na tej samej wysokości) i kolejny mały nitpick. Ale muszę powiedzieć, że nadal nie wiem, w jaki sposób twój kod tworzy efekt fali (co robi y+=155464?). Gratulacje!
Charlie,
@CarlosAlejo Myślę, że ścieżka powinna teraz zostać naprawiona. Czy mógłbyś dwukrotnie sprawdzić? Dodam wyjaśnienie zastosowanej metody.
Arnauld
1
Ścieżka sprawdzona i bardzo dziękuję za wyjaśnienie!
Charlie,