# Words -> City Skyline

## The challenge

Your program or function will accept a single string input from STDIN or a function parameter. You can assume the input will contain only alphabetic characters (a-zA-Z), spaces, and full stops. Input is case insensitive, so you should treat 'a' exactly the same as you would treat 'A'.

For each character in the string, you will output a representation of a building as per the following specification.

Each building must have a roof, designated by an underscore on the top line then a slash, space, backslash on the second line.

 _
/ \


You will then have a number of floors, matching the letter number (a=1, b=2, c=3 etc.) which are represented by a wall (|) on each side and a space in the middle. The bottom floor (and only the bottom floor) should have a foundation, which is an underscore between the walls. Like this...

|_|


So for example, 'b' would look like this

 _
/ \
| |
|_|


Now, we know that very tall, narrow buildings cannot stand and must get wider at the base, so no building can stand more than three storeys high without some additional support. So every three levels (no less) you should add a 'widening layer'. The widening layer consists of a slash and backslash directly above the walls of the section below it, and the section below should be two spaces wider than the section above. The extra layer does not count towards the height of the building.

Buildings should not overlap but should not have any unnecessary spaces between them, and the ground is always flat so all buildings should have their base on the same level.

For example, 'abcdefga' will look like this.

                           _
/ \
_    | |
_   / \   | |
_   / \  | |   | |
/ \  | |  | |  /   \
_  | |  | |  | |  |   |
_ / \ | |  | | /   \ |   |
_ / \| | | | /   \|   | |   |  _
/ \| || |/   \|   ||   |/     \/ \
|_||_||_||___||___||___||_____||_|


Spaces in the string input should be represented by a double space.

Full stops in the string input should be represented by rubble like this.

/\/\


## Further examples

Input = Hello world.

Output =

                                                   _
/ \
| |
| |
| |
/   \
|   |
|   |                       _
|   |                      / \
/     \                     | |
|     |                     | |
_            |     |          _          | |
/ \           |     |         / \        /   \
| |          /       \        | |        |   |
| |          |       |        | |        |   |
_        _        | |          |       |        | |        |   |        _
/ \      / \      /   \         |       |       /   \      /     \      / \
| |      | |      |   |        /         \      |   |      |     |      | |
| |      | |      |   |        |         |      |   |      |     |      | |
| |      | |      |   |        |         |      |   |      |     |      | |
_          /   \    /   \    /     \       |         |     /     \    /       \    /   \
/ \         |   |    |   |    |     |      /           \    |     |    |       |    |   |
| |         |   |    |   |    |     |      |           |    |     |    |       |    |   |
| |         |   |    |   |    |     |      |           |    |     |    |       |    |   |
| |    _   /     \  /     \  /       \     |           |   /       \  /         \  /     \
/   \  / \  |     |  |     |  |       |    /             \  |       |  |         |  |     |   _
|   |  | |  |     |  |     |  |       |    |             |  |       |  |         |  |     |  / \
|   |  | |  |     |  |     |  |       |    |             |  |       |  |         |  |     |  | |
|   |  | | /       \/       \/         \   |             | /         \/           \/       \ | |
/     \/   \|       ||       ||         |  /               \|         ||           ||       | | |
|     ||   ||       ||       ||         |  |               ||         ||           ||       |/   \
|_____||___||_______||_______||_________|  |_______________||_________||___________||_______||___|/\/\


Input = lorem ipsum

                                                                                             _
/ \
_             | |
/ \            | |
_                                                  | |            | |
/ \                                                 | |           /   \
| |                                    _            | |           |   |
| |                                   / \          /   \          |   |
_          | |                                   | |          |   |          |   |
/ \        /   \                                  | |          |   |         /     \
| |        |   |              _                   | |          |   |         |     |         _
| |        |   |             / \                 /   \        /     \        |     |        / \
_        | |        |   |             | |                 |   |        |     |        |     |        | |
/ \      /   \      /     \            | |                 |   |        |     |       /       \       | |
| |      |   |      |     |            | |                 |   |        |     |       |       |       | |
| |      |   |      |     |           /   \               /     \      /       \      |       |      /   \
| |      |   |      |     |           |   |        _      |     |      |       |      |       |      |   |
/   \    /     \    /       \          |   |       / \     |     |      |       |     /         \     |   |
|   |    |     |    |       |          |   |       | |     |     |      |       |     |         |     |   |
|   |    |     |    |       |         /     \      | |    /       \    /         \    |         |    /     \
|   |    |     |    |       |         |     |      | |    |       |    |         |    |         |    |     |
/     \  /       \  /         \   _    |     |     /   \   |       |    |         |   /           \   |     |
|     |  |       |  |         |  / \   |     |     |   |   |       |    |         |   |           |   |     |
|     |  |       |  |         |  | |  /       \    |   |  /         \  /           \  |           |  /       \
|     |  |       |  |         |  | |  |       |    |   |  |         |  |           |  |           |  |       |
/       \/         \/           \ | |  |       |   /     \ |         |  |           | /             \ |       |
|       ||         ||           |/   \ |       |   |     | |         |  |           | |             | |       |
|       ||         ||           ||   |/         \  |     |/           \/             \|             |/         \
|_______||_________||___________||___||_________|  |_____||___________||_____________||_____________||_________|


Input = a.a.a.x.x.x.a.a.a

                             _                    _                    _
/ \                  / \                  / \
| |                  | |                  | |
| |                  | |                  | |
| |                  | |                  | |
/   \                /   \                /   \
|   |                |   |                |   |
|   |                |   |                |   |
|   |                |   |                |   |
/     \              /     \              /     \
|     |              |     |              |     |
|     |              |     |              |     |
|     |              |     |              |     |
/       \            /       \            /       \
|       |            |       |            |       |
|       |            |       |            |       |
|       |            |       |            |       |
/         \          /         \          /         \
|         |          |         |          |         |
|         |          |         |          |         |
|         |          |         |          |         |
/           \        /           \        /           \
|           |        |           |        |           |
|           |        |           |        |           |
|           |        |           |        |           |
/             \      /             \      /             \
|             |      |             |      |             |
|             |      |             |      |             |
|             |      |             |      |             |
/               \    /               \    /               \
_      _      _     |               |    |               |    |               |     _      _      _
/ \    / \    / \    |               |    |               |    |               |    / \    / \    / \
|_|/\/\|_|/\/\|_|/\/\|_______________|/\/\|_______________|/\/\|_______________|/\/\|_|/\/\|_|/\/\|_|


## Rules

• Of course this is code golf, lowest score in bytes wins
• Standard loophole rules apply
• Any number of additional blank lines before or after the output are allowed
• You can choose to output the entire result in one string or offer the output as an array where each element represents one line of output, or send to STDOUT

## Note

This is my first post to PPCG, so please go easy on me. It's been through the sandbox. Any negative points or possible improvements please post as a comment and I'll do what I can

• Converting [a,z] and [A,Z] to [1,26] seems like a pointless requirement. It would be much better to just use a list integers as input (having 0 be the input for rubble). Also, posting your challenge after it's been in the Sandbox for only 21 hours, without waiting to get any votes or feedback from more than one user, does not count as it having "been through the sandbox". It's recommended to leave challenges in the Sandbox for 48-72 hours at the minimum, to give people plenty of time to review them. – Mego Sep 18 '16 at 19:06
• Reminds me of this. Nice first challenge, but I suggest you consider changing the [a,z],[1.26] parts the Mego mentioned. Having it optional is often the best (unless it's a key part of the challenge (it's not here). – Stewie Griffin Sep 18 '16 at 20:15
• I will say, this is a very nice first challenge. Welcome to the site! – DJMcMayhem Sep 18 '16 at 22:20
• I can understand the point about the a-z mapping to integers. It made sense in the first iteration of the challenge but since editing for clarity and brevity (there was a backstory which I have removed) the letters are no longer relevant. I will edit appropriately as soon as possible – Darren H Sep 18 '16 at 22:22
• I prefer a-z, space, period, personally. – isaacg Sep 19 '16 at 17:54

## JavaScript (ES6), 330326 ... 315 309 bytes

Builds the ASCII art recursively, starting with the bottom floor and applying several regular expressions between each stage:

(a,l,R=(E,b)=>E.split:.map((e,i)=>l=(l||a).replace(RegExp(e,'g'),b?b.split:[i]:n=>(x=(n.charCodeAt()-65)%32)<0?x+1?'/y/y':n+n:x%3+'_'.repeat((x/3<<1)+1)+0)))=>(L=l)?(R('/y:_:/xy:1:2:/xx(x+)y:0(x+)0:3','  :x: _ :3:1: 2$10 :/$1y:0'),L==l?(l=a.join
,R('\\d:x:y','|: :\\'),l):f([l].concat(a),l)):f(R('.'),l)


### How it works

1) Bottom floor

We start by translating the input string into a bottom floor such as:

"ab cd.df.hcab"  -->  "0_01_0  2_00___0/y/y0___02___0/y/y1_____02_00_01_0"


where:

• y is a shorter alias for the backslash (which requires escaping)
• The digit (0, 1 or 2) just before a sequence of _ is the left wall of the building. It represents the number of walls that must be placed above it before the next 'widening layer'.
• The digit after a sequence of _ is the right wall of the building and is always set to 0.

2) Regular expressions applied between each stage

The recursive process consists of applying 9 replacements on the previous floor, using the following regular expressions:

1. /\/y/g => " " (remove the rubble)
2. /_/g => "x" (replace the foundation or the top of the building with a solid block)
3. /\/xy/g => " _ " (replace the last widening layer with the top of the building)
4. /1/g => "3" (temporarily replace 1 with 3 -- see last step)
5. /2/g => "1" (replace 2 with 1)
6. /\/xx(x+)y/g => " 2$10 " (replace a widening layer with a new, narrower wall) 7. /0(x+)0/g => "/$1y" (replace the top of the wall with a widening layer)
8. /3/g => "0" (replace 3 with 0)

For instance, here are the successive transformations of 2___0 (bottom floor generated by a 'f'):

"2___0" > "1xxx0" > "0xxx0" > "/xxxy" > " 2x0 " > " 1x0 " > " 0x0 " > " /xy " > "  _  "

_
/xy       /xy
0x0       0x0       0x0
1x0       1x0       1x0       1x0
2x0       2x0       2x0       2x0       2x0
/xxxy     /xxxy     /xxxy     /xxxy     /xxxy     /xxxy
0xxx0     0xxx0     0xxx0     0xxx0     0xxx0     0xxx0     0xxx0
1xxx0     1xxx0     1xxx0     1xxx0     1xxx0     1xxx0     1xxx0     1xxx0
2___0     2___0     2___0     2___0     2___0     2___0     2___0     2___0     2___0


NB: The top of the building is then replaced by a x. This is not shown in the above diagram.

3) Regular expressions applied to the final result

The recursion stops when there's nothing more to replace, which means that we are beyond the top of the highest building.

We now need to clean up everything with yet another few regular expressions:

1. /\d/g => "|" (replace digits with pipes)
2. /x/g => " " (replace solid blocks with spaces)
3. /y/g => "\" (replace y with backslashes)

For instance:

  _            _
/xy          / \
0x0          | |
1x0          | |
2x0   -->    | |
/xxxy        /   \
0xxx0        |   |
1xxx0        |   |
2___0        |___|


### Demo

let f =

(a,l,R=(E,b)=>E.split:.map((e,i)=>l=(l||a).replace(RegExp(e,'g'),b?b.split:[i]:n=>(x=(n.charCodeAt()-65)%32)<0?x+1?'/y/y':n+n:x%3+'_'.repeat((x/3<<1)+1)+0)))=>(L=l)?(R('/y:_:/xy:1:2:/xx(x+)y:0(x+)0:3','  :x: _ :3:1: 2$10 :/$1y:0'),L==l?(l=a.join
,R('\\d:x:y','|: :\\'),l):f([l].concat(a),l)):f(R('.'),l)

console.log(f('ab cd.df.hcab'));

Contributors:
4 bytes saved thanks to Hedi
8 bytes saved thanks to Not that Charles

• Anyone wanting to take an input as an integer is welcome to but I'd suggest such an entry should be non-competing – mbomb007 Sep 19 '16 at 20:35
• @mbomb007 - This is fixed. My current optimizations do not compensate for the the cost of the infamous .charCodeAt(), though. – Arnauld Sep 20 '16 at 6:59
• You don't need new  in new RegExp(e,'g') – Hedi Sep 20 '16 at 7:32
• I like this approach a lot. Some ideas that may help: 1. use a char like y that requires no escaping for your forward slash. 2. if you use _ for the ground floor, you can still differentiate the top with the regex: /_ /. – Not that Charles Sep 22 '16 at 19:50
• @NotthatCharles - I actually never noticed the "double space" rule. ;) This is fixed. – Arnauld Sep 23 '16 at 16:30

# PHP, 386376367364362358 356 bytes

first approach; may still be golfable.

foreach(str_split($argv[1])as$c)for($n=28,$w='.'!=$c?1+2*ceil(1/3*$n=31&ord($c)):4,$p=$y=0;$y<36;){$s=str_pad("",$w,$y||!$n?" ":_);if($n>26&&!$y){$s="/\\/\\";$n=-1;}elseif($n-->0){$s[$p]=$s[$w-$p-1]="|";if($n%3<1){$o[$y++].=$s;$s=str_pad("",$w);$s[$p]="/";$s[$w-++$p]="\\";}}$o[$y++].=$s;if(!$n)$o[$y++].=str_pad(_,$w," ",2);}for($y=36;$y--;)echo"$o[$y]
";


PHP, 366 362 361 360 357 bytes

similar approach with a subfunction:

function a($p,$r){global$o,$w,$y;$o[$y++].=str_pad(str_pad($r[0],2*$p,$r[1]).$r[2],$w," ",2);}foreach(str_split($argv[1])as$i=>$c)for($n=28,$w='.'!=$c?1+2*$p=ceil(1/3*$n=31&ord($c)):$p=4,$y=0;$y<36;)if($n>26&&!$y)$o[$n=$y++].="/\\/\\";elseif($n-->0){a($p,$y?"| |":"|_|");if($n%3<1)a($p--,"/ \\");if(!$n)a(1," _");}else a(0,"");for($y=36;$y--;)echo"$o[$y] ";  breakdown for the second approach function a($p,$r) { global$o,$w,$y;
$o[$y++].=                  // 3. add result to current line, increase line counter
str_pad($r[0],2*$p,$r[1]).$r[2]     // 1. A + inner width(=2*$p-1) times B + C ,$w," ",2);             // ... to $w with blanks on both sides # 2==STR_PAD_BOTH } foreach(str_split($argv[1])as$i=>$c)
for(
$n=28,$w='.'!=$c //$w=total width
?1+2*$p=ceil(1/3*$n=31&ord($c)) //$n=storey count, $p=(inner width+1)/2 :$p=4                           // $n=28,$p <= $w=4 for rubble ,$y=0;$y<36;) //$y=line counter
if($n>26&&!$y)
$o[$n=$y++].="/\\/\\"; // bottom line=rubble,$n=0
elseif($n-->0) { a($p,$y?"| |":"|_|"); // add storey if($n%3<1)a($p--,"/ \\"); // add widening layer/roof if(!$n)a(1," _");           // add roof top
}
else
a(0,"");                    // idk why str_pad doesn´t yield a warning here

for($y=36;$y--;)if($s=rtrim($o[$y]))echo"$s\n"; // output


+16 bytes if leading newlines are not allowed:
Replace echo"$o[$y]\n; with if($s=rtrim($o[$y]))echo"$s\n";.

-3 bytes for any of ;<=>?[\]^_{|}~ as rubble: Replace 1) ($n=31&ord($c)) with $n, 2) $n=28,$w='.'!=$c with ($n=31&ord($c))<27 and 3) 4 with ($n=28)/7. Another -8 for >, ^ or ~ as rubble: Undo 3) • if(!$n){$o[$y++].=str_pad(_,$w," ",2);} -2 Bytes for the brackets – Jörg Hülsermann Sep 19 '16 at 10:21 • and output can be done 3 bytes shorter: for($y=36;$y--;)echo"$o[$y]\n";; but I have a new approach that saves an other 2 bytes. – Titus Sep 19 '16 at 10:35 • php.net/manual/en/functions.anonymous.php for the second approach function a($p,$r)use($o,$w,$y)' instead of global – Jörg Hülsermann Sep 19 '16 at 21:41
• @JörgHülsermann: use only works for anonymous functions. it would save 2 bytes; but I would have to store that function in a variable $a= instead of giving it a name (+3 bytes) and add a $ to each of the four calls. – Titus Sep 19 '16 at 21:54

# Pyth, 93 79 bytes

K"/\\"j_.tsm?hJxGdC_m.[hyNk\ +\_mj*hy/k4?nkJ\ \_?%k4"||"Kh=+J=Nh/J3[F*2|@d;Krz0


### Explanation

I hid this away by default since it's way too long.

<ul type="disc"><li>Initialize <code>K</code> to <code>"/\"</code>.</li><li>Lowercase (<code>r</code>&hellip;<code>0</code>) the input (<code>z</code>).</li><li>For each (<code>m</code>) character <code>d</code> in the lowercase input:</li><ul type="disc"><li>Find the index (<code>x</code>) of the character (<code>d</code>) in the alphabet (<code>G</code>). This results in a number 0-25 for the letters and -1 for anything else. Save the index to <code>J</code>.</li><li>Add one (<code>h</code>). This results in a positive number for the letters and 0 for anything else.</li><li>If the result was nonzero (<code>?</code>):</li><ul type="disc"><li>Int-divide (<code>/</code>) <code>J</code> by <code>3</code> and increment (<code>h</code>) and save (<code>=</code>) the result to <code>N</code>.</li><li>Increment (<code>=+</code>) <code>J</code> by that amount.</li><li>Add one (<code>h</code>). This yields the height of the house excluding the roof.</li><li>For each (<code>m</code>) number <code>k</code> from 0 to result-1:</li><ul type="disc"><li>Take <code>" "</code> (<code>\ </code>) if (<code>?</code>) the number (<code>k</code>) is not equal to (<code>n</code>) <code>J</code>, otherwise take <code>"_"</code> (<code>\_</code>).</li><li>Int-divide (<code>/</code>) the number (<code>k</code>) by <code>4</code>, double (<code>y</code>) and add one (<code>h</code>).</li><li>Multiply the string by the number (<code>*</code>).</li><li>Take <code>"||"</code> if (<code>?</code>) the number (<code>k</code>) is not divisible by 4 (<code>%</code>&hellip;<code>4</code>), otherwise take the string in <code>K</code>.</li><li>Join (<code>j</code>) that string by the previous one. This puts the previous string in the middle.</li></ul><li>Prepend (<code>+</code>) a <code>"_"</code> (<code>\_</code>) to the resulting list to add the roof.</li><li>For each line <code>k</code> in the list:</li><ul type="disc"><li>Take <code>N</code>, double (<code>y</code>) and add one (<code>h</code>).</li><li>Pad (<code>.[</code>) the line (<code>k</code>) to that length with spaces (<code>\ </code>).</li></ul><li>We now have the rows for this one house. Reverse (<code>_</code>) and transpose (<code>C</code>) the result to get the reversed columns.</li></ul><li>If the result was zero (all the way up there):</li><ul type="disc"><li>Take the intersection (<code>@</code>) of the character (<code>d</code>) and <code>" "</code> (<code>;</code>). This yields an empty string for periods and a space for itself.</li><li>Logical OR (<code>|</code>) that with the string in <code>K</code>. This yields that string for periods and a space for itself.</li><li>Repeat that string (<code>*</code>) <code>2</code> times and cast to array (<code>[F</code>). This yields the (reversed) columns for spaces and periods.</li></ul></ul><li>Concatenate (<code>s</code>) the resulting lists. This yields the total reversed columns for the output.</li><li>Transpose, padding with spaces (<code>.t</code>), reverse (<code>_</code>) and join by newline (<code>j</code>). Implicitly print the output.</li></ul>

# Perl, 147 146 bytes

Includes +1 for -p

Run with input on STDIN, e.g.

citysky.pl <<< " abcdefgxyz."


citysky.pl:

#!/usr/bin/perl -p
s%.%@{[map chr~-ord(lc$&)*4/3-4*(abs||-9),-9..9]}%g;y/M\xa248 A|-\xc6\0-\xff/MA|| A}-\xc6A/d,$a=(lc$a).$_ for($_)x36;*_=a;s/\x9f.*?\K\x9f/\xa3/g;y%A\xc6\x9f-\xa3\x0b-\xff%__/|||\\ %  Works as shown, but replace the \xhh escapes by their literal value to get the claimed score. You can do that using this commandline: perl -0pi -e 's/\\x(..)/chr hex$1/eg;s/\n$//' citysky.pl  I haven't really explored any other approaches, so this may be very beatable... # Haskell, 289 bytes c?l=c++l++c c%s=("|"?(drop 2(r s)>>c)):s g 46=["/\\"?""] g 32=[" "] g x="_"%h(mod x 32) h 1=["/ \\"," _ "] h x=(" "%h(x-1))!x v!x|mod x 3/=1=v|z<-'/':r v++"\\"=z:map(" "?)v r v=v!!0>>" " f t|l<-map(g.fromEnum)t,m<-maximum(map length l)-1=unlines[l>>= \x->(x++cycle[r x])!!i|i<-[m,m-1..0]]  ## Ruby, 245 ->s{a=['']*36 w=' ' s.chars{|c|a[u=0]+=c<?!?w*2:c<?/?"/\\"*2:(h=c.upcase.ord-64 1.upto(1+h+=(h-1)/3){|t|u=[u,l=1+2*((f=h-t)/4)].max a[t]+=w*(a[0].size-a[t].size)+(f<-1?w:f<0??_:(f%4<1?[?/,?\\]:[?|]*2)*(w*l)).center(u+2)} "|#{?_*u}|")} a.reverse}  You allow as many extra newlines as you want, so I'm taking liberty with that. Aside from that, the process is as follows: 1. Initialize an output array a. 2. For each char: 1. if it's ' ', add   to a[0] 2. if it's '.', add /\/\ to a[0] 3. otherwise: 1. calculate the height (c.upcase.ord + (c.upcase.ord-1)/3) 2. for each row in a: 1. pad the row with whitespace. a[t]+=w*(a[0].size-a[t].size) 2. if we're one above h, center a _ 3. else if we're above height, center a 4. else if we're below height, center | | or / \ of the proper width (1+2*((h-t)/4), depending on if h-t%4==0 5. add "|___|" of the right width to a[0] 3. return a.reverse I bet I can get it smaller if I work out the math to avoid reverse # PHP, 297 bytes foreach(str_split($argv[1])as$c)for($j=0,$h=ord($c)-64,$g=$h+$f=ceil($h/3),$w=$v=$h<0?$h<-18?2:4:2*$f+1;$j<36;$j++,$g--,$v-=$h>0&&$v>1?($g%4||!$j)?0*$n="|$s|":2+0*$n="/$s\\":$v+0*$n=['','_','',0,'/\/\\'][$v],$o[$j].=str_pad($n,$w,' ',2))$s=str_repeat($j?' ':'_',$v-2);krsort($o);echo join($o,' ');  A more readable version: foreach (str_split($argv[1]) as $character) { for ($line = 0,
$buildingHeight = ord($character) - 64,
$floorsLeft =$buildingHeight + $supportFloors = ceil($buildingHeight / 3),
$buildingWidth =$widthOnThisFloor = $buildingHeight < 0 ?$buildingHeight < -18
? 2
: 4
: 2 * $supportFloors + 1;$line < 36;

// The body of the for-loop is executed between these statements

$line++,$floorsLeft--,
$widthOnThisFloor -=$buildingHeight > 0 && $widthOnThisFloor > 1 ? ($floorsLeft % 4 || !$line) ? 0 *$floorString = "|$middleSpacing|" : 2 + 0 *$floorString = "/$middleSpacing\\" :$widthOnThisFloor + 0 * $floorString = ['', '_', '', 0, '/\/\\'][$widthOnThisFloor],
$outputArray[$line] .= str_pad($floorString,$buildingWidth, ' ', 2)
) {
$middleSpacing = str_repeat($line ? ' ' : '_', $widthOnThisFloor - 2); } } krsort($outputArray);
echo join(\$outputArray, '
');
`