Help the architects visualize the skyline

MATLAB, 219 209 203 bytes

i=input('');x=1;c=0;m(1:4*numel(i))='_';for a=i;b=fix(a);m(1:b,x)='|';s=95;if a~=b;m(b+2,x+2)=95;s='/ \';end;m(b+1,x+(1:3))=s;x=x+(a>0)*3+1;m(1:b,x)='|';x=x+(a<1&c>0);c=a;end;disp(flipud(m(:,1:x-(a<1))))

This unfortunately doesn't work on Octave. Not entirely sure why, seems to be something to do with the disp/flipud bit that breaks.

Also, there is currently no definition of what a 0.5 height building looks like, nor any mention of them, so in this code I assume that they are disallowed.

The following is the code in a slightly more readable way:

i=input(''); %e.g. [0 0 2 1 3.5 0 4 2 4 2 4 6 1 6 0 5 1 0 0 1 0]
x=1;
c=0;
m(1:4*numel(i))='_';
for a=i;
    b=fix(a);
    m(1:b,x)='|';
    s=95;
    if a~=b;
        m(b+2,x+2)=95;
        s='/ \';
    end;
    m(b+1,x+(1:3))=s;
    x=x+(a>0)*3+1;
    m(1:b,x)='|';
    x=x+(a<1&c>0);
    c=a;
end;
disp(flipud(m(:,1:x-(a<1))))

First we take an input as an array, and do some variable initialisation.

i=input(''); %e.g. [0 0 2 1 3.5 0 4 2 4 2 4 6 1 6 0 5 1]
x=1;
c=0;

Because the zero height buildings are a pain - they basically end up with a width which is dependent on what they are next to (though what is printed doesn't change), we simplify things by drawing enough ground for all of the buildings. We assume each building will be 4 characters wide (because adjacent buildings merge together) - zero height ones aren't, but the excess will be trimmed later.

m(1:4*numel(i))='_';

Now we draw out each building in turn.

for a=i

First we get the integer part of the height as this will determine how many '|' we need.

    b=fix(a);

Now draw in the wall for this building - if there are two adjacent buildings, the wall for this new one will be in the same column as the wall from the last one.

    m(1:b,x)='|';

Check to see if this is a half height building. If it is, then the roof will be different. For the half heights, the roof is going to be / \ whereas full height ones it will be ___ (Matlab will implicitly replicate this from a single underscore, so save a couple of bytes there). There is an extra bit of roof one row higher for the half height buildings, so that is added as well.

    s=95;
    if a~=b;
        m(b+2,x+2)=95;
        s='/ \';
    end;

Draw in the roof

    m(b+1,x+(1:3))=s;

Now move to the start of the next building and draw in the shared wall (if the wall is too short at this point, it will be made larger when the next building is drawn). Note that zero height buildings are 1 wide, normal buildings are 4 wide, so we simplify what would otherwise be an if-else by treating (a>0) as a decimal number not a Boolean.

    x=x+(a>0)*3+1;
    m(1:b,x)='|';

Next comes a bit of hackery to work with zero height buildings. Basically what this says is if this building was zero height, and the one before that wasn't, it means the place of the next building needs incrementing by 1 because a zero height building sandwiched between two other buildings is effectively twice as wide - this accounts for the extra wall which is normally shared with an adjacent building. We also keep track of this building height for doing this check next time.

    x=x+(a<1&c>0);
    c=a;
end;

Once done, flip the building matrix to be the correct way up, and display it. Note that we also trim off any excess ground here as well.

disp(flipud(m(:,1:x-(a<1))))

So, when we run this script, we are asked for our input, for example:

[0 0 2 1 3.5 0 4 2 4 2 4 6 1 6 0 5 1 0 0 1 0]

It then generates the building and displays the result. For the above input, the following is generated:

                                     ___     ___                   
                                    |   |   |   |  ___             
            _    ___     ___     ___|   |   |   | |   |            
           / \  |   |   |   |   |   |   |   |   | |   |            
   ___    |   | |   |___|   |___|   |   |   |   | |   |            
  |   |___|   | |   |   |   |   |   |   |___|   | |   |___    ___  
__|___|___|___|_|___|___|___|___|___|___|___|___|_|___|___|__|___|_

Python 2, 199 193 188 185 bytes

a=map(int,raw_input().split())
l=max(a)+1|1
while~l:print''.join((x%2*'/  _\\ '[x<l::2]*(x<=l<x+4)or'_ '[x|1!=l>1]*3)[x<1:x+2]+'| '[x<=l>=y]*(x+y>0)for x,y in zip([0]+a,a+[0]))[1:];l-=2

This is a full program that accepts integers as input. Example input.


Kotlin, 447 442 bytes

val a={s:String->val f=s.split(" ").map{it.toFloat()}.toFloatArray();val m=(f.max()!!+1).toInt();for(d in m downTo 0){var l=0f;for(c in f){val h=c.toInt();print(if(h==d&&d!=0)if(h<l-0.5)"|" else{" "}+if(c>h)"/ \\" else "___" else if(h<d)if(d<l-0.5)"|" else{" "}+if(h==0)" " else if((c+0.5).toInt()==d)" _ " else "   " else{if(h==0)if(l<1)"  " else "| " else "|   "}.replace(' ',if(d==0)'_' else ' '));l=c;};if(d<l-0.5)print("|");println();}}

Ungolfed version:

val ungolfed: (String) -> Unit = {
    s ->

    val floats = s.split(" ").map { it.toFloat() }.toFloatArray()
    val maxH = (floats.max()!! + 1).toInt()

    for (drawHeight in maxH downTo 0) {
        var lastBuildingH = 0f
        for (f in floats) {
            val buildingH = f.toInt()
            if (drawHeight == 0) {
                // Baseline
                if (buildingH == 0)
                    if (lastBuildingH.toInt() == 0) print("__")
                    else print("|_")
                else print("|___")
            } else if (buildingH == drawHeight) {
                // Ceiling
                if (buildingH < lastBuildingH - 0.5) print("|")
                else print(" ")
                if (f > buildingH) print("/ \\")
                else print("___")
            } else if (buildingH < drawHeight) {
                // Above building
                if (drawHeight < lastBuildingH - 0.5) print("|")
                else print(" ")
                if (buildingH == 0) print(" ")
                else {
                    if ((f + 0.5).toInt() == drawHeight) print(" _ ")
                    else print("   ")
                }
            } else {
                if (buildingH == 0) print("| ")
                else print("|   ")
            }
            lastBuildingH = f;
        }
        if (drawHeight < lastBuildingH - 0.5) print("|")
        println()
    }
}