Staircase writing

35

3

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.

Fatalize

Posted 2015-07-01T14:51:24.663

Reputation: 32 976

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." – JohnE – 2015-07-01T15:01:24.503

1Y is Y a vowel ? – Optimizer – 2015-07-01T15:02:55.317

1@JohnE indeed it was, I fixed it. Thanks. – Fatalize – 2015-07-01T15:03:43.330

@Optimizer Y is used as both in English, so I say it is a vowel in this challenge – Fatalize – 2015-07-01T15:04:30.443

Related. Related. – Martin Ender – 2015-07-01T15:08:33.763

2The 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. – trichoplax – 2015-07-01T15:09:26.983

3Is trailing newlines / spaces allowed? – Loovjo – 2015-07-01T15:31:00.057

@Loovjo There shouldn't be any unnecessary empty lines. so no. There can be extra spaces at the end of lines as long as it does not make the line longer than the longest line (for instance on the first example, you can have extra spaces on the first line up to the f at the end of the second line) – Fatalize – 2015-07-01T15:48:22.623

Btw, the UppErcAsE VOwEls are missing backticks. – Loovjo – 2015-07-01T15:53:53.300

@Loovjo Fixed, thanks. – Fatalize – 2015-07-01T15:55:11.413

Answers

18

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.

Martin Ender

Posted 2015-07-01T14:51:24.663

Reputation: 184 808

You don't need printf. Just replace \e with the ESC byte (0x1b). – Dennis – 2015-07-01T15:41:21.557

@Dennis Oh, that's much better, thank you. – Martin Ender – 2015-07-01T15:45:49.097

1This is so awesome!!! – kirbyfan64sos – 2015-07-01T17:03:16.913

This answer is why no one takes Retina seriously ;) – Christopher Wirt – 2015-07-02T18:24:41.337

@ChristopherWirt Please elaborate :) (Although I would actually be terrified if anyone took Retina seriously.) – Martin Ender – 2015-07-02T20:42:08.347

8

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#

Dennis

Posted 2015-07-01T14:51:24.663

Reputation: 196 637

Don't forget to unset PS1save afterwards. – usandfriends – 2015-07-01T23:46:08.833

5

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.

Loovjo

Posted 2015-07-01T14:51:24.663

Reputation: 7 357

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. – TNT – 2015-07-01T22:03:18.183

I like the x++-~-p – Ypnypn – 2015-07-03T18:35:01.070

4

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.

jcai

Posted 2015-07-01T14:51:24.663

Reputation: 973

char*t=malloc(M*M); --> char t[M*M]; and for(i=0;i<M*M;++i) --> for(;i<M*M;++i) – Spikatrix – 2015-07-02T10:00:24.343

Good catches, edited. – jcai – 2015-07-02T13:36:23.507

Isn't this C99 only due to char t[M*M]? – Zacharý – 2017-07-31T13:42:02.397

4

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.

aditsu quit because SE is EVIL

Posted 2015-07-01T14:51:24.663

Reputation: 22 326

4

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.

Dennis

Posted 2015-07-01T14:51:24.663

Reputation: 196 637

3

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!

JohnE

Posted 2015-07-01T14:51:24.663

Reputation: 4 632

2

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

manatwork

Posted 2015-07-01T14:51:24.663

Reputation: 17 865

If I'm not mistaken, you can shorten your regex to /(?=[aeiouy ])/i. – Alex A. – 2015-07-01T15:49:57.853

Ah, you are right @AlexA. The space as separate word was only important for an earlier theory. Thanks. – manatwork – 2015-07-01T16:06:14.010

2

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

Toby Speight

Posted 2015-07-01T14:51:24.663

Reputation: 5 058

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. – manatwork – 2015-07-02T08:27:14.013

I'm sure it was working earlier. I'll have a look and see what I've broken. Thanks for the heads-up, @manatwork! – Toby Speight – 2015-07-02T09:48:54.747

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. – Toby Speight – 2015-07-02T10:16:15.787

2

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 */
    }
}

Toby Speight

Posted 2015-07-01T14:51:24.663

Reputation: 5 058

' ' --> 32 and f(char*s){int l=0,r=1,v,c; --> l,r=1,v,c;f(char*s){ – Spikatrix – 2015-07-02T10:03:38.673

@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! – Toby Speight – 2015-07-02T10:08:30.620

2

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.

KSab

Posted 2015-07-01T14:51:24.663

Reputation: 5 984

As nice and readable it is to use re.I, you can save 3 bytes by substituting the appropriate flag value, i.e. 2. – Sp3000 – 2015-07-04T07:09:19.763

1@Sp3000 Only in code-golf do have negative associations with "nice and readable" – KSab – 2015-07-04T07:16:03.887

2

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.

InputUsername

Posted 2015-07-01T14:51:24.663

Reputation: 21

1

Jelly, 42 bytes (non-competing?)

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

Try it online!

Why Jelly, why? :-(

Erik the Outgolfer

Posted 2015-07-01T14:51:24.663

Reputation: 38 134

Longer than CJam seems weird – Fatalize – 2017-07-31T17:08:38.123

@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). – Erik the Outgolfer – 2017-07-31T17:34:23.847

1

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       

sudo rm -rf slash

Posted 2015-07-01T14:51:24.663

Reputation: 1 076

1

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 

manatwork

Posted 2015-07-01T14:51:24.663

Reputation: 17 865