# Chaos is an ASCII ladder

You know nothing The things I do for "Chaos is a ladder" is a memorable line from the television series Game of Thrones.

The purpose of this challenge is to build a ladder from chaos, in ASCII art.

# The challenge

### Input

• Ladder width, W >= 3 (integer)
• Step height, H >= 2 (integer)
• Number of rungs, N >= 2 (integer).

### Output

A ladder with horizontal rungs and vertical rails, all 1 character wide. Ladder width (W) includes the two rails, and step height (H) includes the corresponding rung.

All rungs, including the uppermost and the lowermost, will have a piece of vertical rail of length H-1 directly above and below. The example will make this clearer.

The ladder will be made of printable, non-whitespace ASCII characters, that is, the inclusive range from ! (code point 33) to ~ (code point 126).The actual characters will be chosen randomly. Given the inputs, each of the random choices of characters must have nonzero probability. Other than that, the probability distribution is arbitrary.

Leading or trailing whitespace, either horizontal or vertical, is allowed.

### Example

Given W=5, H=3, N=2, one possible output is as follows.

x   :
g   h
q$UO{ t T 6 < bUZXP 8 T 5 g  Note that total height is H*(N+1)-1, as there are N rungs and N+1 vertical sections. # Aditional rules • Input means and format are flexible as usual. For example, you can input the three numbers in any order, or an array containing them. • Output may be through STDOUT or an argument returned by a function. In this case it may be a string with newlines, a 2D character array or an array of strings. • A program or a function can be provided. • Standard loopholes are forbidden. • Shortest code in bytes wins. # Test cases For each W, H, N a possible output is shown. W=5, H=3, N=2: \ ~ : K ke:[E 5 u 0 _ 8Fr.D # r 7 X W=3, H=2, N=2:$ X
Mb)
0 ]
(T}
j 9

W=12, H=4, N=5:

d          Y
P          
5          3
p$t$Ow7~kcNX
D          x
O
*          H
LB|QX1'.[:[F
p          p
x          (
2          ^
ic%KL^z:KI"^
C          p
(          7
7          h
TSj^E!tI&TN8
|          [
<          >
=          Q
ffl^,tBHk?~
O          +
p          e
n          j

W=20, H=5, N=3:

G                  %
o                  y
%                  3
-                  7
U'F?Vml&rVch7{).fLDF
o                  }
U                  I
h                  y
a                  g
;W.58bl'.iHm\8v?bIn&
,                  U
N                  S
4                  c
5                  r
F3(R|<BP}C'$=}xK$F]^
'                  h
h                  u
x                  $6 5  • can you add the (numerical) range for the ascii characters? – Rod Sep 14, 2017 at 14:45 • @Rod Good idea. Done Sep 14, 2017 at 14:51 • What kind of lower limit on the quality of randomness is there? I assume starting at a random point and incrementing modulo (126-33) wouldn't qualify because of obvious correlation between adjacent values. Or does it have to be capable of producing every possible sequence? (So an 8-bit linear congruential generator wouldn't work, because one character uniquely determines the next character?) Sep 14, 2017 at 23:50 • @PeterCordes There is no problem with having some correlation, as long as every possible combination can occur. The approach you describe is, as you say, not valid because it introduces too strong statistical dependence between characters at different positions, making some combinations impossible Sep 15, 2017 at 0:01 • @PeterCordes Yes, I meant theoretically. Don't worry about RNG strength; you can assume the RNG is ideal. There's some meta consensus about that. I thought there was something more specific, but all I could find was this and this Sep 15, 2017 at 0:41 ## 24 Answers # Operation Flashpoint scripting language, 643 624 bytes f={l=["""","!","#","$","%","&","'","(",")","*","+",",","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","\","]","^","_","","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~"];t=_this;w=t select 0;r={l select random 92};v="";s=v;i=2;while{i<w}do{i=i+1;v=v+" "};p={i=1;while{i<t select 1}do{i=i+1;s=s+call r+v+call r+"\n"}};k=0;call p;while{k<t select 2}do{k=k+1;i=0;while{i<w}do{i=i+1;s=s+call r};s=s+"\n";call p};s}


Ridiculously long because there is no way to create the characters from the character codes.

Call with:

hint ([5, 3, 2] call f)


Output: The ladder is extra chaotic because the font is not monospaced.

Unrolled:

f =
{
l = ["""","!","#","$","%","&","'","(",")","*","+",",","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","\","]","^","_","","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~"]; t = _this; w = t select 0; r = { l select random 92 }; v = ""; s = v; i = 2; while {i < w} do { i = i + 1; v = v + " " }; p = { i = 1; while {i < t select 1} do { i = i + 1; s = s + call r + v + call r + "\n" } }; k = 0; call p; while {k < t select 2} do { k = k + 1; i = 0; while {i < w} do { i = i + 1; s = s + call r }; s = s + "\n"; call p }; s }  • It seems as "\n" works, "\xa3" to get something like £ doesn't work? if you can use unicode escapes you might be able to trim that array of yours. Sep 15, 2017 at 9:45 • How can I run this myself? :D Sep 15, 2017 at 10:22 • This just looks like a script was written and the spaces and newlines were removed. Is it only getting upvotes because of an image of a ladder or have I missed some clever golfing? Sep 15, 2017 at 10:51 • @steadybox are you using contextual screenshots now after I requested a non-grassland screenshot on that one question xD? Sep 15, 2017 at 20:15 • @Tschallacka \n is the only escape that is recognized. (And "" inside quotes to represent one ") Sep 15, 2017 at 21:58 # 05AB1E, 29 bytes Input taken in the order N, H, W >*GNUžQ¦©.RIÍFð®.R«X²Öè}®.RJ,  Try it online! Explanation >*G # for N in [1 ... (N+1)*H)-1] do: NU # store N in variable X žQ # push a string of printable ascii ¦© # remove the first (space) and save a copy in register .R # pick a random character IÍF # W-2 times do: ð # push a space ®.R # push a random ascii character « # concatenate X²Ö # push X % H == 0 è # index into the string of <space><random_char> with this } # end inner loop ®.R # push a random ascii character J, # join everything to a string and print  • This is a bit cheating. Sep 14, 2017 at 23:43 • @user544262772 why so? Sep 15, 2017 at 5:08 • may you explain this a little bit? Sep 15, 2017 at 8:35 • @MischaBehrend: Sure, I've added an explanation now. Sep 15, 2017 at 8:41 • @user544262772 it can be quite challenging to make a well golfed answer in a golfing language, believe me, they are designed for golfing, but using them usually requires some thought (unless it's just "here's a built-in that does exactly what you want"). Sep 15, 2017 at 10:48 # C, 95 bytes f(w,h,n,i){++w;for(i=0;i++<w*~(h*~n);)putchar(i%w?~-i%w%(w-2)*((i/w+1)%h)?32:33+rand()%94:10);}  • 93 bytes Oct 30, 2020 at 1:33 # R, 13812911198 93 bytes -13 bytes thanks to Neal Fultz! -1 byte thanks to Robin Ryder function(W,H,N){m=matrix(intToUtf8(32+sample(94,W*(h=H*N+H-1),T),T),h) m[-H*1:N,3:W-1]=" " m}  Try it online! Anonymous function; returns the result as a matrix. Thanks to that Word Grids question, I've been thinking about matrices a lot more than usual. I observed that the rungs are in those matrix rows that are a multiple of the step height H (R is 1-indexed), and that the rails are the first and last columns, 1 and W. So I create a matrix of random ASCII characters, and replace those letters that didn't match those criteria with spaces, and return the matrix. TIO link prints it out nicely. Neal Fultz suggested a different indexing for the space characters, [-H*(1:N),3:W-1], which replaces all characters except for those in rows of multiples of H: -H*(1:N) and not on the edge, 3:W-1 <==> 2:(W-1). # R, 121 bytes function(W,H,N)for(i in 1:(H*N+H-1)){for(j in 1:W)cat("if"(!(i%%H&j-1&j-W),sample(intToUtf8(33:126,T),1)," ")) cat("\n")}  Try it online! An improvement over the original matrix-based approach I started with; it's the same algorithm but for loops are shorter than constructing and printing a matrix (but not if I don't print it!) • m[-H*(1:N),3:W-1]=" " seems a little shorter - you can always replace testing row and col with a 2-d slice. Sep 19, 2017 at 4:59 • @NealFultz wow, that's quite excellent! Thank you! Sep 19, 2017 at 13:13 • -1 byte by replacing sample(33:126,...) with 32+sample(94,...). May 18, 2019 at 17:15 # Jelly, 24 23 22 bytes «þỊoU$ẋ⁵‘¤Ḋ×94X€€+32ỌY


A full program taking the three arguments W, H, N and printing the result.

Try it online!

### How?

Builds a 2d array mask of a single rung and its vertical sections below, repeats it N+1times and removes the top rung then places random characters or spaces depending upon the mask value.

«þỊoU$ẋ⁵‘¤Ḋ×94X€€+32ỌY - Main link: W, H (N is third input / 5th command line argument) þ - outer product (build a table using): « - minimum - ...note: þ implicitly builds ranges of W and H prior Ị - insignificant? (abs(z)<=1) - yields a W by H 2-d array, - all zeros except the left and top edges which are 1s$                 - last two links as a monad:
U                  -   upend (reverse each row)
o                   -   or (vectorises) - giving us our |‾| shape of 1s
⁵               -   5th command line argument, N
‘              -   increment -> N+1
ẋ                - repeat list - giving us our ladder-mask plus a top rung)
Ḋ            - dequeue - remove the top rung
94         - literal ninety-four
×           - multiply (vectorises) - replace the 1s with 94s
X€€      - random for €ach for €ach - 0 -> 0; 94 -> random integer in [1,94]
32   - literal thirty-two
+     - add (vectorises) - 0 -> 32; random integers now from [33,126]
Ọ  - character from ordinal (vectorises)
Y - join with newlines
- implicit print


## Perl 5, 81 bytes

80 bytes code + 1 for -p.

/ \d+ /;$_=(($}=(_.$"x($-2)._.$/)x($&-1))._ x$.$/)x$'.$};s/_/chr 33+rand 94/ge


Try it online!

• the space after \d+ may be removed because of greedy match Sep 15, 2017 at 9:29

# Charcoal, 34 32 bytes

Ｅ…¹×⁺¹ＮＮ⪫ＥＩζ⎇∧﹪ιＩη﹪λ⁻Ｉζ¹ §⮌γ‽⁹⁴ω


Try it online! Takes input in the order N, H, W. Verbose approximation (Plus(InputNumber(), 1) is currently broken on TIO). Explanation:

Ｅ…¹×⁺¹ＮＮ


Map over the range 1..H*(N+1). This means that the rungs appear when i is a multiple of H.

⪫


Join the result of:

ＥＩζ


mapping over the implicit range 0..W:

⎇∧﹪ιＩη﹪λ⁻Ｉζ¹


if the column is not 0 or W-1 and the row is not a multiple of H then output a space;

§⮌γ‽⁹⁴


otherwise, take the predefined ASCII character variable, reverse it (putting the space in 94th place), and print a random character from what is now the first 94. (Because Slice sucks.)

ω


• Not sure if it's helpful but you could draw the ladder and then peekall and map over random printable I think? EDIT It appears to be broken. Oops. Sep 15, 2017 at 13:29
• I'll try to fix it (pretty sure it was working before) but I've been a bit busy so it might take a while Sep 15, 2017 at 13:37
• @ASCII-only I assume you're thinking of ＮθＧＨ↓θ→Ｎ↑θ*‖Ｏ↓Ｆ⁻Ｎ¹Ｃ⁰θ¿ＥＫＡ§⮌γ‽⁹⁴«? Well, I bisected and commit a0a6316 broke it.
– Neil
Sep 20, 2017 at 19:54
• @ASCII-only Actually that's not quite true, there was an unrelated bug in Map where it used not is_command when it meant is_command. So you were supposed to write ＮθＧＨ↓θ→Ｎ↑θ*‖Ｏ↓Ｆ⁻Ｎ¹Ｃ⁰θＵＭＫＡ§⮌γ‽⁹⁴ if it hadn't been for that bug.
– Neil
Sep 20, 2017 at 20:01

# C (gcc), 141131114109 107 bytes

Should be able to golf this down quite a bit...

i,j,c;f(w,h,n){for(i=1;i<h*n+h;i+=j==w)printf(i%h?i++,j=0,"%c%*c\n":"%c",++j^w?c^8:10,w-2,c=33+rand()%94);}


Try it online!

• can we edit i=1 in the global declaration? Sep 16, 2017 at 6:06
• 106 bytes Oct 30, 2020 at 1:37

# Perl 6, 76 73 bytes

->\h,\n,\w{map {chrs (roll(w,1..94)Z*1,|($_%%h xx w-2),1)X+32},1..^h*n+h}  Try it online! Takes (h, n, w) as arguments. Returns a list of strings. Explanation: -> \h, \n, \w { # Block taking arguments h, n, w map { # String from codepoints chrs # Generate w random numbers between 1 and 94 (roll(w, 1..94) # Set inner numbers on non-rungs to zero Z* 1, |($_%%h xx w-2), 1)
X+ 32
}
# Map h*n+h-1 row numbers (1-indexed)
1..^h*n+h
}

• Alternative 73 byter using xx and ++$ instead of map. Maybe you can find a place to golf off any bytes? – Jo King Nov 5, 2018 at 11:27 # PowerShell, 132 124 bytes param($w,$h,$n)-join([char[]]((($a=('#'+' '*($w-2)+"#n")*--$h)+'#'*$w+"n")*$n+$a)|%{($_,[char](33..126|Random))[$_-eq35]})


Try it online!

We construct a ladder composed of only # first (example), then loop |%{...} through each character and if it's -equal to 35, we pull out a new Random character from the appropriate range. Otherwise we output (i.e., either a space or newline).

# JavaScript (ES6), 117 115 bytes

A recursive function building the output character by character.

"Look Ma, no literal line-feed!"

(w,h,n)=>(g=x=>y<h*n+h-1?String.fromCharCode(x++<w?x%w>1&&-~y%h?32:Math.random()*94+33|0:10)+g(x>w?!++y:x):'')(y=0)


### Demo

let f =

(w,h,n)=>(g=x=>y<h*n+h-1?String.fromCharCode(x++<w?x%w>1&&-~y%h?32:Math.random()*94+33|0:10)+g(x>w?!++y:x):'')(y=0)

O.innerText = f(5,3,4)
<pre id=O></pre>

• Damnit! I was in the process of golfing mine down when I saw this. :\ You win again! :p Sep 15, 2017 at 13:41
• I finished golfing mine down (for now) - I drew the line at moving all the ternaries within String.fromCharCode, as I couldn't honestly say I'd come up with that myself after seeing this. Let me know if you feel mine is now too similar to yours. Sep 15, 2017 at 15:32
• @Shaggy No worries! (Actually, I saw your answer only after posting mine. If I had seen it earlier, I would probably have given up.) Sep 15, 2017 at 15:42
• Ah, it's the nature of the game! :) Combining our solutions gives 113 bytes, by the way Sep 15, 2017 at 16:45

# Python 2, 142 bytes

lambda w,h,n,e=lambda:chr(randint(33,126)):[e()+[eval(("e()+"*(w-2))[:-1])," "*(w-2)][-~i%h>0]+e()for i in range(h*-~n-1)]
from random import*


Try it online!

Saved bytes thanks to ovs!

• @LuisMendo I think I fixed it now. Sep 14, 2017 at 15:16
• @LuisMendo It's my fault for not checking carefully. Sep 14, 2017 at 15:18
• 42 bytes
– ovs
Sep 17, 2017 at 21:28
• @ovs Thanks! You forgot a 1 in the front though >_> Sep 17, 2017 at 21:29

# Pyth, 33 bytes

VhEjtW!Nmsm?&d}kr1tQ\ Or\!C127Qvz


Try it online: Demonstration

# Python 2, 114 bytes

lambda w,h,n:[[chr(32+randint(1,94)*(x%~-w*(y%h)<1))for x in range(w)]for y in range(1,h*-~n)]
from random import*


Try it online!

# SOGL V0.12, 32 31 bytes

 ~ΔkψR
I{e{R}¶bH{Re⁾⌡@R¶}}¹∑e⌡k


Try it Here!

Input in the order N, W, H.

Explanation:

 ~ΔkψR

R  a function named "R", pushes a random character:
~       push "~"
Δ      get the charasters from " " to "~"
k     remove the 1st character
ψ    choose a random character from that

I{e{R}¶bH{Re⁾⌡@R¶}}¹∑e⌡k  main program

I                         increase the 1st input - N
{                }       N times do
e                         push the variable e, which is here initialised to the next input - W
{ }                      E times do
R                         execute R
¶                     push a newline
bH                   push b-1, where b is initialised to the next input - H
{       }          B-1 times do
R                   execute R
e⁾                 push e-2 aka width-2
⌡@               push that many spaces
R              execute R
¶             push a newline
¹∑     join the stack together
e⌡k  remove the first width characters


18 bytes without the random characters :/

# Java 8, 203188168133132130128 126 bytes

W->H->N->{for(double i=0,j,q;++i<H*N+H;)for(j=W,q=10;j-->=0;q=i%H*j<1|j>W-2?33+Math.random()*94:32)System.out.print((char)q);}


Try it online!

• 133 bytes: W->H->N->{for(int i=0,j;i++<H*N+H-1;){char c=10;for(j=W;j-->0;c=i%H<1|j>W-2|j<2?(char)(Math.random()*94+33):32)System.out.print(c);}} Sep 14, 2017 at 15:58
• You are currently printing the result for W-1, the inner loop has to do an additional iteration (>=0 +1 byte). Sep 14, 2017 at 16:09
• 132 bytes: W->H->N->{for(int i=0,j;i++<H*N+H-1;){char c=10;for(j=W;j-->=0;c=i%H*j<1|j>W-2?(char)(Math.random()*94+33):32)System.out.print(c);}} Sep 14, 2017 at 16:10
• ;++i<H*N+H; : -2 bytes. Sep 15, 2017 at 9:24

import System.Random
a=mapM id
b=(putStr.unlines=<<).a
c=randomRIO('!','~')
r w=a$c<$[1..w]
s w=a$c:(return ' '<$[3..w])++[c]
(w#h)0=b$s w<$[2..h]
(w#h)n=do{b$(s w<$[2..h])++[r w];(w#h)$n-1}  Try it online! Saved 9 bytes thanks to Laikoni Saved 21 bytes thanks to wchargin Should be golfable (b$(s w)<$[2..h] and b$((s w)<$[2..h])++[r w]). I don't feel comfortable with IO and golfing. • You can use infix notation for t w h n= ...: (w#h)n= .... pure can be used instead of return. You can drop the parentheses around (d ' ')<$and (s w)<. Sep 14, 2017 at 20:36 • @Laikoni I won't forget the infix notation next time! Thanks. Sep 15, 2017 at 15:43 • Save a bunch of bytes with c=randomRIO('!','~'), which also lets you inline d=return. Also, mapM id is one byte shorter than sequence. Sep 17, 2017 at 20:35 • @wchargin Thanks. I learned something today! Sep 18, 2017 at 16:05 # JavaScript (ES6), 144 bytes (w,h,n)=>Array(n+1).fill(("#".padEnd(w-1)+# ).repeat(h-1)).join("#".repeat(w)+ ).replace(/#/g,_=>String.fromCharCode(33+Math.random()*94|0))  Creates the ladder out of # characters then replaces each one with a random ASCII char. ## Test Snippet let f= (w,h,n)=>Array(n+1).fill(("#".padEnd(w-1)+# ).repeat(h-1)).join("#".repeat(w)+ ).replace(/#/g,_=>String.fromCharCode(33+Math.random()*94|0)) ;(W.oninput=H.oninput=N.oninput=B.onclick=_=>{let vals=[W.value,H.value,N.value].map(eval);D.innerText=vals.join, ;O.innerText=f(...vals);})() W: <input id=W type=range min=3 max=25 value=12><br> H: <input id=H type=range min=2 max=25 value=5><br> N: <input id=N type=range min=2 max=10 value=2><br> <button id=B>Rerun</button> <code id=D></code> <pre id=O></pre> • String.fromCharCode and Math.random in one solution - why does JavaScript hate us?! Came up with this for 137 bytes, which is very similar to yours, just without the array. I wonder now if a recursive solution might be shorter still; will investigate later. Sep 15, 2017 at 10:12 • Knocked another byte off that Sep 15, 2017 at 16:49 # JavaScript (ES6), 129 117 bytes Unfortunately, while I was in the process of golfing this down, Arnauld beat me to a similar but shorter solution. By combining our 2 solutions, this can be 113 bytes Includes a trailing newline. (w,h,n)=>(g=c=>l?(c++<w?c%w>1&&l%h? :String.fromCharCode(94*Math.random()+33|0): )+g(c>w?!--l:c):)(0,l=h*++n-1)  ## Try it o.innerText=(f= (w,h,n)=>(g=c=>l?(c++<w?c%w>1&&l%h? :String.fromCharCode(94*Math.random()+33|0): )+g(c>w?!--l:c):)(0,l=h*++n-1) )(i.value=5,j.value=3,k.value=2);oninput=_=>o.innerText=f(+i.value,+j.value,+k.value) label,input{font-family:sans-serif;font-size:14px;height:20px;line-height:20px;vertical-align:middle}input{margin:0 5px 0 0;padding:0 0 0 5px;width:100px;} <label for=i>W: </label><input id=i min=3 type=number><label for=j>H: </label><input id=j min=2 type=number><label for=k>N: </label><input id=k min=2 type=number><pre id=o> # Japt-R, 424140373428 25 bytes Takes input in the order H,W,N. ;*°WÉ ÆJ²ùVXgJùU¹r,@EÅöÃé  Try it # QBIC, 76 bytes [:*:+b-1|G=chr(_r33,126|)~a%b\[:-2|G=G+@ ]][e|G=G+chr$(_r33,126|)]?_sG,1,e  ## Explanation [ FOR a = 1 TO : input 1 (Height, now in var b) * times : input 2 (# of rungs, now in var c) +b-1| plus one bottom rung without crossbar G=chr$(_r33,126|)       Assign to G a random char (_r is the RAND() function, chr$() is BASIC's num-to-char) ~a%b| IF we are not at a crossbar (the modulo returns anything but 0), [:-2|G=G+@  add to G x spaces, where x is width (input 3, now 'e') - 2 Note that G is now either 'X' or 'X ' (for rnd char X and W=5) ]] Close the spacing FOR, close the IF [e| FOR f = 1 to width G=G+chr$(_r33,126|)]     Append to G a rnd char
G is now either 'XXXXXX'  or 'X   XXXXX' (for rnd char X and W=5)
?_sG,1,e                PRINT the first w characters of G, and on to the next line


## Sample run

Command line: 3 2 5
N   F
M
Bj#=y
!   (
S   N
q(.Ho
%   7
g   ,


# MATL, 63 50 bytes

-13 bytes thanks to Luis Mendo

Q*qXJ*6Y2HY)wT3$ZrJ3G3$eJ3G&Ol5LZ(J:HG\~3GTX"!+g*c


Try it online!

I'm still new to golfing in MATL (and I'm not super good at MATLAB for that matter), so I know this probably isn't close to optimal. Tips are welcome. Takes input in order N,H,W.

Here we go:

Q*qXJ                     # compute H*(N+1)-1, store as J
*                    # multiply by W
6Y2HY)              # push printable ASCII
wT3$Zr # sample uniformly with replacement J3G3$e  # reshape to make a matrix of the appropriate shape.


We now have a matrix of random char.

J3G                       # push J,W
&O                     # zero matrix, J x W
l5LZ(                # assign 1 to first and last columns


Now there's also a logical matrix for the rails.

J:                        # push J, range, so 1...J
HG                      # take second input (H)
\~                    # mod and bool negate (so it's 1 for rows of multiples of H)
3GTX"!              # repmat and transpose so we have 1's for rungs


Now we have 3 matrices on the stack:

• Top: 0 for non-rung, 1 otherwise
• Middle: 0 for non-rail, 1 otherwise
• Bottom: random characters, -20

So we do the following:

+                         # add the top two matrices.
g                        # convert to logical so 0->0, nonzero->1
*                      # elementwise multiply
c                     # convert to char, implicit output (0 -> space).

• Here are a few quick tips: X" is 3$ by default. 6Y2 may be handy instead of 13:106...20+. ~~ is g. J3G&Ol5LZ( can be used instead of 1F3G2-Y"h1hJT3$X" Sep 18, 2017 at 21:36
• @LuisMendo Ah, I didn't quite make it through all the docs or I would have discovered that about X". In that last tip, 5L is [1 0] but I'm not sure how that's used in conjunction with Z( -- I get that it's assigning 1 to the first and last columns but I don't get how 5LZ( accomplishes that. I'll probably ping you in MATL CHATL sometime later about this so don't worry about that for now. Sep 18, 2017 at 22:37
• Indexing is modular, so 0 is the same as "end". Z( assigns to columns. Of course, feel free to ping me in chat! Sep 18, 2017 at 23:03

# Powershell, 102 bytes

param($w,$h,$n)1..(++$n*$h-1)|%{$l=$_%$h
-join(1..$w|%{[char](32,(33..126|Random))[!$l-or$_-in1,$w]})}


Less golfed test script:

$f = { param($w,$h,$n)
1..(++$n*$h-1)|%{       # for each lines of the ladder
$l=$_%$h # line number in a step -join(1..$w|%{      # make a line
[char](32,(33..126|Random))[!$l-or$_-in1,$w] }) # a random character if the line number in a step is a rung line or char position is 1 or width # otherwise a space } } &$f 5 3 2
&$f 3 2 2 &$f 12 4 5
&$f 20 5 3  Output: 0 { H S ']UxR G ] 3 t q^R8O q y t J U h YQZ _ i 3#D I # = m & < ] 6 8nmuyw2'Y7%+ o l ; ! D M Fn[zGfT";RYt @ B$          e
z          @
@J[1|:-IS~y<
(          L
:          [
|          q
zBow0T0FnY8)
/          *
e          B
R          p
9{d2(RacBdRj
u          ~
          l
J          h
v                  t
T                  -
v                  H
'                  Y
IS7{bx2&k@u7]o}>[Vq?
F                  U
?                  U
|                  Q
}                  T
:wv1wEfc6cS;430sigF|
<                  L
:                  }
*
H                  =
L8k5Q/DQ=0XIUujK|c6|
j                  =
!                  p
V                  :
#                  w


# Ruby, 71 bytes

EDIT: Oops I thought this was a new challenge because of the recent edit to fix a typo lol. I'm still leaving this up though because there's no Ruby answer for it yet.

->w,h,n{(1..h*-~n-1).map{|i|[*?!..?~].sample(x=i%h>0?2:w)*(' '*(w-x))}}


Try it online!