# Fill a bowl with alphabet soup

We seem to never get tired of alphabet-related challenges...

# The recipe

Given

• a string of letters S, and
• two positive integers M, N,

produce an alphabet soup with the letters of S occupying random positions in a rectangular bowl of size M×N, framed by a non-alphabetic, non-space character to represent the rim of the bowl.

Positions not used by letters should be displayed as spaces. See the examples below.

• The size M×N refers to the interior of the bowl. The size including the rim is M+2×N+2.
• Each character from S should appear once in the bowl, in a different position; that is, one character cannot overwrite another.
• S may contain duplicates. For instance, if S is the string 'abcc', the soup must contain one a, one b, and two c (all in different positions).
• The inputs will satisfy the restrictions M >= 1, N >= 1, 1 <= length(S) <= M*N.
• The rim of the bowl can be any non-alphabetic, non-space character, consistent across program runs and input values.
• Letter positions in the bowl are random, so the result may differ every time the program is run with the same inputs.
• Given the input, every possible set of letter positions should have a non-zero probability. Since this cannot be checked from a few realizations of the program, please explain how your code fulfills this.
• Leading or trailing whitespace around the rim is allowed.
• S will contain are only uppercase letters. If wou wish, you can choose to take only lowercase letters.
• Input and output are flexible as usual. For example, the output can be a string with newlines, a 2D character array, or a list of lines.
• Programs or functions are allowed, in any programming language. Standard loopholes are forbidden.
• The shortest code in bytes wins.

# Examples

Inputs are shown as S, [M N], where M is number of rows and N is number of columns. The character # is used for the rim.

'O', [1 1]:

###
#O#
###

'HEY', [1 3]:

#####
#YHE#
#####

'HELLO', [4 11]:

#############
#  O        #
#         H #
#    LE     #
#   L       #
#############

##############
#K  V  L   S #
# A   V  X H #
#T    M C    #
# I       O N#
#  YC        #
# G  I   R SE#
#   J      F #
#JT  D  V EN #
##############

'OOOOOOOOOOXXXXX', [13 31]:

#################################
#    X                          #
#                O              #
#                               #
#                  X            #
#                        O      #
#             X           O     #
#      O                        #
#         X                     #
#                        O      #
#       X                       #
#                    O          #
#  O      O      O              #
#                             O #
#################################

'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ', [11 41]

###########################################
#                                       JU#
#     Q         C M    G     R T U Y  H   #
#  KI          E   H    M   YO            #
#      V BW        I    JC                #
#     SV           D     P   B          U #
#           A     F    RF   J  KP E       #
#            E   N      TH        Z       #
#    N  BM  O     Q   I        AS N  WX   #
#   S     O  K       G L       P       Q  #
#Z     L T         R   L       A      F DD#
#      V   Y           WX   C       G XZ  #
###########################################

• Loosely related – Luis Mendo Oct 3 at 8:40
• "Letter positions in the bowl are random, [...]" <-- do you mean can be random or must be random? And can all lines have the same number of chars, all the time? – Ismael Miguel Oct 3 at 17:21
• @IsmaelMiguel Must. And every configuration must have a nonzero probability of occurring (stated in the challenge). So that rules out the -always-same-length approach – Luis Mendo Oct 3 at 17:25
• Can the rim be consistent across program runs and input values, but use multiple non-letters, e.g. drawing an ASCII art border with |+-? – Adám Oct 3 at 17:45
• @Adám Hm I’m going to say no, that’s too big a change – Luis Mendo Oct 3 at 19:12

# 05AB1E, 20181615 14 bytes

*j.rS²ô2Føε8.ø


Takes three inputs in the order: height, width, string. Output as a 2D list of characters.
Uses 8 as border, but could be any digit.

-1 byte thanks to @Grimy.

Try it online or verify all test cases. (TIO contains }}J» in the footer to pretty-print the result; feel free to remove it to see the actual output 2D list of characters instead.)

Explanation:

*               # Multiply the (implicit) width and height inputs
j              # Pad the (implicit) input-string with up to that amount of leading spaces,
# so the total string-length is equal to that value
.r            # Shuffle the characters in the string
S           # Convert the string to a list of characters
# (edge-case for the zip below with strings of size 1 with 1x1 dimensions)
²          # Push the width input again
ô         # Split the list of characters into parts of that size
2F       # Loop 2 times:
ø      #  Zip/transpose the 2D list; swapping rows/columns
ε     #  Inner map over each line:
8.ø  #   And surround this line-list with a leading/trailing "8"
# (after the loop, the result is output implicitly)

• @LuisMendo I actually just say the challenge 3 minutes ago. ;) It's a pretty straight-forward implementation. Will try to golf it down from here. – Kevin Cruijssen Oct 3 at 8:56
• Very nice, I didn't think about *j! Here's 13 for legacy, or an ugly 14 for modern (outputs a 2D character array). – Grimmy Oct 3 at 11:59
• @Grimy The 13-byter seems to fail for input 1,1,"O", so I think it has to be the 14-byter for the legacy as well. Thanks for -1 though. – Kevin Cruijssen Oct 3 at 12:08

# APL (Dyalog Unicode), 25 bytesSBCS

'#',∘⌽∘⍉⍣4{⍵⍴⊃¨↓∘⍺¨?⍨×/⍵}


Try it online!

-22 thanks to @ngn, -7 thanks to @ngn and @Adám

Explanation:

'#',∘⌽∘⍉⍣4{⍵⍴⊃¨↓∘⍺¨?⍨×/⍵}
{            ⍵} ⍝ Function that generates the content
⍝  argument: ⍵ (width and height), ⍺ (string)
×/   ⍝ get the product
?⍨     ⍝ For each randomized elements
↓∘⍺¨       ⍝ take the character in ⍺
⍵⍴⊃¨           ⍝ turn it back into a matrix of shape ⍵
∘ ∘ ⍣4                ⍝ Then, 4 times, do these 3 things:
'#',                      ⍝ - prepend a # to the axis
⌽                    ⍝ - reverse the columns
⍉                  ⍝ - swap columns and lines


# APL (Dyalog Extended), 21 bytesSBCS

The rim's angles are different characters

{⌂disp⊂⍵⍴⊃¨↓∘⍺¨?⍨×/⍵}


Try it online!

Using the dfn to display the box.

• 29: '#',∘⌽∘⍉⍣4⊢⍴∘(?⍨∘≢⊃¨⊂)↑⍨∘(×/) Try it online! – Adám Oct 3 at 10:19
• 28: {'#',∘⌽∘⍉⍣4⊢⍵⍴⍺\⍨(≢⍺)>?⍨×/⍵} (or ≥ instead of > if ⎕io=1) – ngn Oct 3 at 10:35
• actually, 27: '#',∘⌽∘⍉⍣4{⍵⍴⍺\⍨(≢⍺)>?⍨×/⍵} – ngn Oct 3 at 10:42
• 25: '#',∘⌽∘⍉⍣4{⍵⍴⊃¨↓∘⍺¨?⍨×/⍵} – ngn Oct 3 at 10:51
• Regarding the 21 – Adám Oct 3 at 19:13

# Python 3, 110 bytes

lambda s,m,n,r=['#']:[r+(n*r+[i for i,j in{*zip(s+m*n*' ',range(m*n))}]+n*r)[k*n:-~k*n]+r for k in range(m+2)]


Try it online!

Randomizes by using a set comprehension and returns a 2D character array.

• Nice use of set comprehension to randomize. +1. But your codes relies on the name 'f' of the function. Therefore I think the lambda solution isn't valid... – agtoever Oct 3 at 16:41
• @agtoever Thanks! The function is not recursive, so it can be anonymous. Are you referring to the f'{s:{m*n}}' part? Because that is the syntax for a formatted string, which just coincidentally also happens to start with an f. – Jitse Oct 3 at 17:24
• This code seems to violate PEP 8 in several ways. – Christofer Ohlsson Oct 4 at 13:22
• @Christofer-Ohlsson absolutely! – Jitse Oct 4 at 18:48
• @ChristoferOhlsson Welcome to the world of code-golf, haha. ;) No comments/docs whatsoever; single character variables/methods without any (unnecessary) spaces/newlines; potentially hundreds of compiler-warnings we simply ignore; increasing performance from O(log(N)) to O(N^N); etc. If it can save even a single byte, it's all fine and totally worth it for code-golfing. ;) – Kevin Cruijssen Oct 7 at 12:30

# Bash + coreutils, 139 125 characters

r=printf %$3s@@|tr \ @ echo$r
printf %$[$2*$3]s$1|fold -1|shuf|tr -d \\n|fold -$3|paste -d@ <(:) - <(:)|head -$2
echo $r  Sample run: bash-5.0$ bash soup.sh HELLO 4 11
@@@@@@@@@@@@@
@  H        @
@      OE   @
@    L      @
@          L@
@@@@@@@@@@@@@


Try it online!

### Bash + coreutils + boxes, 97 characters

printf %$[$2*$3]s$1|fold -1|shuf|tr -d \\n|fold -$3|boxes -dsimple -s$[$3+2]x$[$2+2] -pa0 -itext  Sample run: bash-5.0$ set -- HELLO 4 11

bash-5.0$printf %$[$2*$3]s $1|fold -1|shuf|tr -d \\n|fold -$3|boxes -dsimple -s$[$3+2]x$[$2+2] -pa0 -itext
*************
* O   L  E  *
*      H    *
*           *
*     L     *
*************


Try it online! (Partially, as boxes is not installed on TIO.)

# J, 30 29 bytes

-1 byte thanks to Jonah

'#'|.@|:@,^:4[$*/@[(?~@[{{.)]  Try it online! • Because TIO uses a fixed random seed. 9!:1]6!:9'' fixes that – Adám Oct 3 at 12:32 • Really like the way used the zero fill of {.. Slight tweak for 29 '#'|.@|:@,^:4[$*/@[(?~@[{{.)]: Try it online! – Jonah Oct 4 at 2:08
• @Jonah Thank you! – Galen Ivanov Oct 4 at 3:48

# PowerShell, 163111 93 bytes

param($w,$h,$s)'#'*$w+-join($s|% *ht($w*$h)|% t*y|sort{Random})+'#'*$w-replace".{$w}",'#$0#
'


Try it online!

Takes input as $width, $height, $string. Constructs a string of # of the appropriate $width, string-joins that with some computation, and then that same # string again. The computation starts with taking the input $string, and doing a .padRight up to the $width by $height length (i.e., make a string long enough to completely take up the rectangular space. We then convert that string toCharArray, and sort it Randomly. That gives us the mixed-up middle portion. Finally, we -replace it into chunks of equal $width, and surround those chunks with #s.

-52 thanks to inspiration from AZTECCO
-18 bytes thanks to mazzy

• You have 2 x random (12) while JS has one, he added spaces to fill M*N size and sorted, after that you still pay 21 for '$' unfortunately – AZTECCO Oct 4 at 5:56 • @AZTECCO Thanks for the inspiration! – AdmBorkBork Oct 4 at 13:04 • Thanks @mazzy - very clever with the -replace instead of splitting and joining. – AdmBorkBork Oct 7 at 12:58 # JavaScript (ES7), 125 bytes Returns a string. Uses 0 as the frame character. (s,h,w)=>(a=[...s.padEnd(w++*h++)].sort(_=>Math.random()-.5),g=x=>y+~h?(x%w&&y%h&&a.pop())+[ [x-w]]+g(x<w?x+1:!++y):a)(y=0)  Try it online! ### Commented (s, h, w) => ( // s = string; h = height; w = width a = // build an array a[] consisting of: [...s.padEnd(w++ * h++)] // all original characters in s padded with spaces for a .sort(_ => // total length of w * h, in a random order Math.random() - .5 // (this is not guaranteed to be uniform, but it is not ), // required to be) g = x => // g is a recursive function taking x: y + ~h ? // if we haven't reached the end of the grid: ( x % w && // if we're not located on a vertical border y % h && // nor on a horizontal border, a.pop() // extract the last character from a[] ) + // (otherwise, append '0') [\n[x - w]] + // if we've reached the end of the row, append a linefeed g( // append the result of a recursive call: x < w ? x + 1 : !++y // using either (x+1, y) or (0, y+1) ) // end of recursive call : // else (end of grid): a // a[] is now empty and can be used as an empty string )(y = 0) // initial call to g with x = y = 0  # APL (Dyalog Extended), 23 bytesSBCS Anonymous tacit infix function. Takes [M,N] as left argument and S as right argument. '#',∘⌽∘⍉⍣4⊣⍴×/⍛(?⍨⍤⊣⊇↑)  Try it online! ×/⍛() apply the following function between the arguments, replacing the left argument with its product: ↑ take M×N characters from S, padding with spaces on the right ⊇ reorder that to the following order: ?⍨⍤ the shuffled indices 1 through… ⊣ the left argument (M×N) ⍴reshape that to the following shape: ⊣ the left argument (i.e. M rows and N columns) '#'⍣4 apply the following function four times, each time with the hash character as left argument: ∘⍉ transpose the right argument ∘⌽ mirror the right argument , concatenate a column of hashes to the left side of that # PHP 7.4, 10799 94 characters fn($s,$r,$c)=>_.chunk_split(($b=str_pad(_,$c,_)).str_shuffle(str_pad($s,$r*$c)),$c,"_
_").$b._  Thanks to: • Ismael Miguel for reminding me about PHP 7.4's arrow functions (-10 characters) • Night2 for efficiently reversing the concatenations and the join() (-8 characters) • Night2 for showing how to use chunk_split()'s $end parameter (-5 characters)

Try it online!

### PHP 7.3, 117112 108 characters

function s($s,$r,$c){echo _.chunk_split(($b=str_pad(_,$c,_)).str_shuffle(str_pad($s,$r*$c)),$c,"_ _").$b._;}


Thanks to:

• Night2 for efficiently reversing the concatenations and the join() (-5 characters)
• Night2 for showing how to use chunk_split()'s $end parameter (-4 characters) Sample run: php > function s($s,$r,$c){echo _.chunk_split(($b=str_pad(_,$c,_)).str_shuffle(str_pad($s,$r*$c)),$c,"_

# Ruby, 121 bytes

Creates the bowl, queries the indices of all spaces within the bowl, samples a number of spaces equal to the size of the string, and fills them in. sample does not return a sorted list, so there is no need to shuffle. Searching indices up to 9*m*n (which almost certainly goes out of range) will still get all spaces and is 1 byte shorter than r.size.

->s,m,n{r=[t=?@*(n+2),*["@#{' '*n}@"]*m,t]*$/;i=-1;(0..9*m*n).select{|j|r[j]==' '}.sample(s.size).map{|j|r[j]=s[i+=1]};r}  Try it online! # Red, 120116114 112 bytes -2 bytes thanks to @Kevin Cruijssen! func[s m n][random pad s m * n insert/dup t: copy"00"0 n print t loop m[print rejoin[0 take/part s n 0]]print t]  Try it online! • -2 bytes by getting rid of the + 1 and using to"""00"0 n instead. – Kevin Cruijssen Oct 3 at 9:50 • @KevinCruijssen Thanks! I replaced it with copy, for the same byte count. – Galen Ivanov Oct 3 at 10:10 • That indeed looks a bit cleaner! I don't know Red except for the answers I've seen from you, so I was just fiddling around a bit. ;) Would putting the t line as leading/trailing item before looping to save on the two loose print t be shorter? I doubt it, but since I don't know how to add items to a list I'm not sure. – Kevin Cruijssen Oct 3 at 11:22 # Perl 6, 74 67 bytes -5 bytes thanks to Jo King {0 X~0 x$^n,|comb($n,[~]$^s.comb[pick *,^$^m*$n]X//' '),0 x$n X~0}  Try it online! ### Explanation { } ^$^m*$n # Range 0 .. M*N-1 pick *, # Shuffle$^s.comb  # Split S into chars
[              ]  # Pick shuffled elements
X//' '  # undef to space
[~]  # Join
# Split into n-character strings
comb($n, ) | # Flatten # Add top and bottom of bowl 0 x$^n,                                            ,0 x$n # Add left and right of bowl 0 X~ X~0  # Perl 5-lF, 99 97 bytes -2 bytes courtesy of @NahuelFouilleul %k=map{$_=>$F[$_]||$"}0..($m=<>)*($n=<>)-1;say+($p='#'x($n+1)),map"# #"x!($i++%$n).$_,values%k,$p  Try it online! • ($i++%$n==0) could be changed by !($i++%$n) – Nahuel Fouilleul Oct 4 at 8:17 # k4, 32 28 bytes {4{|+x,'"#"}/y#a?(a:-*/y)$x}


edit: -4 thanks to Galen Ivanov!

called like

f["hey";3 3]


explanation:

                 (a:-*/y)    / neg product of y and assign to a
$x / left pad x so we have char vector the length of the inner area a? / take a random drawings. if a is negative, draw with no duplicates/replacements y# / reshape to y's dimensions 4{ }/ / do {} 4 times |+x,'"#" / append "#" along right-side of x then transpose (+) and reverse (|)  • I think you can save some bytes if you only append # at the end of each line and transpose/reverse 4 times, something like this. – Galen Ivanov Oct 7 at 8:54 • @GalenIvanov nice, updated! – scrawl Oct 7 at 11:06 # Java (JDK), 180 178 bytes Not a single extra import: (y,m,n)->{for(m*=n;y.length()<m;y+=" ");var s="";for(;m-->0;y=s)for(var c:y.split(s=""))s=Math.random()<.5?s+c:c+s;s="#".repeat(n);return(s+y+s).replaceAll(".{"+n+"}","\n#$0#");}


Try it online!

It was quite the struggle to get this golfed down. In particular, the imports involved with Collections.shuffle()/Arrays methods were too much to accept, so I had to create my own String shuffling algorithm (probably neither efficient nor uniformly distributed). Massive thanks to Steven for proving any set of positions can be generated from the algorithm.

Formatted (with explanation):

(y, m, n) ->                                                   // y = yummies in the soup,
{                                                              // m = height, n = width
for (m *= n; y.length() < m; y += " ")                     // set m to m*n and
;                                                      // add spaces to y to fill up
var s = "";                                                // the rest of the soup
for (; m-- > 0; y = s)                                     // for m*n iterations, scramble y
for (var c : y.split(s = ""))                          // with random appends
s = Math.random() < .5 ? s + c : c + s;
s = "#".repeat(n);                                         // create the top/bottom of the rim