23
\$\begingroup\$

You may have seen Jacob's Ladder in children's science museums. If you're not familiar with what they look like, there are several images and video examples on the Wikimedia Commons. The challenge today is to create an animated ASCII version of the electrical gadget. In the end, it should look something like this:

LadderGIFExample


Ladder Construction

Here is the basic shape of a ladder with a height (H) of 6:

6   \            /
5    \          /
4     \        /
3      \      /
2       \    /
1        \  /
0         ¯¯

The numbers on the left simply indicate row number for this example and should not be included in the output. We'll refer to a given row by it's number (R). Row 0 is the bottom ¯¯. Each row 1 through H is comprised of four parts:

  • A space (U+0020) repeated (H - R) times
  • A back slash \ (U+005C)
  • A space (U+0020) repeated (2 * R) times
  • A forward slash / (U+002F)

Row 0 is identical except both the slashes are replaced with a macron ¯ (U+00AF). Trailing whitespace at the end of each line or below the ladder is OK. Leading whitespace is not.


Arc Construction

Once the ladder is constructed, you can create arcs between the left and right side. One arc is entirely within a row and replaces the spaces between the leading \ and trailing /. Therefore, row 2 will have 4 characters in its arc, row 3 will have 6, and so forth. Each arc is composed using the following rules:

  • The only allowable characters are _/¯\ (U+005F, U+002F, U+00AF, U+005C)
  • In order to ensure a smooth appearance, any ¯ or / must be followed by a ¯ or \
  • In order to ensure a smooth appearance, any _ or \ must be followed by a _ or /
  • The two rules above apply to the edges of the ladder as well
  • The three rules above effectively mean that the first character in the arc must be _ or / and the last character must be _ or \ (\¯\_// is invalid on both ends but \_/¯\/ is OK)
  • There must be a non-zero chance for each allowable character to occur at a given point
  • Each arc is independent from every other arc

Animation

The life of a single arc is created by starting it at row 1 and "moving" it up one row at a time until it reaches the top. I.E., first generate an arc at row 1, then set it back to spaces and generate an arc at row 2, and so forth. Given a number of arcs to show (N), show the complete life of that many arcs one at a time using the following guidelines:

  • Only one arc should be "alive" at any one time. The next arc cannot start until the current one reaches the top and then extinguishes.
  • Each row of the arc's life should be shown for exactly one frame
  • There should one frame of just the basic ladder (with no arcs) before a new arc starts (optional before the first arc)
  • The animation should show the full life of N arcs. If N=0, it should animate random arcs forever until stopped.
  • If N>0, you may still loop the animation forever but it must be a loop of the same arcs over and over. (The example GIF at the top of this post has H=6 and N=3 but it loops forever.)
  • The animation should occur in-place. That is, each frame should completely overwrite the next frame and be in the same location.
  • The length of each frame can be whatever you want but make it watchable by a human (I.E., use your common sense: 0.01s/frame and 30s/frame are both unacceptable.)

Input / Output

  • Input and Output can be in any standard format
  • You can export a GIF, write text to the screen, output a single file for each frame, or any other reasonable means
  • Standard loopholes are forbidden
  • The height of the ladder H will be a positive integer
  • The number of arcs to show N will be a non-negative integer
  • Both H and N are taken as inputs in any order you choose (Please include the order in your answer)

Winning Condition

This is so the shortest code wins.

Sandbox

\$\endgroup\$
  • 1
    \$\begingroup\$ Can arc be generated symmetrical by its center? I can't see restriction to that in rules \$\endgroup\$ – Dead Possum Jun 4 '18 at 13:08
  • \$\begingroup\$ Can i print each frame after each other to the console? \$\endgroup\$ – TFeld Jun 4 '18 at 13:31
  • \$\begingroup\$ @DeadPossum I was thinking you were right despite the fact it wouldn't look very lightning-y but it's actually disallowed by the combination of two rules: the first character in the arc must be _ or / and the last character must be _ or \ and There must be a non-zero chance for each allowable character to occur at a given point. In order to be symmetrical, both the first and last character would have to be _ every single time which means there is zero chance of either / or `\` occurring. \$\endgroup\$ – Engineer Toast Jun 4 '18 at 13:40
  • \$\begingroup\$ @TFeld So long as each frame appears in the same location on the screen, yes. That means you'll have to clear the console (or maybe scroll down, if that's possible) each time. \$\endgroup\$ – Engineer Toast Jun 4 '18 at 13:46
  • 2
    \$\begingroup\$ Does the macron requirement mean that QBasic can't compete? It uses CP437, in which code point 0xAF is ». \$\endgroup\$ – DLosc Jun 4 '18 at 17:22
5
\$\begingroup\$

Python 2, 287 271 270 276 275 bytes

import time,random
r,n=input()
c=n*-~r or-r
while c:
 c-=1;L=[list(' '*i+'\\'+'  '*(r-i)+'/')for i in range(r)];x=c%-~r;time.sleep(1);y=x+1;exec"L[x][y]=random.choice('\xaf/\_'[L[x][y-1]in'\_'::2][y==2*r-x:]);y+=1;"*2*(r-x)
 for l in['']*99+L+[' '*r+'\xaf'*2]:print''.join(l)

Try it online!

Does not clear the screen on tio, but works in a console.

Gif of it running:

enter image description here

\$\endgroup\$
  • \$\begingroup\$ A bit sneaky but you could use print'\n'*99 instead of os.system('cls') and lose the os import. Still doesn't work on TIO but works in both Windows and Linux consoles. \$\endgroup\$ – ElPedro Jun 4 '18 at 14:32
  • 1
    \$\begingroup\$ There should [be] one frame of just the basic ladder (with no arcs) before a new arc starts (optional before the first arc) \$\endgroup\$ – wastl Jun 4 '18 at 15:49
  • 5
    \$\begingroup\$ I think you're using hyphens (U+002D) instead of macrons (U+00AF). I don't think it'll increase your byte count to fix it. Also, as @wastl pointed out, there's no empty ladder frame between arcs. \$\endgroup\$ – Engineer Toast Jun 4 '18 at 16:08
  • \$\begingroup\$ The bottom row uses macrons but the arcs do not \$\endgroup\$ – Engineer Toast Jun 6 '18 at 12:34
  • 1
    \$\begingroup\$ @EngineerToast Fixed now :) \$\endgroup\$ – TFeld Jun 6 '18 at 12:50
4
\$\begingroup\$

JavaScript (ES6), 245 bytes

f=(o,h,n,i=0)=>(o.innerText=[...Array(h+1)].map((_,j)=>` `.repeat(j)+(j<h?`\\${[...Array(w--*2)].map((_,k)=>h+~j-i?` `:k>w*2|Math.random()<.5?s[s=t,1]:s[s=`¯\\`,0],s=t=`/_`).join``}/`:`¯¯`),w=h).join`
`,(++i<h||--n)&&setTimeout(f,250,o,h,n,i%h))
Height: <input type=number min=1 value=6 id=h><br>Arcs: <input type=number min=0 value=3 id=n><br><input type=button value=Go! onclick=f(o,+h.value,+n.value)><pre id=o></pre>

Byte count assumes ISO-8859-1 encoding.

\$\endgroup\$
  • \$\begingroup\$ It's possible to reduce it to 242 by defining A=x=>[...Array(x)].map; at the begining and replacing both usages. \$\endgroup\$ – Bary12 Jun 6 '18 at 9:17
  • \$\begingroup\$ @Bary12 You can't return map, that's just a property of Array.prototype and no use on its own. I tried working versions but they all came out longer than 245 bytes. \$\endgroup\$ – Neil Jun 6 '18 at 9:26
3
\$\begingroup\$

C (gcc), 406 bytes

#define p(X) printf(X),usleep(999)
#define x(X) do{s[X]=0;p(s);s[X]=' ';}while(0)
char c[2][2]={95,47,92,'¯'};R;i;j;k;a(){char s[2]={92,0};for(j=0;j<2*R-1;++j,p(s))*s=c[*s<50][rand()%2];*s=c[*s<50][0];p(s);}f(H,N){char s[99];for(i=0;i<99;++i)s[i]=' ';p("\e[s");for(i=0;;++i){i%=(N?N:i+1);srand(i^H^N);for(k=1;k<H;++k){for(R=H;--R;){x(H-R+1);p("\\");if(R==k)a();else x(2*R);p("/\n");}x(H);p(" ¯¯\n\e[u");}}}

Try it online!

Description:

#define p(X) printf(X),usleep(999)              // Define p to printf(p) + delay
#define x(X) do{s[X]=0;p(s);s[X]=' ';}while(0)  // Define x(X) to print X spaces
                                                // This uses a string s full of
                                                // spaces and adds the null
                                                // terminator where approrpiate
char c[2][2]={95,47,92,'¯'};                    // 2d array of 'next arc' options
R;i;j;k;                                        // Variables
a(){                                            // a() -> print arc for row R
    char s[2]={92,0};                           // s is a string of next char
                                                // initialize to backslash
    for(j=0;j<2*R-1;++j                         // loop over each character
            ,p(s))                              // printing s each time
        *s=c[*s<50][rand()%2];                  // set s to the next arc char
    *s=c[*s<50][0];                             // set s to the 'first' arc char
                                                // note that in definition of c
                                                // first means appropriate as
                                                // final character before /
    p(s);}                                      // print the last character
f(H,N){                                         // f(H,N) -> print jacob ladder
    char s[99];for(i=0;i<99;++i)s[i]=' ';       // this is the space string for x
    p("\e[s");                                  // ANSI terminal save position
    for(i=0;;++i){i%=(N?N:i+1);                 // loop i->N (or i->INT_MAX if N=0)
        srand(i^H^N);                           // seed random with i XOR H XOR N
        for(k=1;k<H;++k){                       // for each row (bottom to top)
            for(R=H;--R;){                      // for each row (top to bottom)
                x(H-R+1);p("\\");               // print left "    \"
                if(R==k)                        // if on the arc row
                    a();                        // print the arc
                else x(2*R);                    // otherwise print spaces
                p("/\n");}                      // finish off the row
            x(H);p(" ¯¯\n\e[u");}}}             // print bottom line and move back
\$\endgroup\$
  • \$\begingroup\$ Note: only really works in Xterm... many terminal emulators just don't support save/restore position. \$\endgroup\$ – LambdaBeta Jun 4 '18 at 16:04
  • \$\begingroup\$ The bottom row is row 0 and has two macrons only. It is not \--/. That's probably an easy fix. Are you able to capture and post a GIF of it working in Xterm? \$\endgroup\$ – Engineer Toast Jun 4 '18 at 16:11
  • \$\begingroup\$ Sadly I lack the tools to do that (just playing around during build times at work). I will update with correct row 0 though, it is an easy fix. \$\endgroup\$ – LambdaBeta Jun 4 '18 at 16:13
  • \$\begingroup\$ Invalid: There should [be] one frame of just the basic ladder (with no arcs) before a new arc starts (optional before the first arc) \$\endgroup\$ – wastl Jun 4 '18 at 17:41
  • \$\begingroup\$ changing k=1 to k=0 fixes that... 0 byte cost. Will update soon. \$\endgroup\$ – LambdaBeta Jun 4 '18 at 17:44
2
\$\begingroup\$

PowerShell, 347 319 bytes

filter c{Param($h,$n)if($n-eq0){$n=-1}for($x=0;$x++-ne$n;){($h..1)|%{$l=(($h..1)|%{"$(" "*($h-$_))\$(" "*$_*2)/"})+"$(" "*$h)¯¯"
$r="Get-Random"
$i=0
$z=-join((0..(($h-$_)*2))|%{$i=switch($i%3){0{&$r 0,1}default{&$r 2,3}}"_/¯\"[$i]})+"_\\_"[$i]
$l[$_-1]=($l[$_-1]).Substring(0,$_)+"$z/"
cls
$l
Sleep -m 250}}}

Try it online! Couldn't get $args to play nice, so the link calls the function without clearing the console.

Ungolfed

filter c{
    Param($h,$n)
    if($n -eq 0){$n=-1} # inelegant swap to allow for an infinite loop. 
                        # Curse you zero-indexing!
    for($x=0;$x++-ne$n;)
    {
        ($h..1) | % {         
            $l=(($h..1)|%{ # (( double paren is needed to induce each line 
                           # as a new array element
                "$(" "*($h-$_))\$(" "*$_*2)/" # offset by total height. 
                                              # N spaces + rung + N*2 spaces + rung
            })+"$(" "*$h)¯¯" # last line is the floor of the ladder

            $r="Get-Random" # shorter to declare once and execute with & operator

            $i=0 # initialize $i so we choose only _ or / for the first char

            $z=-join( # build an electric ZAP!
                (0..(($h-$_)*2))|%{                    
                    $i = switch($i%3) { # choose next char based on previous selection
                        0{&$r 0,1}
                        default{&$r 2,3}
                    }    
                    "_/¯\"[$i]
                }
            )+"_\\_"[$i] # final char is \ or _ to rejoin the ladder        
            $l[$_-1]=($l[$_-1]).Substring(0,$_)+"$z/" # select one rung of the ladder 
                                                      # append an electric ZAP!                
            cls # clear the console
            $l  # display the ladder
            Sleep -m 250
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ It's a small thing, but the bottom row is hyphens instead of macrons. It's a zero-byte change $l=(($h..1)|%{"$(" "*($h-$_))\$(" "*$_*2)/"})+"$(" "*$h)¯¯" \$\endgroup\$ – Engineer Toast Jun 6 '18 at 12:36
  • 1
    \$\begingroup\$ ¯\(°_o)/¯ oops! swapped in macrons :p \$\endgroup\$ – Peter Vandivier Jun 6 '18 at 13:10
  • 1
    \$\begingroup\$ I don't know PowerShell too well, but you can remove most of the new-lines. In addition for($x=0;$x-ne$n;$x++) can be for($x=0;$x++-ne$n;). I got it down to 324 bytes (321 chars) doing so. Tips for golfing in <all languages> and Tips for golfing in PowerShell might be interesting to read through as well. \$\endgroup\$ – Kevin Cruijssen Jun 6 '18 at 13:35
  • 1
    \$\begingroup\$ sleep 1 saves a bit (it defaults to -seconds) but is pretty slow but still reasonable-ish, sleep -m 99 is pretty quick but also reasonable. Saves 5/1 bytes depending on what you like. Didn't check Kevin's attempt but function to filter is a free byte as well. \$\endgroup\$ – Veskah Jun 6 '18 at 20:21
1
\$\begingroup\$

Ruby, 293 bytes

m={}
"   __/\\_/¯¯\\/¯\\".chars.each_slice(3){|e|u,*v=e;m[u]=v}
a=->l,c{l<1?"/":(d=m[c].sample;c+a[l-1,d])}
n=gets.to_i
h=gets.to_i
o=0
while o<n||n<1
h.times{|i|puts (0...h).map{|j|" "*j+"\\"+a[2*(h-j),i==h-j-1?["_","/"].sample: " "]}*"\n";puts" "*h+"¯¯";sleep(0.3);puts"\n"*99}
o+=1
end

Try it online!

I'm on windows so it just prints many "\n" to clear the console. Takes 2 arguments n and h as two lines on stdin.

\$\endgroup\$

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.