35
\$\begingroup\$

Write a program or a function that will output a given string in a staircase fashion, writing each part of a word that starts with a vowel one line below the previous part.

For example:

Input: Programming Puzzles and Code Golf


Output: Pr          P           C    G
          ogr        uzzl   and  od   olf
             amm         es        e
                ing

Input

A string containing nothing but letters and spaces.

The string can be passed via STDIN or function arguments or anything equivalent.

Letters can be lowercase or uppercase.

Inputs are always assumed to follow those rules, you don't need to check for incorrect inputs.

Output

Each time a vowel (that is, a, e, i, o, u or y) is encountered in a word, you must output the rest of the word on the next line (the encountered vowel included), at the correct horizontal position. This rule is recursive, which means that if there are n vowels in the word, it will be written on n+1 lines.

  • The vowel should be written at the beginning of the next line, and not at the end of the previous line when one is encountered.

  • Each word starts on the first line, and should thus be formatted independently from other words. Two words are separated by a space.

  • If a word starts with a vowel, you have to write it starting on the second line.

Test cases

  • Input: Programming Puzzles and Code Golf

Output:

Pr          P           C    G
  ogr        uzzl   and  od   olf
     amm         es        e
        ing
  • Input: The quick brown fox jumps over the lazy dog

Output:

Th  q     br    f   j          th  l    d
  e  u      own  ox  umps ov     e  az   og
      ick                   er        y
  • Input: aeiouy

Output:

 
a
 e
  i
   o
    u
     y
  • Input: YEAh UppErcAsE VOwEls

Output:

               V
Y    Upp        Ow
 E      Erc       Els
  Ah       As  
             E
  • Input: If you only knew the power of the Dark Side

Output:

            kn   th  p        th  D    S
If y   onl    ew   e  ow   of   e  ark  id
    o     y             er                e
     u

Scoring

This is , so the shortest code wins.

\$\endgroup\$
10
  • \$\begingroup\$ The third output example seems inconsistent with the rule "If a word starts with a vowel, you have to write it starting on the second line." \$\endgroup\$
    – JohnE
    Commented Jul 1, 2015 at 15:01
  • 1
    \$\begingroup\$ Y is Y a vowel ? \$\endgroup\$
    – Optimizer
    Commented Jul 1, 2015 at 15:02
  • 1
    \$\begingroup\$ @JohnE indeed it was, I fixed it. Thanks. \$\endgroup\$
    – Fatalize
    Commented Jul 1, 2015 at 15:03
  • 2
    \$\begingroup\$ The vowel should be written at the beginning of the next line, and not at the end of the previous line when one is encountered. After some thought, I understand that this means that the move to the next line should happen before the vowel is printed, not after, but it might be worth wording this in a way that is instantly understandable - it took me a while. \$\endgroup\$ Commented Jul 1, 2015 at 15:09
  • 3
    \$\begingroup\$ Is trailing newlines / spaces allowed? \$\endgroup\$
    – xenia
    Commented Jul 1, 2015 at 15:31

15 Answers 15

18
\$\begingroup\$

Retina, 50 44 34 (+10) 32 30 bytes

Thanks to Dennis for saving 14 bytes by using actual control characters.

i`[aeiouy]
<VT>$0#
+`#(\S*)
$1<ESC>[A

Based on this answer, I'm using ANSI escape codes to move the terminal cursor vertically. The <ESC> should be replaced with the control character 0x1B, and <VT> with the vertical tab 0x0B. For simpler testing, you can also replace <ESC> with \e, <VT> with \v and feed the output through printf.

For counting purposes, each line goes in a separate file. However, for convenience it's simpler to just paste the code into a single file and invoke Retina with the -s option.

The first replacement surrounds each vowel in \v...#, where the \v shifts the cursor downward and the # is a marker for the second step. The i` is Retina's notation for case-insensitive matching.

The second step then repeatedly (+`) removes a # from a word and puts a e\[A at the end of the word which shifts the cursor upward. This stops once the string stops changing, i.e. when there are no more # markers in the string.

\$\endgroup\$
5
  • \$\begingroup\$ You don't need printf. Just replace \e with the ESC byte (0x1b). \$\endgroup\$
    – Dennis
    Commented Jul 1, 2015 at 15:41
  • \$\begingroup\$ @Dennis Oh, that's much better, thank you. \$\endgroup\$ Commented Jul 1, 2015 at 15:45
  • 1
    \$\begingroup\$ This is so awesome!!! \$\endgroup\$ Commented Jul 1, 2015 at 17:03
  • \$\begingroup\$ This answer is why no one takes Retina seriously ;) \$\endgroup\$ Commented Jul 2, 2015 at 18:24
  • \$\begingroup\$ @ChristopherWirt Please elaborate :) (Although I would actually be terrified if anyone took Retina seriously.) \$\endgroup\$ Commented Jul 2, 2015 at 20:42
8
\$\begingroup\$

CJam, 39 36 bytes

0000000: 6c 7b 5f 65 6c 22 61 65 69 6f 75 79 22  l{_el"aeiouy"
000000d: 26 7b 22 1b 5b 41 22 27 0b 6f 5c 7d 26  &{".[A"'.o\}&
000001a: 5f 53 26 7b 5d 7d 26 6f 7d 2f           _S&{]}&o}/

The above is a reversible xxd dump, since the source code contains the unprintable character VT (code point 0x0b) and ESC (code point 0x1b).

Like this answer, it uses vertical tabs and ANSI escape sequences.

This requires a supporting video text terminal, which includes most non-Windows terminal emulators.

Test run

Before executing the actual code, we'll disable the prompt and clear the screen.

$ PS1save="$PS1"
$ unset PS1
$ clear

This makes sure the output is shown properly.

echo -n Programming Puzzles and Code Golf | cjam <(xxd -ps -r <<< 6c7b5f656c226165696f757922267b221b5b4122270b6f5c7d265f53267b5d7d266f7d2f)
Pr          P           C    G
  ogr        uzzl   and  od   olf
     amm         es        e
        ing

To restore the prompt, execute this:

PS1="$PS1save"

How it works

We insert a vertical tab before each vowel to move the cursor down and sufficient copies of the byte sequence 1b 5b 41 ("\e[A") after each space to move the cursor back to the first row.

l           e# Read a line from STDIN.
{           e# For each character C:
  _el       e#   Push lowercase(C).
  "aeiouy"& e#   Intersect with "aeiouy".
  {         e#   If the intersection is non-empty:
    ".[A"   e#     Push "\e[A" (will be printed later).
    '.o     e#     Print "\v".
    \       e#     Swap "\e[A" with C.
  }&        e#
  _S&       e#   Intersect C with " ".
  {         e#   If the intersection is non-empty:
    ]       e#     Wrap the entire stack in an array.
  }&
  o         e#   Print C or the entire stack.
}/          e#
\$\endgroup\$
1
  • \$\begingroup\$ Don't forget to unset PS1save afterwards. \$\endgroup\$
    – rgajrawala
    Commented Jul 1, 2015 at 23:46
5
\$\begingroup\$

Java, 428 bytes

void s(String s){int l=s.length(),m=0;char[][]c=new char[l][];for(int i=0;i<c.length;java.util.Arrays.fill(c[i++],' '))c[i]=new char[l];String v="aeiouyAEIOUY";String[]a=s.split(" ");for(int r=0,p=0;r<a.length;r++){String u=a[r]+" ";int o=v.indexOf(u.charAt(0))>=0?1:0,x=p;for(;x<u.length()-1+p;o+=v.indexOf(u.charAt(x++-~-p))>=0?1:0)c[o][x]=u.charAt(x-p);p+=u.length();m=m<o?o:m;}for(int i=0;i<=m;i++)System.out.println(c[i]);}

I know, it's horrible. There's probably some chars that can be shaved of, but I'm too lazy to do that.

\$\endgroup\$
2
  • \$\begingroup\$ You can probably declare many of your int variables (namely i, r, p, o, and x) where you initialize l and m since they'll be given values later. You can also do String v="...",a[]=...; and do the same as above for String u. That should lower your score quite a bit. \$\endgroup\$
    – TNT
    Commented Jul 1, 2015 at 22:03
  • \$\begingroup\$ I like the x++-~-p \$\endgroup\$
    – Ypnypn
    Commented Jul 3, 2015 at 18:35
4
\$\begingroup\$

Perl, 31 bytes

0000000: 24 5c 3d 22 1b 5b 41 22 78 20 73 2f 5b 61  $\=".[A"x s/[a
000000e: 65 69 6f 75 79 5d 2f 0b 24 26 2f 67 69     eiouy]/.$&/gi

The above is a reversible xxd dump, since the source code contains the unprintable character VT (code point 0x0b) and ESC (code point 0x1b).

The code is 27 bytes long and requires the switches 040p (4 bytes).

The program requires a video text terminal that supports vertical tabs and ANSI escape sequences, which includes most non-Windows terminal emulators.

Test run

Before executing the actual code, we'll disable the prompt and clear the screen.

$ PS1save="$PS1"
$ unset PS1
$ clear

This makes sure the output is shown properly.

echo -n Programming Puzzles and Code Golf | perl -040pe "$(xxd -ps -r <<< 245c3d221b5b41227820732f5b6165696f75795d2f0b24262f6769)"
Pr          P           C    G
  ogr        uzzl   and  od   olf
     amm         es        e 
        ing

To restore the prompt, execute this:

PS1="$PS1save"

How it works

  • perl -040p automatically reads the input as space-separated tokens (-040), saves each token in $_ (-p) and executes the program.

  • s/[aeiouy]/.$&/gi performs a global, case-insensitive search in $_ for vowels and replaces each vowel by the control character VT (moves the cursor down), followed by the vowel itself.

  • s returns the number of replacements it made, so $\=".[A"x s... saves multiple copies of the byte sequence 1b 5b 41 (moves the cursor up) in $\, one for each vowel.

  • At the end of the program, Perl automatically prints "$_$\", because of the -p switch.

\$\endgroup\$
4
\$\begingroup\$

C, 200 190 bytes

i,j,k,l,M;f(char*s){M=strlen(s)+1;char t[M*M];for(;i<M*M;++i)t[i]=(i+1)%M?32:10;for(i=0;i<M-1;++i)k=(strspn(s+i,"aeiouyAEIOUY")?++j:s[i]==32?j=0:j)*M+i,l<k?l=k:0,t[k]=s[i];t[l+1]=0;puts(t);}

Ungolfed:

i,j,k,l,M;
f(char *s){
    M = strlen(s)+1;
    char t[M*M];
    for(; i<M*M; ++i) t[i] = (i+1)%M ? 32 : 10;
    for(i=0; i<M-1; ++i)
        k = (strspn(s+i,"aeiouyAEIOUY") ? ++j : s[i]==32 ? j=0 : j) * M + i,
        l<k ? l=k : 0,
        t[k] = s[i];
    t[l+1]=0;
    puts(t);
}

It allocates a rectangular buffer (actually square), fills it with spaces and newlines, then traverses the given string. At the end it adds a null character to prevent trailing newlines.

Technically it's not a function since it contains globals; in fact it can't be called more than once (j and l must be 0 at the start). To comply, i,j,k,l,M; could be moved to int i,j=0,k,l=0,M; at the start of the function.

\$\endgroup\$
3
  • \$\begingroup\$ char*t=malloc(M*M); --> char t[M*M]; and for(i=0;i<M*M;++i) --> for(;i<M*M;++i) \$\endgroup\$
    – Spikatrix
    Commented Jul 2, 2015 at 10:00
  • \$\begingroup\$ Good catches, edited. \$\endgroup\$
    – jcai
    Commented Jul 2, 2015 at 13:36
  • \$\begingroup\$ Isn't this C99 only due to char t[M*M]? \$\endgroup\$
    – Adalynn
    Commented Jul 31, 2017 at 13:42
4
\$\begingroup\$

CJam, 47

Yeah, it's a bit long, but it's not "cheating" with ANSI codes :)

q_{_S&!\el"aeiouy"-!U+*:U}%_0|$])\zff{~@-S@?}N*

Try it online

The idea is to calculate a line number for each character (starting at 0, incrementing at vowels and jumping back to 0 at space), and then for each line, repeat the string but replace the characters that have a different line number with a space.

\$\endgroup\$
3
\$\begingroup\$

K, 81 72 70 66 bytes

Well, it's a start:

`0:{+{(-z)!y,x#" "}[|/s].'x,'s:,/{+\{12>"aeiouyAEIOUY"?x}'x}'(0,&~{"  "?x}'x)_ x}

Usage Examples:

  `0:{+{(-z)!y,x#" "}[|/s].'x,'s:,/{+\{12>"aeiouyAEIOUY"?x}'x}'(0,&~{"  "?x}'x)_ x} "Programming Puzzles and Code Golf"
Pr          P           C    G   
  ogr        uzzl   and  od   olf
     amm         es        e     
        ing                      
  `0:{+{(-z)!y,x#" "}[|/s].'x,'s:,/{+\{12>"aeiouyAEIOUY"?x}'x}'(0,&~{"  "?x}'x)_ x} "YEAh UppErcAsE VOwEls"
               V     
Y    Upp        Ow   
 E      Erc       Els
  Ah       As        
             E       

Edit 1:

Better. Made some surface level improvements:

`0:{+{(-z)!y,x#" "}[|/s].'x,'s:,/{+\{12>"aeiouyAEIOUY"?x}'x}'(0,&~{"  "?x}'x)_ x}
`0:{+{(-z)!y,x#" "}[|/s].'x,'s:,/(+\12>?["aeiouyAEIOUY"]')'_[0,&" "=x]x}

Notably, I inverted the arguments for ? when I perform the vowel search and thus eliminated the need for a lambda, did the same inversion with _ where I split words on whitespace, and I realized that ~{" "?x}'x is a really silly, overcomplicated way of saying " "=x.

Edit 2:

Another surface level tweak- negate s before applying it to the lambda, saving parens inside:

`0:{+{(-z)!y,x#" "}[|/s].'x,'s:,/(+\12>?["aeiouyAEIOUY"]')'_[0,&" "=x]x}
`0:{+{z!y,x#" "}[|/s].'x,'-s:,/(+\12>?["aeiouyAEIOUY"]')'_[0,&" "=x]x}

Edit 3:

OK, let's take a different approach to calculating the offset for each character. Instead of splitting the sequence at spaces and calculating a running sum (+\) of the positions of vowels, we can operate on the whole input string in one pass, multiplying the running sum by 0 whenever we encounter a space. I need the negation of this sequence, so I can subtract instead of adding as I scan and use number-of-distinct (#?) instead of max (|/) when I calculate the amount of vertical padding.

`0:{+{z!y,x#" "}[|/s].'x,'-s:,/(+\12>?["aeiouyAEIOUY"]')'_[0,&" "=x]x}
`0:{+{z!y,x#" "}[#?s].'x,'s:1_0{(~" "=y)*x-12>"aeiouyAEIOUY"?y}\x}

That saves another 4 characters. Phew!

\$\endgroup\$
2
\$\begingroup\$

Ruby: 135 131 124 115 112 characters

a=[]
y=l=0
gets.split(r=/(?=[aeiouy ])/i).map{|w|w=~r&&y+=1
w<?A&&y=0
a[y]='%*s%s'%[-l,a[y],w]
l+=w.size}
puts a

Sample run:

bash-4.3$ ruby staircase.rb <<< 'Programming Puzzles and Code Golf'
Pr          P           C    G
  ogr        uzzl   and  od   olf
     amm         es        e
        ing
\$\endgroup\$
2
  • \$\begingroup\$ If I'm not mistaken, you can shorten your regex to /(?=[aeiouy ])/i. \$\endgroup\$
    – Alex A.
    Commented Jul 1, 2015 at 15:49
  • \$\begingroup\$ Ah, you are right @AlexA. The space as separate word was only important for an earlier theory. Thanks. \$\endgroup\$
    – manatwork
    Commented Jul 1, 2015 at 16:06
2
\$\begingroup\$

C, 192 bytes

f(char*s){int l=0,r=1,v,c;for(;r;l=1){v=l;r=0;char*p;for(p=s;*p;++p){c=*p;if(c==' ')v=l,putchar(c);else if((strchr("aoeuiyAOEUIY",c)?--v:v)<0)r=1,putchar(' ');else*p=' ',putchar(c);}puts(p);}}

This iterates through the string, blanking characters as it prints them. It repeats until there are no non-space characters left to print. It's portable C, making no assumptions about character encoding.

Readable version

f(char *s) {
    int l=0,       /* true if we've done the first line (no vowels) */
        r=1,       /* true if characters remain in buffer */
        v,         /* how many vowels to print from current word */
        c;         /* current character value */
    for (l=0; r; l=1) {
        v = l;
        r = 0;
        char *p;
        for (p=s;*p;++p) {
            c=*p;
            if (c==' ') {       /* a space - reset vowel counter */
                v=l;
                putchar(c);
            } else if ((strchr("aoeuiyAOEUIY",c)?--v:v)<0) {
                /* vowel conter exceeded - print a space */
                putchar(' ');
                r=1;
            } else {
                /* print it, and obliterate it from next line of output */
                putchar(c);
                *p=' ';
            }
        }
        puts(p); /* p points at the NUL, so this just prints a newline */
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ ' ' --> 32 and f(char*s){int l=0,r=1,v,c; --> l,r=1,v,c;f(char*s){ \$\endgroup\$
    – Spikatrix
    Commented Jul 2, 2015 at 10:03
  • \$\begingroup\$ @Cool - ' ' may be 32, but it depends on the character encoding, and as I said, I made this portable C. Dropping the explicit int is great, though - not sure why I forgot that! \$\endgroup\$ Commented Jul 2, 2015 at 10:08
2
\$\begingroup\$

Python 3, 265 207 202 185 177 characters

i=input()
w,e=i.split(" "),lambda:[[" "]*len(i)]
o,x=e(),0
for p in w:
    y=0
    for c in p:
        if c in"AEIOUYaeiouy":o+=e();y+=1
        o[y][x],x=c,x+1
    x+=1
for l in o:print("".join(l))

This is terrible and I'm not proud. I know this can be made shorter, but I thought I'd post anyway.

Inspired by the C version, it creates a list which is then filled while traversing the input string.

\$\endgroup\$
2
\$\begingroup\$

GNU Sed, 151 + 1

(+1 as it needs the -r flag)

s/^/ /;h;s/[aoeuiy]/_/ig;:a;s/_[^ _]/__/;ta;y/_/ /;g;:x;:b;s/ [^ aoeuiy]/  /i;tb;h;s/([^ ])[aoeuiy]/\1_/ig;:c;s/_[^ _]/__/;tc;y/_/ /;g;s/ [^ ]/  /ig;tx

I thought that sed would be the tool for this job, but found it surprisingly hard.

Readable version:

#!/bin/sed -rf

# make sure the string starts with a space
s/^/ /
h

# print leading consonants, if any
s/[aoeuiy]/_/ig
:a
s/_[^ _]/__/
ta
y/_/ /
p
g

:x
# strip the consonants just printed
:b
s/ [^ aoeuiy]/  /i
tb
h

s/([^ ])[aoeuiy]/\1_/ig
:c
s/_[^ _]/__/
tc
y/_/ /
p
g
# remove leading vowel of each word
s/ [^ ]/  /ig
tx
\$\endgroup\$
3
  • \$\begingroup\$ I'm afraid, that should be 128 characters. The one-line version is missing a p, so it outputs nothing. A small issue is that outputs starts with an extra space. A huge issue is that first piece of text starting with vowel disappears. \$\endgroup\$
    – manatwork
    Commented Jul 2, 2015 at 8:27
  • \$\begingroup\$ I'm sure it was working earlier. I'll have a look and see what I've broken. Thanks for the heads-up, @manatwork! \$\endgroup\$ Commented Jul 2, 2015 at 9:48
  • \$\begingroup\$ I was wrong to jump into loop c, due to the line just before tx. I've reinstated an earlier version with its similar loop, and I'll have another attempt later. \$\endgroup\$ Commented Jul 2, 2015 at 10:16
2
\$\begingroup\$

Python 2, 145 142 Bytes

Probably isn't as competitive as some other methods, but I thought this was a cool way of using regex.

import re;s=I=input()[::-1]+" ";i=0
while s.strip()or i<2:s=re.sub("(?!([^aeiouy ]*[aeiouy]){%s}[^aeiouy]* )."%i," ",I,0,2)[::-1];print s;i+=1

The regex (?!([^aeiouy ]*[aeiouy]){N}[^aeiouy]* ). matches any single character not within the N-th group of letters from the end of a word. Since it counts from the end of the world I reverse the string before and afterward, and I also have to add a space at the end, but after that it becomes a simple matter of using re.sub to replace every instance of these characters with a space. It does this for every value of N until the string is empty.

\$\endgroup\$
2
  • \$\begingroup\$ As nice and readable it is to use re.I, you can save 3 bytes by substituting the appropriate flag value, i.e. 2. \$\endgroup\$
    – Sp3000
    Commented Jul 4, 2015 at 7:09
  • 1
    \$\begingroup\$ @Sp3000 Only in code-golf do have negative associations with "nice and readable" \$\endgroup\$
    – KSab
    Commented Jul 4, 2015 at 7:16
1
\$\begingroup\$

Octave, 132 129 characters

p=1;x=[];y=input(0);for j=1:numel(y);if regexpi(y(j),'[aeiouy]');p+=1;elseif y(j)==" ";p=1;end;x(p,j)=y(j);end;x(x==0)=32;char(x)

Test

Input: "YEAh UppErcAsE VOwEls"

Output:

               V     
Y    Upp        Ow   
 E      Erc       Els
  Ah       As        
             E       
\$\endgroup\$
1
\$\begingroup\$

Gema: 53 48 characters

/[aeiouyAEIOUY]/=@append{u;^[[A}^K$1
 = $u@set{u;}

Note that ^[ (x1b) and ^K (x0b) are single characters. (In the below sample run I use their copy-paste friendly \e and \v equivalents, in case you want to try it out.)

Sample run:

bash-4.3$ gema '/[aeiouyAEIOUY]/=@append{u;\e[A}\v$1; = $u@set{u;}' <<< 'Programming Puzzles and Code Golf'
Pr          P           C    G    
  ogr        uzzl   and  od   olf 
     amm         es        e 
        ing 
\$\endgroup\$
1
\$\begingroup\$

Jelly, 42 bytes

Ḳµe€Øyœṗ⁸⁶ṁ$;¥\z⁶Zµ€µḷ/⁶ṁW⁸;ḣ®µ€L€Ṁ©$¡ZK€Y

Try it online!

Why Jelly, why? :-(

\$\endgroup\$
2
  • \$\begingroup\$ Longer than CJam seems weird \$\endgroup\$
    – Fatalize
    Commented Jul 31, 2017 at 17:08
  • \$\begingroup\$ @Fatalize It's because Jelly just doesn't go with strings...usually. Also you can't really compare different programming paradigms (cjam is stack-based, jelly is tacit). \$\endgroup\$ Commented Jul 31, 2017 at 17:34

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.