| Bytes | Lang | Time | Link |
|---|---|---|---|
| 183 | C GCC in a terminal | 240915T110538Z | matteo_c |
| 079 | C in an xterm | 240914T140341Z | G. Sliep |
| 141 | Zsh extendedglob in xterm | 240914T222714Z | GammaFun |
| 039 | Z80 Machine Code | 240914T055429Z | v-rob |
| 033 | Charcoal | 240913T163742Z | Neil |
| 035 | Perl 5 + pF M5.10.0 | 240913T150923Z | Dom Hast |
C (GCC) in a terminal, 185 183 bytes
h;w;d;W;i;p;main(m,v)char**v,*m;{for(W=strlen(*++v)+1,m=calloc(W*3,W);w[*v];m[(++h+d+!d+W)*W+w++]="\\_/"[d])h-=d=w[*v]-100>>3;for(;i<3*W*W;putchar(++i%W?w?:32:p?p=0,10:13))p|=w=m[i];}
C (GCC) with many empty lines, 171 169 bytes
h;w;d;W;i;main(m,v)char**v,*m;{for(W=strlen(*++v)+1,m=calloc(W*3,W);w[*v];m[(++h+d+!d+W)*W+w++]="\\_/"[d])h-=d=w[*v]-100>>3;for(;i<3*W*W;putchar(++i%W?w?:32:10))w=m[i];}
- -2 bytes thanks to @ceilingcat
In ATO, both versions output many empty lines. The first version does not print empty lines when run in a terminal.
C (in an xterm), 80 79 bytes
l(char*s){for(puts("\e[2J\e[99B");*s;)printf("/\e[A\0\e[B\\\0_"+(1^*s++&3)*5);}
Clears the terminal and draws a landscape at the bottom of the screen:

Zsh --extendedglob in xterm, 141 bytes
Inspired by G. Sliepen's C answer, but fixed to follow us → /‾ and make ud/du not unnecessarily switch lines.
For fairness, this also sends \e[2J\e[99B to clear screen and move cursor down 99 lines, just as in their answer.
<<<$'\e[2J\e[99B'
m=(u '\e[A' d '\e[B')
for c
printf ${${c#[^$v]}:+$m[1+m[(i)$c]]}${${${c/u//}/d/\\}/s/${${${v#d}:+‾}:-_}}&&v=${${c#s}:-$v}
Don't try it online, it doesn't work there!
Zsh --extendedglob in xterm, 78 bytes
--extendedglob in xterm, 78 bytesSame idea as G. Sliepen's C answer, both of our answers don't follow spec for us → /‾. ~~I think I have an idea to fix it though. EDIT: See above.
m=(u $'/\e[A' d $'\e[B\\' s _)
<<<$'\e[2J\e[99B'${1//(#m)?/$m[m[(i)$MATCH]+1]}
Z80 Machine Code, 39 bytes
1AB7C81323FE552005362F250600FE44200524365C065FFE5320E5A0200624367E2518DC7018D9
Without the requirement for the s character being different based on whether it follows a u or d, the code can be gotten down to 27 bytes.
Explanation of disassembled code:
; de = Pointer to input string. Hitting a null character causes the subroutine
; to return. Characters other than 's', 'u', 'd' cause undefined behavior.
; hl = Pointer to starting location within output array, minus 1. The width of
; the array must be 256 bytes.
DrawLandscape:
ld a, (de) ; Get the character from the input string. If
or a ; it's the null character, return.
ret z
inc de ; Increment both input and output pointers.
inc hl
cp 'u' ; If the character is 'u', draw a '/', move the
jr nz, noUp ; pointer up a row, and set the lower straight
ld (hl), '/' ; value to 0, indicating a '~' will be drawn one
dec h ; position below the pointer.
ld b, 0
noUp:
cp 'd' ; If the character is 'd', move the pointer down
jr nz, noDown ; a row, draw a '\', and set the lower straight
inc h ; value to '_', which we draw later.
ld (hl), '\'
ld b, '_'
noDown:
cp 's' ; If the character is 's', we need to draw a
jr nz, DrawLandscape ; straight character.
and b ; If the lower straight value is not set, we
jr nz, drawLower ; need to draw a '~' below the pointer.
inc h
ld (hl), '~'
dec h
jr DrawLandscape
drawLower:
ld (hl), b ; Otherwise, draw the lower straight value
jr DrawLandscape ; directly.
There's no portable way to print stuff in Z80, but here's an example of the code in action:
Charcoal, 33 bytes
≔_θFS≡ιsθd«M⁼θ_↓\≔_θ»«M⌕θ_↓/≔‾θ
Try it online! Link is to verbose version of code. Explanation:
≔_θ
Start by mapping s to _.
FS≡ι
Loop through and switch on each character of the input.
sθ
For an s just output its current mapping.
d«M⁼θ_↓\≔_θ»
For a d, move down if the mapping is _, then print a \ and set the mapping to _.
«M⌕θ_↓/≔‾θ
Otherwise, move up unless the mapping is _, then print a / and set the mapping to ‾ (which costs 3 bytes to represent in Charcoal).
Had it been acceptable to print an underline on the line above instead of an overline, then for 14 bytes:
FS≡ιd¦¶\¦s¦_↗/
Try it online! Link is to verbose version of code. Explanation: Depending on each input letter, the program either d) moves down a line, then prints a \ s) prints a _ or u) prints a /, then moves up a line. Annoyingly I can't seem to do anything about the multiple separators required between the strings which take up over 20% of my bytes.
