ASCII Mountain Range

This is a CC-BY-SA challenge from the CCSC Mid-South Programming Contest 2018.
Author(s): Brent Yorgey
Source: Consortium for Computing Sciences in Colleges Mid-South Programming contest 2018
https://ccsc18.kattis.com/problems/ccsc18.mountainrange

Good news: you have been hired to do all the special effects for a film! One of the scenes will take place outside, on a windswept plain with a towering mountain range in the background. Your job is to turn a description of the mountain range into a rendered image.

The bad news: the film’s producers have an extremely small budget, and so they can’t afford luxuries like rendered 3D graphics made up of pixels. Hence, you will be making your image out of ASCII characters.

Write a function or full program to produce mountain range ASCII art according to the spec below. This is ; tie breaker is earlier submission.

Input

You will receive a list of 5-tuples in any standard input format for PPCG and/or your language. Each 5-tuple is the values (x, h, z, t, s) representing one mountain of the range you will draw.

• x is the x-coordinate of the mountain's peak, with 1≤x≤199.
• h is the height of the mountain, with 1≤h≤100.
• z indicates the relative distance of the mountain from the viewer, with 1≤z≤m. The smaller the value of z, the closer the mountain is to the viewer. Closer mountains hide ones that are farther away.
• t is the height of the treeline. The side of the mountain up to the treeline should be filled with trees, represented by the ASCII character Y.
• s is the height of the snowline. The side of the mountain above the snowline should be filled with snow, represented by the ASCII character *.

The base of each mountain will not extend beyond 0 or 200 horizontally; you may assume that 0≤x−h and x+h≤200. The height of each mountain is no more than 100, so all mountains will fit within a 100x200 grid. The snow and trees will not overlap or exceed the height of the mountain; you may assume that 0≤t≤s≤h. All numbers are non-negative, and all z-values will be unique.

Output

The output should consist of an ASCII drawing of the specified mountain range. Each mountain is triangular in shape with its sides drawn using / and \ characters. For example, here is a mountain of height 4:

   /\
/  \
/    \
/      \


This mountain has x-coordinate 4, since the horizontal distance from the left margin to its peak is 4 units.

Trees and snow should fill the interior of the mountain but not obscure the sides of the mountain. For example, here is a mountain with height 6, treeline height 2, and snowline height 3:

     /\
/**\
/****\
/      \
/YYYYYYYY\
/YYYYYYYYYY\


That is, the trees extend from the base of the mountain up to a height of 2 units, and the snow starts at a height of 3 units and extends to the top of the mountain.

Mountains with a smaller z-coordinate may hide all or part of a mountain with a larger z-coordinate; see the third sample input/output below.

Leading whitespace is significant, and should be as specified by the x-position and width of the drawn mountains. Leading/trailing newlines are allowed, and any amount of spaces after each line.

Examples

(8, 5, 1, 2, 3)

       /\
/**\
/    \
/YYYYYY\
/YYYYYYYY\


(2, 2, 1, 0, 1)

 /\
/  \


(8, 5, 2, 2, 3), (2, 2, 1, 0, 1), (9, 8, 3, 1, 4), (17, 4, 4, 4, 4)

        /\
/**\
/****\
/*/\***\
/ /**\   \  /\
/ /    \   \/YY\
/\ /YYYYYY\   \YYY\
/  \YYYYYYYY\YYY\YYY\


If you find other interesting examples, please share in your answers!

Python 2, 194191 186 bytes

g=map(list,[' '*200]*100)
for x,h,z,t,s in sorted(input(),key=lambda m:-m[2]):
for i in range(h):g[-h+i][x+~i:x-~i]=['/']+[' *Y'[(i<h-s)-(i>=h-t)]]*2*i+['\\']
for l in g:print''.join(l)


Try it online!

Prints the whole 100x200 grid.

Ruby, 133 bytes

->a{r=[];a.sort_by{|x|-x[2]}.map{|x,h,z,t,s|h.times{|i|(r[i]||=?\s*200)[x-j=h-i,j*2]=?/+" Y*"[i<t ?1:i<s ?0:2]*~-j*2+?\\}};r.reverse}


Try it online!

Anonymous lambda. Takes input as array of parameter arrays for mountains, returns array of strings.

Walkthrough

->a{                 #Input array is a
r=[];               #Initialize the output array
a.sort_by{|x|-x[2]} #Sort by decreasing distance
.map{|x,h,z,t,s|    #Loop through the mountains
h.times{|i|        #For each mountain fill h lines starting from the ground
(r[i]||=?\s*200)  #If we haven't yet been so high, initialize with spaces
[x-j=h-i,j*2]=    #Take a slice from x-(h-i) for double length of h-i
?/+" Y*"[i<t ?1:i<s ?0:2]*~-j*2+?\\ #Draw the sides of the mountain (/\),
} #with (h-i-1)*2 of the appropriately selected filler symbols in the middle
};r.reverse         #Reverse to the top->bottom order suitable for printing
}


Charcoal, 79 bytes

  ＡθＷθ«≔⊟Φθ⁼§κ²⌈Ｅθ§μ²ι≔Φθ¬⁼κιθ≔§ι¹ηＪ§ι⁰±η≔⟦§ι⁴⁻η§ι³η⟧ζＦY *«≔⊟ζεＧ↘ε←⊗ε↗εκ→»Ｐ↘η←↙η


Try it online! Link is to verbose version of code. Note: Leading space used to force the required indentation. Explanation:

ＡθＷθ«


Loop while the input array is not empty.

≔⊟Φθ⁼§κ²⌈Ｅθ§μ²ι


Find the most distant mountain in the array.

≔Φθ¬⁼κιθ


Remove it from the array.

≔§ι¹η


Get the height of the mountain.

Ｊ§ι⁰±η


Jump to the top of the mountain.

≔⟦§ι⁴⁻η§ι³η⟧ζ


Get the snowline, treeline and height.

ＦY *«


Loop over the trees, bare mountain and snow.

≔⊟ζεＧ↘ε←⊗ε↗εκ→»


Draw a trapezium of the desired height.

Ｐ↘η←↙η


Draw the sides of the mountain.

C (clang), 284261257 252 bytes

a,b,*z,i,j,k,o;c(*a,*b){return b[2]-a[2];}f(*m,n){qsort(m,n,5*sizeof *m,c);for(i=101;--i>0;puts(""))for(j=0;++j<200;putchar(o))for(o=32,k=0;k<n;z=&m[5*k++],a=z[0]-z[1]+i,b=z[0]-~z[1]-i,o=z[1]/i&&j/a&&b/j?"/\\*Y "[j==a?0:j==b?1:i>z[4]?2:z[3]/i?3:4]:o);}


Try it online!

Credits:

-3 bytes Thanks Kevin Cruijssen

f() takes these params:

m: array of integers with all mountains' parameters
n: number of mountains

m should be a flat array of specifying x, h, z, t and s for mountains with each mountain's parameters appended to this array.

f() prints full 100x200 grid of ASCII scenery render.

Ungolfed:

a,b,*z,i,j,k,o;
c(*a,*b){return b[2]-a[2];}             //comaprison function for sort by z-order
f(*m,n){
qsort(m,n,5*sizeof(*m),c);             //sort mountains by z-order
for(i=100;i>0;i--,puts("")) {          //for each row
for(j=1;j<200;j++){                   // for each pixel in this row
o=' ';                               //initialize pixel to background pixel value
for(k=0;k<n;k++) {                   // for each mountain, in z-order distant to close
z=&m[5*k];                          //start pointer for mountain's params
a=z[0]-z[1]+i;                      //min x limit of mountain on this line
b=1+z[0]+z[1]-i;                    //max x limit of mountain on this line
o=z[1]/i && j/a && b/j ? "/\\*Y "[j==a?0:j==b?1:i>z[4]?2:z[3]/i?3:4]:o;
//calculate pixel value ovrwriting previous mountain if necessary
}
putchar(o);                  //print pixel value
}
}
}

• for(j=1;j<200;putchar(o),j++) can be for(j=0;++j<200;putchar(o)) (-2 bytes) and b=1+z[0]+z[1]-i can be b=z[0]-~z[1]-i (-1 byte) – Kevin Cruijssen Apr 19 '18 at 14:02
• Thanks @Kevin. Applied same trick to i too, saved 2 more bytes. – GPS Apr 19 '18 at 14:27
• Suggest 40 instead of 5*sizeof *m, --i instead of --i>0, b=*z++-~*z-i,o=*z/i&&j/a&&b/j?j-a?j-b?i>z[3]?42:z[2]/i?89:32:92:47:o)a=i-1[z=m+5*k++]+*z instead of z=&m[5*k++],a=z[0]-z[1]+i,b=z[0]-~z[1]-i,o=z[1]/i&&j/a&&b/j?"/\\*Y "[j==a?0:j==b?1:i>z[4]?2:z[3]/i?3:4]:o) – ceilingcat Jul 9 '18 at 3:00

JavaScript (Node.js), 265243229210204 203 bytes

a=>a.sort((x,y)=>y[2]-x[2]).map(([x,h,,t,s])=>{for(y=h;y--;)for(i=h+x-y;i--;)(d=R[y]=R[y]||[])[i]=i<x-h+y?d[i]||" ":"Y *\\/"[i>x-h+y?i<h+~y+x?y<t?0:y<s?1:2:3:4]},R=[])&&R.map(x=>s=x.join+
+s,s="")&&s


Try it online!