11
\$\begingroup\$

I somewhat miss those old demos showing the capabilities of computers when they were called x86 instead of i3, i5 and i7. One of the first I watched in my 386 was the Unreal demo from Future Crew that is now celebrating its 25th anniversary. At minute 0:43 the first part of the demo starts and we see a scrolling text following a sinusoidal path. Let's try to imitate that effect in ASCII art!

The challenge

Given this path:

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

and an input text, draw the text following that path, like this:

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

Note that the spaces count as characters in the path, and that the path repeats itself if the text is longer than the sample.

The animation part

Once you have drawn the text, wait for 100 ms (to create an animation of around 10 fps) and draw the text again but starting from the next position of the path. So, for frame #n, calculate n modulo 40 and start drawing in the following position of the path with the text always aligned to the left of the canvas:

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

So for frame 10 we will have:

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

Notes

  • Input will be a single string (or char array, whatever) with the text to animate, and will always have at least 1 character.
  • Valid characters to animate are those in the printable ASCII set.
  • The path to follow will be exactly as given.
  • Text will always be aligned to the left of the canvas, so the effect will be the text waving like a flag, with no text displacement. And by canvas I mean screen or whatever you will use to show the text. ;-)
  • Frames must be clear of any characters/pixels from previous frames unless the character/pixel is the same in both frames.
  • The speed of the animation does not matter as long as it runs smoothly or as fast as your device can (we can set a minimum of 5 fps, but this is not a requirement). Just adjust the speed to make it fluid and do not worry if the waiting times are not exactly the same.
  • The animation will loop endlessly.

This is , so may the shortest program or function capable of animating the text win!

\$\endgroup\$
  • \$\begingroup\$ Related. Sandbox. \$\endgroup\$ – Charlie Jul 1 '17 at 21:20
  • 1
    \$\begingroup\$ I can count 38 columns rather than 40. \$\endgroup\$ – Arnauld Jul 1 '17 at 21:34
  • 1
    \$\begingroup\$ @Arnauld that's because what counts are the positions in the path, not the columns. \$\endgroup\$ – Charlie Jul 1 '17 at 21:35
  • \$\begingroup\$ Oh I see. Makes sense. \$\endgroup\$ – Arnauld Jul 1 '17 at 21:37
  • \$\begingroup\$ Is this okay as output? It displays the input in the form of a sinusoidal wave and loops endlessly. Of course, since the video is in Graphics Interchange format, it is faster in actuality. \$\endgroup\$ – R. Kap Jul 2 '17 at 3:47
9
\$\begingroup\$

HTML + ES6, 241 244 237 bytes

Breakdown:

  • HTML: 16 bytes
  • JS function: 221 bytes

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>

How?

Building the path

The following code builds the path:

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

This gives an array of arrays [j, y, z] where j is the position modulo 20, y is the y-coordinate at this position and z is not used later (it just happens to be computed here to save some bytes).

Because the path is symmetric, we only need to encode the 20 first positions. We do that by using a binary number where each 1 bit means that y must be updated (+1 for the first half, -1 for the second half).

001
   001
      01
        1
         1
         1
          1
           01
             001
                000

With the first position being mapped to the least significant bit, this gives:

00010010111110100100 as binary = 77732 as decimal

Because this binary number is itself also symmetric, we can read it in the same order for the 2nd half.

Hence the formula:

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

which can also be written as:

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

where k is the position and j is the position modulo 20.

Updating x is much easier: we just have one special case to detect by comparing the position modulo 20 with 9.

Drawing the text

In the following code, the path building code described above has been replaced by a path variable for readability.

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
\$\endgroup\$
  • \$\begingroup\$ This is brilliant, and quite close to the path asked, but the path is not exactly the one I drew. The two columns that contain two * in vertical are not aligned (at the same height), and another small nitpick. But I must say I still don't know how your code creates the wave effect (what does the y+=155464 do?). Congratulations! \$\endgroup\$ – Charlie Jul 2 '17 at 21:08
  • \$\begingroup\$ @CarlosAlejo I think the path should now be fixed. Could you please double-check? I'll add an explanation of the method used. \$\endgroup\$ – Arnauld Jul 2 '17 at 21:57
  • 1
    \$\begingroup\$ Path checked, and thank you very much for the explanation! \$\endgroup\$ – Charlie Jul 3 '17 at 5:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.