15
\$\begingroup\$

Entries for this challenge will display an animated rotating Lissajous figure. The appearance of 3d rotation occurs as the x parameter is successively phase-shifted in each frame.

Input:

The a and b parameters (as per the wikipedia article) will be specified at the command line, or read from stdin.

Output:

This is , so output will be displayed in a terminal emulator window or equivalent. Output size may be hardcoded, but the Lissajous figure must be at least big enough to fill a 80x24 window.

The animation frame rate will be approximately 50fps. Sleeping for 20ms between each frame is fine, as long as the time to calculate each frame is small compared with the fixed sleep time. If your choice of language cannot calculate quick enough on your chosen platform, then you'll have to calculate the time to sleep dynamically.

Each frame will not be displayed until all calculation for that frame is complete. There is no need to clear the screen between each frame.

The figure will make a full 2*Pi rotation approximately every 4 seconds.

For each frame, a full closed curve must be generated. At least 1000 points must be calculated along the curve. Line-drawing in between points is not necessary.

Points of the curve will be plotted as # characters. The rest of the display area will be blank/whitespace.

This is , so the shortest answer in bytes (deemed acceptable by me) will be the accepted winner one week after this posting.


Ungolfed reference answer.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Are we allowed to draw it using a graphics-oriented language? \$\endgroup\$
    – TheDoctor
    Commented Apr 30, 2014 at 17:18
  • \$\begingroup\$ @TheDoctor I was torn on that, but decided to restrict to ascii-art. Perhaps we can do a graphical-output follow-up if this ends up popular. \$\endgroup\$ Commented Apr 30, 2014 at 17:23
  • 1
    \$\begingroup\$ As with many rotating figures, these figures rotate different ways depending on how you look at them. For example, your answer seems to be rocking back and forth to me. But when I try hard, I can see regular rotation. \$\endgroup\$
    – Justin
    Commented Apr 30, 2014 at 19:11

5 Answers 5

8
\$\begingroup\$

Perl - 177

while($d+=.1){print"\e[H\e[2J";$a=0;while(($a+=.01)<4*atan2 1,0){$x=$==40+40*cos$d+$a*$ARGV[0];$y=$==13+13*sin$d+$a*$ARGV[1];print"\e[$y;$x"."H#";}print$/;select($v,$v,$v,.03);}

The coefficients are passed via arguments. The above gif is produced from perl % 2 3

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @DigitalTrauma I have my very own tool for recording screen in X11 \$\endgroup\$
    – mniip
    Commented May 1, 2014 at 7:41
7
\$\begingroup\$

C (reference answer - not golfed)

Output with ./lissajous 2 3:

enter image description here

/*
 * lissajous.c
 *
 * Compile with:
 *   cc lissajous.c -lm -o lissajous
 *
 * Usage:
 *   ./lissajous a b
 *
 * a and b are the parameters as described in:
 * http://en.wikipedia.org/wiki/Lissajous_curve
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <unistd.h>

int main (int argc, char **argv) {
    char buffer[25][80];
    double t, p;
    int x, y;
    int a, b;

    if (argc != 3) return 1;

    a = atoi(argv[1]);
    b = atoi(argv[2]);

    for (;;) {
        for (p = 0; p < 2 * M_PI; p += M_PI / 100) {
            memset(buffer, ' ', sizeof(buffer));
            /* Set 1st char of final line to nul.  Then we can printf
             * the whole 2d array as if it were one long 1d buffer.
             * Line wraps at 80 chars are assumed */
            buffer[24][0] = 0;
            for (t = 0; t < 2 * M_PI; t += M_PI / 500) {
                x = 39.5 * (1 + sin(a * t + p));
                y = 11.5 * (1 + sin(b * t)); 
                buffer[y][x] = '#';
            }
            printf("%s\n", &buffer[0][0]);
            usleep(20000);
        }
    }
    return (0);
}

C, 257 bytes

Ok, well I couldn't resist golfing it a bit myself. I think there's a lot more golfing to be done on this though:

#include<math.h>
main(int a,char**v){char x,y,b,d[25][80];double t,p,q=2*M_PI;a=atoi(v[1]);b=atoi(v[2]);for(p=0;memset(d,32,2000);p+=q/100){p=p<q?p:0;d[24][0]=0;for(t=0;t<q;y=11.5*sin(b*t)+12,d[y][x]=35,t+=q/1e3)x=39.5*sin(a*t+p)+40;puts(d);usleep(20000);}}
\$\endgroup\$
8
  • 2
    \$\begingroup\$ funny how the reference answer is getting upvotes... \$\endgroup\$
    – TheDoctor
    Commented Apr 30, 2014 at 17:20
  • \$\begingroup\$ @TheDoctor I know, right. Thats why I ended up adding a golfed version, as it didn't feel right to get upvotes for an ungolfed answer. \$\endgroup\$ Commented Apr 30, 2014 at 17:24
  • \$\begingroup\$ If you're usleeping for 20000ms, why not just sleep for 20s ? \$\endgroup\$
    – user12205
    Commented Apr 30, 2014 at 20:40
  • \$\begingroup\$ @ace usleep(20000)== 20000 micro seconds, not 20000 milli seconds \$\endgroup\$ Commented Apr 30, 2014 at 20:41
  • \$\begingroup\$ Oops, sorry, my bad. What about usleep(2e4);? \$\endgroup\$
    – user12205
    Commented Apr 30, 2014 at 20:47
2
\$\begingroup\$

Python 3 - 280

Don't have one of those fancy animated gifs for ya, sorry. Windows console is slow at printing :P

Not sure this meets the 50fps requirement though I'm not sure it's really possible with Python. You can adjust the 1000 on the second line for the amount of points to calculate (the list is output width, output height, points to find, progression per frame (pi*2/n) and starting point). Or you can remove them and specify in the input as well.

import math as m
a,b,w,h,p,r,t=list(map(int,input().split()))+[79,24,1000,100,0]
while 1:v,z=w/2,h/2;d=[[int(m.sin(a*k+t)*v+v),int(m.sin(b*k)*z+z)]for k in[m.pi*2/p*l for l in range(p)]];print('\n'.join(''.join([' ','#'][[i,j]in d]for i in range(w))for j in range(h)));t+=m.pi*2/r

MORE IMPORTANT EDIT: Input via stdin, space-separated, newline terminated. Will wait for your input.

EDIT: Screenshot. Changed height to 40 for this one.

Lissajous rendering

\$\endgroup\$
9
  • \$\begingroup\$ Hmm, it just hangs for me with python 3.2.3 (and 2.7.3) on Ubuntu. I guess I need to dig up a windows VM from somewhere. Or learn python. \$\endgroup\$ Commented Apr 30, 2014 at 21:10
  • \$\begingroup\$ @DigitalTrauma Hm. I'm running 3.3.2. Strange that it doesn't work though, don't see any platform-specific procedures in my code. \$\endgroup\$
    – cjfaure
    Commented Apr 30, 2014 at 21:20
  • \$\begingroup\$ Save as lissajous.py, then run python3 lissajous.py 2 3 should be sufficient, right? \$\endgroup\$ Commented Apr 30, 2014 at 21:22
  • \$\begingroup\$ @DigitalTrauma Oh, oh, sorry. It takes input from stdin, not the args (failed to specify that...oops). Space-separated. \$\endgroup\$
    – cjfaure
    Commented Apr 30, 2014 at 21:23
  • \$\begingroup\$ Aha - I guess I should have seen input() and guessed that. Works fine now for me with 3.2.3. +1 \$\endgroup\$ Commented Apr 30, 2014 at 21:28
1
\$\begingroup\$

C# - 360 352 (cross platform - 332 for Windows only)

Edited after micro-golfing and rounding bug fix + suggestion by Ypnypn

Not exactly a contender at that length - and it's pretty much a verbatim copy of the reference - but oh well. :)

namespace System{class P{static int Main(string[]m){double p=Math.PI*2,a=int.Parse(m[0]),b=int.Parse(m[1]),q,t;for(;;)for(q=0;q<p;q+=p/200){var s=new string(' ',1920).ToCharArray();for(t=0;t<p;t+=p/1000)s[(int)(39.5*Math.Sin(a*t+q)+40)+(int)(11.5*Math.Sin(b*t)+12)*80]='#';Console.SetCursorPosition(0,0);Console.Write(s);Threading.Thread.Sleep(20);}}}}

Memory hog, creating a new array for every refresh - originally (re)used a StringBuilder, but sacrificed it for shortness. But at least the refresh takes less than 1 ms on my old Core2.

After removing some - now length-hurtful - old golfing, thereby reducing it by 8 characters, I tried to get it back to the "poetic" 360 by reverting to double rather than int parsing, and returning to 80*24 instead of 1920. That's still only 359, though - and no other single character addition that I can think of really adds any value to the code. So we'll just stick with 352. :-)

Unrolled (lost the pre-golf code):

namespace System
{
    class P
    {
        static int Main(string[] m)
        {
            double p = Math.PI * 2,
                   a = int.Parse(m[0]),
                   b = int.Parse(m[1]),
                   q, t;

            for (;;)
            {
                for (q = 0; q < p; q += p/200)
                {
                    var s = new string(' ', 1920).ToCharArray();
                    // Alternative - Windows console only:
                    // var s = new char[1920];

                    for (t = 0; t < p; t += p/1000)
                    {
                        s[
                            (int) (39.5*Math.Sin(a * t + q) + 40)
                          + (int) (11.5*Math.Sin(b * t) + 12) * 80
                        ] = '#';
                    }
                    Console.SetCursorPosition(0, 0);
                    Console.Write(s);
                    Threading.Thread.Sleep(20);
                }
            }
        }
    }
}

The Windows console actually accepts outputting a lot of null chars resulting in output that's (graphically) identical to using an actual space character - which allows a few characters less to initialize the character array.

No fancy animation, sorry :-)

\$\endgroup\$
5
  • \$\begingroup\$ Windows console actually accepts outputting a lot of null chars. Ah perhaps that explains why it doesn't so well with mono on Ubuntu. I don't have windows/.net handy right now, so I'll take your word for it that it works. \$\endgroup\$ Commented Apr 30, 2014 at 19:14
  • \$\begingroup\$ Just added a screenshot - should make it cross platform, but considering the non-contender status it has already - and the rather "poetic" number of characters, maybe it should just be left as is. :-) \$\endgroup\$
    – JimmiTh
    Commented Apr 30, 2014 at 19:19
  • \$\begingroup\$ No need for all answers to be cross-platform IMO. If answers are platform-specific, its good to mention the platform, though C# is so well-known that its already obvious. \$\endgroup\$ Commented Apr 30, 2014 at 19:25
  • \$\begingroup\$ Does using C = Console really save any characters? \$\endgroup\$
    – Ypnypn
    Commented May 1, 2014 at 2:21
  • \$\begingroup\$ @Ypnypn - not after invading the System namespace, nope. Didn't change it, or look for it, because the self-stated goal was to get to the same 360 characters while still using "proper" char array initialization. Thanks. :-) \$\endgroup\$
    – JimmiTh
    Commented May 1, 2014 at 6:28
1
\$\begingroup\$

Python 2.7 - 214

I think I'm going to have another look at this. I have a feeling this can be brought down even further, but it will be hard to hit the Perl byte count. The math seems to be my biggest limitation here.

Warning: may crash whatever terminal you are using. I tested this on the Windows command prompt with lissajous.py 2 3. Because of the quick writing to the command prompt, expect the frames to jump a little bit. This can be mostly resolved (at the cost of speed) by using a greater s in the range(s) and t=2*pi*i.

I don't use \r or \b here purposely because I'm running it on Windows and it would cost extra characters.

from math import*;import sys;a,b=sys.argv[1:];p=s=1920
while p:
 c = [" "]*s
 for i in range(s):
    t=2*pi*i/s;c[int(round((39.5*(1+sin(eval(a)*t+p))))+round(11.5*(1+sin(eval(b)*t)))*80)]="#"
 print ''.join(c)
 p+=.1
\$\endgroup\$
2
  • \$\begingroup\$ +1 Works on Ubuntu, though the output is a bit jumpy \$\endgroup\$ Commented May 1, 2014 at 14:38
  • \$\begingroup\$ @DigitalTrauma Yeah the jumpiness is caused by this being a cross platform solution (ie. to work on Windows command prompt). \$\endgroup\$
    – grovesNL
    Commented May 1, 2014 at 16:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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