# 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 that could ever be constructed 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.

# 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

# 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

# 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


# 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

# 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

# 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.


# JavaScript (ES6), 119 bytes

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)<32?q&3:4]+'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

# 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----


# 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

# 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.