# Make me a shape

The game shapez.io has a huge variety of shapes you can produce, such as:

Each shape has a unique short code, for example the above is CrWbScRu.

This means, going clockwise from top right, red circle (Cr), blue wedge (Wb), cyan star (Sc), uncolored rectangle (Ru).

There are four different shapes:

• Rectangle - R
• Wedge - W
• Star - S
• Circle - C

And eight different colours:

• uncolored - u
• red - r
• green - g
• blue - b
• yellow - y
• purple - p
• cyan - c
• white - w

A quadrant is made out of one of each of these - (shape)(color). A quadrant can also be empty with --. This means there are 33 possible quadrants.

Four quadrants concatenated together make a layer, of which there are $$\33^4 - 1 = 1185920\$$ possibilities. You can't have an empty layer (--------). These layers can be stacked on top of one another by joining with : - the code CyCyCyCy:SrSrSrSr looks like:

Shapes can be stacked up to 4. This means there are $$\\left(33^{4}-1\right)+\left(33^{4}-1\right)^{2}+\left(33^{4}-1\right)^{3}+\left(33^{4}-1\right)^{4} = \$$ 1.97 septillion possible shapes total.

Your challenge is to randomly generate one of these.

Your generation does not have to be uniformly random, as long as every possible shape has a nonzero chance of being chosen.

## Specs

• Each quadrant is one of SWCR followed by one of ugbrycpw, or --.
• A layer is four quadrants concatenated together.
• A shape is 1-4 layers joined by :
• You should generate a not-necessarily-uniformly-random shape.

You can view shapes at https://viewer.shapez.io/.

# Scoring

This is , shortest wins!

• This should definitely be made into a graphical-output challenge. Aug 25, 2021 at 15:05
• Would forcing the first quadrant to be non-empty (with the remaining 3 being randomly empty or not) fall within the scope of not being uniformly random? Aug 25, 2021 at 18:57
• @xigoi That's my next idea... that would be taking a shortcode and printing it though. Aug 25, 2021 at 20:23
• @Shaggy No, because then you can't generate shapes with empty first quadrants. Aug 25, 2021 at 20:24
• @emanresuA Right, that's what I meant. Aug 25, 2021 at 21:22

# 05AB1E, 354038 36 bytes

+5 to prevent empty output which would've occured with probability $$\33^{-16}\$$
-2 bytes thanks to Kevin Cruijssen!

Quite slow, generates $$\33^4\$$ possible layers 4 times and chooses a random one each time. Layers of all - are removed.

[₄ε‘¥„W‘’»Õpcw’â„--ª4ãΩJ}'-8×K':ýDĀ#


Try it online!

At the cost of two bytes this can actually run fast: Try it online!

Because every quadrant in each layer has a $$\1\$$ in $$\33\$$ chance of being empty the resulting shapes are quite noisy:

[    ...    Dg#              # until the the output is not empty, do the following:

₄                            # push constant 1000
ε                     }    # for each digit in this number:
‘¥„W‘                     #   dictionary compressed string "SRCW"
’»Õpcw’              #   dictionary compressed string "rugbypcw"
â             #   cartesian product of the two strings
„--ª         #   append "--" to this list
4ã       #   4th cartesian power, all 4-element combinations from the length-2 strings
Ω      #   choose a random one
J     #   join into a single string

'-8×                         # string of 8 -'s
K                        # remove all occurences from the list
':ý                     # join the list by ":"

• Damnit! Beaten by better string compression! :( Aug 25, 2021 at 14:23
• 4L to ₄ for -1. Also, the challenge states it can't have an empty layer, not that the entire output can't be empty. So '-8×K':ýDgĀ# is incorrect, since it only checks if the entire shape is empty. You can fix this with ':ýD'-8×å≠#, which is also 1 byte shorter. Sep 2, 2021 at 7:08
• @KevinCruijssen Thanks for the -1! But for the second part of your comment the challenge states A shape is 1-4 layers [...], and since layers can't be empty this implies the output is not empty. '-8×K removes empty layers, no? With your suggestion the output always has 4 layers.
– ovs
Sep 2, 2021 at 7:45
• Ah, I misread the challenge regarding the second part. You're indeed correct that it has 1-4 layers, and empty layers should be removed, so your approach was correct. I think you can remove the g for another -1, though? Ā will be truthy for a non-empty string and falsey for "". Sep 2, 2021 at 7:59
• @KevinCruijssen Yes that works, thanks again
– ovs
Sep 2, 2021 at 8:22

# Jelly, 3835 34 bytes

“SWCR”p“¬,ĖṆḷ⁸»Żṗ4XʋⱮ4ẸƇƊṆ¿o⁾--j”:


Try it online!

This just about finishes on TIO most of the time (it has a $$\1\$$ in $$\33^{16}\$$ chance of taking longer), taking around 55 seconds. Full program, as we require Jelly's smash-printing to get the correct output

-4 bytes thanks to Jonathan Allan!

The first sample output I got was:

# How it works

“SWCR”p“¬,ĖṆḷ⁸»Żṗ4XʋⱮ4ẸƇƊṆ¿o⁾--j”: - Main link. No arguments. Left argument = 0
Ɗ          - Group the previous 3 links into a monad f(_): (ignores the argument)
“SWCR”                             -   Set the return value to “SWCR”
ʋ               -   Group the previous 4 links into a dyad g(“SWCR”, i): (ignores i)
“¬,ĖṆḷ⁸»                    -     Compressed string: “rugbypcw”
p                            -     Cartesian product with “SWCR”
Ż                   -     Prepend a zero (represents “--”)
ṗ4                 -     4th Cartesian power
X                -     Choose a random list
Ɱ4             -   Yield [g(“SWCR”,1), g(“SWCR”,2), g(“SWCR”,3), g(“SWCR”,4)]
ẸƇ           -   Remove any that are just zeros
¿        - While:
Ṇ         -   The left argument is falsey ([] or 0)
Ɗ          -   Set the left argument to f(_)
o⁾--    - Replace all zeros with “--”
j”: - Join by ”: and smash-print

• @JonathanAllan Corrected Aug 25, 2021 at 18:29
• Good job golfing :) Aug 25, 2021 at 18:59
• @JonathanAllan I still think I can beat 05AB1E somehow :) Aug 25, 2021 at 19:00
• @JonathanAllan I couldn't beat 35 with any patches of a single link, so I've gone with your 35 byte one. Thanks for the golfs! Aug 25, 2021 at 19:40
• Aha, I saw the 4µ and the length of “SWCR”, and thought to myself, "it must be possible to make a save using that somehow!" 34 bytes :D ...EDIT: although now the 4 can go back in if we want TIO Aug 25, 2021 at 20:27

# Python 3, 129 bytes

from random import*
print(':'.join(''.join(sample(q,4))for q in[[i+j for i in'RWSC'for j in'urgbypcw']*4+['--']*3]*randint(1,4)))


Try it online!

For each layer, 4 items are picked from a distribution containing 3 empty quadrants and the other shape-colour combinations 4 times.

# Japt, 4039 38 bytes

This version has a very high probability of having all 4 layers, using a similar approach as Level's Ruby solution of generating all 4 layers, removing the ones consisting entirely of empty quadrants and making a recursive call if there are none left.

4Æ"RWCS"ïÕ½wbgp p-² ö4 qÃfÈr-Ãq': ªß


Test it

## Original, 39 bytes

Ò4ö)ÆÈr-}a@"RWCS"ïÕ½wbgp p-² ö4 q
q':


Test it

Ò4ö)ÆÈr-}a@"RWCS"ïÕ½wbgp p-² ö4 q\nq':
Ò                                            :Negate the bitwise not of (i.e., increment)
4ö                                          :  Random int in the range [0,4)
)                                         :End bitwise op
Æ                                        :Map the range [0,Ò4ö)
È                                       :  Left function taking a string as argument
r-                                     :    Remove all "-"s
}                                    :  End function
a                                   :  Return the first result of the right function that returns truthy (non-empty string) when passed through the left
@                                  :  Right function
"RWCS"ï                           :    Cartesian product of "RWCS" and
Õ½wbgp                   :      Compressed string "ucrywbgp"
p                 :    Push
-²               :      "-" duplicated
ö4            :    Choose 4 random elements
q          :    Join
q':     :Join with ":"

• How does this prevent generating empty layers?
– ovs
Aug 25, 2021 at 10:46
• @ovs, the right function generates the layer, which could possibly be entirely empty, and it is then run through the left function which removes all -s in it. If that produces an empty string then the process is repeated until a valid layer is generated. Aug 25, 2021 at 10:53

# C#[9.0] - 191, 235, 228, 204, 203 Bytes (Condensed)

static Random r=new();static string g(){var z="";for(int j,i,l=r.Next(1,4);l-->0;z+=":")for(i=j=0;i++<4;)z+=r.Next()%8<1&&j++<3?"--":$"{"CRWS"[r.Next(0,3)]}{"rgbypcuw"[r.Next(0,7)]}";return z.Trim(':');}  • +44 bytes due to a missed requirement. • -5 bytes by reducing GetShape to g per @emanresu A's recommendation. • -24 bytes by using var, consolidating declarations and using constant strings per @Johan du Toit. • -1 byte by swapping ==0 for <1 per @ceilingcat. #### How it Works // Create a new random number generator using C# 9.0's new invocation. static Random r = new(); static string g() { // Create a variable to store the possible shapes. string s = "CRWS", // Create a variable to store the possible colors. c = "rgbypcuw", // Create a variable to store the result. z = ""; // Create an iteration variable to iteratively create between 1 and 4 layers randomly. // Evaluate for stop then decrement. // Append : to the result. for (int l = r.Next(1, 4); l-- > 0; z+=":") // Create an iteration variable to iteratively create the four quadrants. // Create a variable for tracking the number of empty quadrants. for (int i = 0, j = 0; i++ < 4;) // If we haven't reached the maximum number of allowed empty quadrants, a new one can still be added. // Use a random number to determine if we should create an empty quadrant. // Since && short circuits, the increment of J doesn't occur unless we're adding an empty quadrant. if (r.Next() % 8 == 0 && j++ < 3) // Add an empty quadrant. z += "--"; // Otherwise, add a shape and color from the variables above, // using a random index within the bounds of the variables lengths. else z +=$"{s[r.Next(0, 3)]}{c[r.Next(0, 7)]}";

// Return the result and remove any trailing :.
return z.Trim(':');
}


#### Sample Output

I called the method in a for loop over 10 iterations to sample the output for this post:

--CyCpCb:RpCu--Rc
RpCyWbWc:RcWuWyWc
CyWy--Cr:RrWpWuWg
Wp--WuRc
RpCcCgWp
CbWuWgCu:--RcRbWg:CcCuCpCc
RgRpCyCu:CbWyWuCu
CgCuRpCu
CyRc--Ru:WuRuRbRu:--CgRbWr
WpCgCpRu


#### Resulting Image Sample

I ran the shape code CbWuWgCu:--RcRbWg:CcCuCpCc through and got this result:

• Btw, you can probably replace getShape with g or something. Aug 25, 2021 at 20:46
• I like the Random r=new(); bit, haven't seen that before!
– jdt
Aug 25, 2021 at 22:25

# Charcoal, 51 bytes

⪫ＥＥ⊕‽⁴⊕‽⊖Ｘ³³¦⁴⭆Ｅ⁴÷ιＸ³³λ⎇﹪λ³³⁺§RWSCλ§urgbypcw÷λ⁴--¦:


Try it online! Link is to verbose version of code. Explanation:

    ‽⁴                                              Random integer 0-3
⊕                                                Incremented
Ｅ                                                 Map over implicit range
Ｘ³³¦⁴                                      33⁴
⊖                                           Decremented
‽                                            Random integer
⊕                                             Incremented
Ｅ                                                  Map over random integers
÷ι                                 Current random integer
÷                                  Integer divide by
³³                              Literal integer 33
Ｘ                                Raised to power
⭆                                     Map over values and join
λ                          Current value
﹪ ³³                        Modulo literal 33
⎇                            If nonzero then
§RWSCλ                 Cyclically indexed colour
⁺                       Concatenated with
§urgbypcw        Shape cyclically indexed by
λ      Current value
÷       Integer divide by
⁴     Literal integer 4
--   Else literal string --
⪫                                                 : Join with literal :
Implicitly print


# VyxalrR, 37 bytes

\:4℅ƛ«3⟩∑ḣ«SWRCẊ‛--J33²²‹℅τ∑₅8ε-;j


Try it Online!

This uses a quite neat custom-base-decompression-based approach to ensure there'll never be an empty layer. It basically generates a random number between 1 and $$\33^4 - 1\$$, decompresses it with the key as all possible quadrants (with -- first, so 0 can never occur, so empty layers won't either).

  4℅ƛ                              ;  # array of length (random 1-4) filled with...
33²²‹℅         # Random integer between 1 and 33^4-1 inclusive
τ        # Decompressed by...
Ẋ                   # Cartesian product of...
«3⟩∑ḣ«                          # Compressed string bcgpruwy
SWRC                    # String literal SWRC
‛--J               # Prepend a --
∑       # Concatenate all this
₅8ε-   # Pad with - from the start
j # Join the whole thing by...
\:                                    # Semicolons.


# Ruby, 117 114 bytes

->{s=((0..19).map{|i|i%5<1??::"-SWCR"[(r=rand 40)/8]+"rgbpucyw-"[r<8?8:r%8]}*'').gsub ?:+?-*8,''
s>''?s[1,99]:f[]}


Try it online!

A function returning a string.

Commented code (original version)

->{s=
((0..19).map{|i|i%5<1?     #Iterate 20 times to make an array. if i%5==0
?::                        #put a colon, else...
"-SWCR"[(r=rand 40)/8]+   #Select a shape
(r<8??-:"rgbpucyw"[r%8])  #If selected shape is - then put another -, else put a colour
}*'').                    #Convert the 20-element array into a string
gsub(?:+?-*8,'')          #Delete all instances of :--------
s<?!?f[]:s[1,99]}         #If s is empty, recursively call f[] to try again. Else output s (minus the initial colon)

• Won't this always generate 4 layers instead of a random number? Aug 25, 2021 at 23:25
• @Shaggy it can generate empty layers (with a one in 5**4=625 probability, which is quite high) which are then deleted. What remains is anything between 0 and 4 layers. In the case of 0 layers (very rare) it calls itself again recursively to avoid ouputting nothing. Aug 25, 2021 at 23:29
• Ah. I'd read it as the replace & repeat being done for each layer, rather than the final image. Interesting approach; I wonder if I can save anything on my Japt solution by doing something similar? Aug 26, 2021 at 11:26

# C (gcc), 137 bytes

#define r rand()
m;b;q(){m=r;m&30||q();}f(){for(q(b=5+r%4*5);--b;)printf(b%5?m&1<<b%5?"%c%c":"--":":"+!q(),"WRSC"[r%4],"rugbywcp"[r%8]);}


Try it online!

-5 bytes thanks ceilingcat!

## Perl 5, 95 138 bytes

{@q=(qw(: --),map{$s=$_;map$s.$_,ugbrycpw=~/./g}C,R,W,S);@i=map$_%5?$==1+rand
33:0,1..rand 19;@i%5==4&&" @i "!~/ (1 ){4}/||redo;say@q[@i]}


Try it online!

• How does this avoid the possibility of empty layers? Aug 25, 2021 at 15:46
• @Shaggy I fix it. Aug 27, 2021 at 9:07

# Java (JDK), 263 253 bytes

-10 thanks to ceilingcat! Forgot that | exists and that ternary ifs can be written without parentheses. D'oh!

String f(int i){var r=new Random();var q=""+"SWCR-".charAt(r.nextInt(5));q+=q.equals("-")?'-':"ugbrycpw".charAt(r.nextInt(8));return i<1?(f(2)+f(2)+f(2)+f(3)).substring(1):i<9?i>2|r.nextInt(2)<1?(":"+f(9)+f(9)+f(9)+f(9)).replace(":--------",f(1)):"":q;}


Try it online!

Took forever to find something promising, ow my head... This won't win any prices, but I liked the approach so here it is anyways. It uses the same function recursively with a different parameter to build different parts of the string.
I hope I find the template for Java lambdas again, I keep forgetting how the boilerplate needs to look for them to work :(

## EDIT:

Function lamdas aren't shorter in this case, since you don't call but apply() them, which is WAY longer. Still, thanks for the template Johan du Troit!

### Explained:

// function is called with 0 to start
String f(int i) {
// use var because it's shorter
var r=new Random();

// decide on a type...
var q=""+"SWCR-".charAt(r.nextInt(5));
// ...and append the color. If the type is "-" then append a "-" instead
q+=q.equals("-")?'-':"ugbrycpw".charAt(r.nextInt(8));

// decide what to return
return
// shape case: build full shape
i<1?
// a shape consists of four layers
// each layer starts with a :, cut the first one off
(f(2)+f(2)+f(2)+f(3)).substring(1)

// not the starting case, check for layer case
:i<9?
// build one layer. It either doesn't exist or contains four shapes
// note: 3 overrides the random decision and always returns a layer
i>2|r.nextInt(2)<1?
// replace an empty layer with a newly generated one...
(":"+f(9)+f(9)+f(9)+f(9)).replace(":--------",f(1))
// ...or return a nonexistant layer
:""
// not the shape case nor the layer case, return the quadrant calculated above
:q;
}

• Here is an example of a lambda function with recursion TIO
– jdt
Aug 26, 2021 at 14:48

# APL(Dyalog Unicode), 64 bytes SBCS

A full program that prints the shape.

⊃{∊⍺':'⍵}/{∊((⊂'--'),,'RWCS'∘.,'urgbypcw')[?4⍴33]}⍣{∨/'-'≠⍺}¨⍳?4


Try it on APLgolf!

?4 random integer between 1 and 4
 ... ¨⍳ call the function on the left this many times and collect the results in a list:

{ ... }⍣{∨/'-'≠⍺} until any char in the result is not -:
'RWCS'∘.,'urgbypcw' Cartesian product of the two strings
, flatten into a list of 2-char strings
(⊂'--'), prepend the empty quadrant
( ... )[?4⍴33] get elements at 4 random indices between 1 and 33.
∊ flatten into a string

{∊⍺':'⍵}/ join with :
⊃ get the first element from the nested output

# JavaScript, 125120119115112 118 bytes

To avoid the possibility of an empty layer, we force the 4th quadrant to be non-empty if all previous quadrants are already empty (+6 bytes 'cause I realised I'd screwed that up).

Need to figure out a way to shorten the selection of the characters.

(r=x=>Math.random(g=s=>s[7]?l--?s+:+g:s:g(s+RWSC-[c=r(5^s==------)]+urgbypcw-[c-4?r(8):8]))*x|0,l=r(4))=>g


Try it online!

# CSASM v2.5.1, 514 bytes

func a:
lda 0
sta $1 lda "" sta$2
push 1
push 5
extern Random.Next(i32,i32)
pop $a .lbl a push$a
brfalse b
dec $a push 0 pop$1
push ""
pop $3 .lbl c push$1
push 4
sub
brfalse d
inc $1 push "RWSC-" push 5 extern Random.Next(i32) dup push 1 add substr dup push "-" sub len brtrue f dup add br e .lbl f push "urgbypcw" push 8 extern Random.Next(i32) dup push 1 add substr add .lbl e push$3
swap
dup
pop $3 push "--------" sub len brtrue c push 0 pop$1
push ""
pop $3 br c .lbl d push$2
push $3 add pop$2
push $a brfalse a push$2
push ":"
pop $2 br a .lbl b push$2
print
ret
end


Whew, this was one mammoth of a program to write.

This submission defines a function a which generates then prints the random shape string as is specified by the challenge.

Explained:

func a:
; $a = random value in [1, 4] ;$1 = loop counter for quadrants
; $2 = final string ;$3 = build string
lda 0
sta $1 ; Initialize$2 to an empty string
lda ""
sta $2 ; Initialize$a to the random value specified above
push 1
push 5
extern Random.Next(i32,i32)
pop $a .lbl a ; Stop looping if$a == 0
push $a brfalse b dec$a

; Reset $1 and$3
push 0
pop $1 push "" pop$3

.lbl c
; Stop looping if $1 == 4 push$1
push 4
sub
brfalse d
inc $1 ; Get a random letter in "RWSC-" push "RWSC-" push 5 extern Random.Next(i32) dup push 1 add substr ; Check if the letter was "-" dup push "-" sub len brtrue f ; Letter was "-"; just add the other one dup add br e .lbl f ; Letter was not "-"; Get the color push "urgbypcw" push 8 extern Random.Next(i32) dup push 1 add substr ; Add the two parts together add .lbl e ; Add the new piece to$3
push $3 swap add dup pop$3

; Check $3. If it's "--------", generate another layer push "--------" sub len brtrue c ; Reset the quadrants counter and build string push 0 pop$1
push ""
pop $3 br c .lbl d ; Append the layer to the result push$2
push $3 add pop$2

; If $a == 0, don't add the ":" push$a
brfalse a
push $2 push ":" add pop$2
br a
.lbl b
; Print the result
push $2 print ret end  The first run of this code produce the following shape: --Cp----:--RgSg--:CgRwWbRr:WyWpWwWw More examples of shapes generated by the code: --ScWyRp WuWuCbRg:--RbSwRc:CwRuWrCb CgSgRpCb:Cu--SrRb Wc--CcCg:----CrRu SyCbWc-- SrCbSc--:----WrWb Ry--CgSr:--Cp--Sw --CwWpCc:CbCpRcSy:CpWw--Cy:--Ru----  # MathGolf, 49 bytes {╘4{4{"CRSW"w"bcgpruwy"w+v¶╛æ;û--}Γy}Γ'-8*a-}↔':u  Try it online. The first output was: CwRuRrRw:RpSwSbRp:RcWpRpCu:CpWwWgWc: Explanation: { ... }↔ # Do while false without popping: ╘ # Empty the stack (in case we've encountered a fully empty result) 4{ # Loop 4 times: 4{ # Inner loop 4 times: "CRSW"w # Push a random character from "CRSW" "bcgpruwy"w # Push a random character from "bcgpruwy" + # Concat these two characters together v # Push a random integer in the range [-2147483648,2147483647] ¶╛ # If this integer is a prime number, æ # use the following four character as inner code-block: ; # Discard the earlier character pair û-- # Push "--" } # After the inner loop: Γ # Wrap the top four items into a list y # And join this list together to a single string } # After the outer loop: Γ # Wrap the top (up to) four items into a list '-8*a- '# Remove any strings consisting of 8 "-" }↔ # After the do-while false: ':u '# Join the list with ":" delimiter # (after which the entire stack is output implicitly as result)  Compressed strings are pretty mediocre in MathGolf, so unfortunately it won't save any bytes here. "CRSW"w can be ╖╖'w+wδ which is 1 byte longer, and "bcgpruwy" can be ╕█≈║☺ 'ußy which is the same byte-count. # JavaScript (ES6), 117 bytes -2 thanks to @l4m2 Although this is not required, this solution has a uniform distribution. f=(k=(R=Math.random)()*4|0,q=R(n=4)*1185920)=>n--?f(k,q/33)+'SWCR-'[((q%=33)&35)%7]+'ugbrycpw-'[q>>2]:k?f(k-1)+':':''  Try it online! • Your comment made me go back to double check my solution and I realised I'd screwed up in how I was avoiding empty layers, preventing all possible outputs. Aug 26, 2021 at 15:48 • 117 – l4m2 Nov 17, 2023 at 6:30 # Bash, 158 bytes p='eval printf %s, ' s=$($p {S,W,C,R}{u,g,b,r,y,c,p,w})-- l=$p{$s}{$s}{$s}{$s}
$p{$l}:{$l}:{$l}:{$l}|tr , \\n|sed -E "s/-{4}(:|$)//g;/(^|:)(:|\$)/d"|shuf -n1


Attempt This Online!

# FMSLogo, 106 bytes

show bl map[word pick bf invoke[(crossmap "word ? ? ? ?)]se[--]crossmap "word[RWCS urgbycpw]":]random 1500


Don't try it online. The calormen.com logo interpreter does not support "templates" (which is what logo calls anonymous functions) on map, only procedure names; it's error messages also leave something to be desired. The program works in FMSLogo 8.3.2 and propably MSWLogo and UCBLogo as well, as FMSLogo is based on them.

Explaination:

• Logo is based on lisp but allows omitting parentheses around procedures that use their default arity.
• crossmap generates all possible combinations of the members of its inputs, which can be given in a list. The first input is a function, or "template", which is passed each combination to combine them.
• I don't know a better way to remove the empty layer yet, so the first crossmap generates a 1185921 element list of every possible layer and then bf removes the first one. That's why this program takes approximately 27 seconds to run on my laptop.
• Numbers can be coerced to strings in logo, so map maps the big instruction list in the middle over the characters of a random number, which has some length between one and four. repeat 1+random 4[...] would not output the concatenation of the results, and using map[...]iseq random 4 4 is 4 characters longer.
• I would not be able to tell, of course, but maybe some correlation in the random number generator makes some shapes actually impossible. I am not going to investigate.

Anonymous functions can not be called the normal way, unlike in javascript for example. They must be called by apply, map, or similar, which is why I consider

[bl map[word pick bf invoke[(crossmap "word ? ? ? ?)]se[--]crossmap "word[RWCS urgbycpw]":]random 1500]


to be an invalid submission.

Alternative much faster 5 characters longer solution, which, unlike the submission, can propably produce empty output:

## FMSLogo, 111 bytes

show bl map[invoke[if ?="--------: ["][?]]word map[pick se[--]crossmap "word[RWCS urgbcypw]]1234 ":]random 1500


Sample shape: CpCuRpSc:SwSuCcWr:Sb--RwSb (everyone seems to do this)

# Wolfram Language (Mathematica), 155 bytes

r=RandomInteger
c=RandomChoice
t=Table
StringRiffle[StringJoin/@(Nest[#/.c@#->"--"&,#,r@2]&/@t[t[#<>#2&@@c@*Characters/@{"RWSC","urgbypcw"},4],r@3+1]),":"]


The distribution of the non-empty quadrants is uniform, but the distribution of shapes is not, as empty quadrants are generated after the fact.

TIO requires two extra bytes for symbol assignment so that the output can be printed.

Try it online!

• 122 bytes
– att
Nov 20, 2023 at 10:15

# JavaScript (V8), 117 116 114 bytes

f=(k=n=5*~R(4))=>++n?n%5?R(~k%5)?'--'+f(k+1):'SWCR'[R(4)]+'ugbrycpw'[R(8)]+f(k):':'+f(n):''
R=n=>Math.random()*n|0
`

Try it online!

If already $$\k\$$ empty in this layer, then there's $$\\frac 1{4-k}\$$ probably be not empty, aka 1 when already 3 empty