Given input n, produce a word grid of size n, in as few bytes as possible.


The number provided will always be odd. The centre square of the grid must always be empty (a space character). The letters that fill the grid must be chosen at random from the English Scrabble letter distribution of letters. That is, each tile must be chosen at random, with uniform probability, from the following 100 characters:


where ? denotes blank tiles.

As with a Scrabble board, some tiles in this board will also be bonus tiles for a higher score. These tiles are only visible on a board with a size of 9 or more. and should be visible every 3 rows from the centre square in a pattern emanating outwards, but should never appear on the outermost letters. Bonus squares are denoted using lowercase letters, with blank tiles identified as ! instead of ?. Please see this visual demonstration or the reference implementation for more examples on how to correctly distribute bonus tiles.


Input: 5



Input: 9



Reference implementation.


This is so the shortest code in bytes wins.

  • Any reasonable format can be used for I/O, assuming it is consistent.
  • You should be able to handle grids at least up to 999.
  • All standard loopholes are forbidden.
  • 3
    \$\begingroup\$ I actually contemplated adding a scrabble letter-set yield to Jelly a while back :p \$\endgroup\$ Sep 12, 2017 at 15:54
  • \$\begingroup\$ Should these not be called letter grids, if we're just being placing tiles randomly without actually having to form any words? \$\endgroup\$
    – Shaggy
    Sep 13, 2017 at 11:01
  • \$\begingroup\$ @Shaggy So, I originally wanted to make the challenge about solving these as per this sandbox post, but when making scripts to generate these and validate the co-ordinates, I thought they'd make fun challenges on their own, I didn't update the name of the challenge as I worked it back though... Perhaps you're right! \$\endgroup\$ Sep 13, 2017 at 11:32

7 Answers 7


Jelly,  67 65 64 66 64  63 bytes


A monadic link taking a number and returning a list of characters, or a full program printing the result.

Try it online! (I prefer it using G rather than Y since that's more square)


”?;ØAx“³Ċu~Ṿr¥rṇ⁽ȦƑ’ḃ12¤µŒl⁾?!yW,WKF€ - Link 1, getLetterSets: no arguments
”?                                    - literal '?'
   ØA                                 - yield uppercase alphabet
  ;                                   - concatenate
                       ¤              - nilad followed by link(s) as a nilad:
      “³Ċu~Ṿr¥rṇ⁽ȦƑ’                  -   base 250 number
                    ḃ12               -   converted to bijective base 12 (frequencies)
     x                                -   times (repeat each)
                        µ             - start a new monadic chain, call that uppers
                         Œl           - to lower-case
                           ⁾?!        - literal ['?','!']
                              y       - translate (change '?'s to '!'s)
                               W      - wrap (that) in a list
                                 W    - wrap (uppers) in a list
                                ,     - pair
                                  K   - join with a space, ' '
                                   F€ - flatten €ach (both flattens the wrapped lists
                                      -               AND makes the lone ' ' into [' '])

H3ṬṚ¤ṁ‘1¦ṚŒḄ0,0j«þ`ị¢X€€Y - Main link: number, n                 e.g. 13
H                         - halve                                     6.5
    ¤                     - nilad followed by link(s) as a nilad:
 3                        -   literal three                           3
  Ṭ                       -   untruth                                 [0,0,1]
   Ṛ                      -   reverse                                 [1,0,0]
     ṁ                    - mould like (implicit range(int(right)))   [1,0,0,1,0,0]
        ¦                 - sparse application:
       1                  - ...to indices: 1
      ‘                   - ...action: increment                      [2,0,0,1,0,0]
         Ṛ                - reverse                                   [0,0,1,0,0,2]
          ŒḄ              - bounce                          [0,0,1,0,0,2,0,0,1,0,0]
            0,0           - literal [0,0]                             [0,0]
               j          - join                          [0,0,0,1,0,0,2,0,0,1,0,0,0]
                  `       - repeat left argument as right argument with:
                 þ        -   outer product using:       [[0,0,0,0,0,0,0,0,0,0,0,0,0],
                «         -     minimum                   [0,0,0,0,0,0,0,0,0,0,0,0,0],
                          -                               [0,0,0,0,0,0,0,0,0,0,0,0,0],
                          -                               [0,0,0,1,0,0,1,0,0,1,0,0,0],
                          -                               [0,0,0,0,0,0,0,0,0,0,0,0,0],
                          -                               [0,0,0,0,0,0,0,0,0,0,0,0,0],
                          -                               [0,0,0,1,0,0,2,0,0,1,0,0,0],
                          -                               [0,0,0,0,0,0,0,0,0,0,0,0,0],
                          -                               [0,0,0,0,0,0,0,0,0,0,0,0,0],
                          -                               [0,0,0,1,0,0,1,0,0,1,0,0,0],
                          -                               [0,0,0,0,0,0,0,0,0,0,0,0,0],
                          -                               [0,0,0,0,0,0,0,0,0,0,0,0,0],
                          -                               [0,0,0,0,0,0,0,0,0,0,0,0,0]]
                    ¢     - call the last link (1) as a nilad (the letter sets)
                   ị      - index into - gets the correct letter sets for each cell
                     X€€  - random-choice for €ach cell in €ach row
                        Y - join with newlines
                          - if running as a full program: implicit print
  • \$\begingroup\$ I don't think this replaces ? with ! when they are in the bonus tile position, I ran a few sets (easier to spot in the 9s!) Sorry... \$\endgroup\$ Sep 13, 2017 at 7:17
  • \$\begingroup\$ @DomHastings ⁾?!y means exactly that, but it seems to fail. \$\endgroup\$ Sep 13, 2017 at 11:11
  • \$\begingroup\$ @DomHastings you are correct there is a bug (since the list of uppercase-letters is wrapped in a list at the point I attempt to perform the translation it never sees the ?s) I will fix it up right now... \$\endgroup\$ Sep 13, 2017 at 15:19

R, 288 281 267 257 225 214 bytes

thanks to @cole for -1 byte, reordering the ? to collapse the 2 into rep(2,10)

-10 bytes realizing that row(m) == t(col(m))

-41 bytes thanks to user2390246 for reconfiguring the weights, golfing down the indexing, and some more usual R tips

m[K,K]=" "

Try it online!

Returns a matrix. Fairly simple implementation; samples n^2 values with the proper distribution, stores as an nxn matrix.

K is the index of the center.

L=col(m) is a matrix indicating the column number of each element in the matrix. Hence we compute !(L-K)%%3 to get the possible columns (including the edges), i.e., those a multiple of 3 away from the center column. To remove the edges, we consider L-1 and L-n. L-1 is 0 (false) for the first column and L-n is 0 for the last column. Applying & (element-wise boolean AND) to these three yields a matrix with TRUE in those columns a multiple of three away from the center, excluding the edges. We store this result as x.

If we take the transpose of x, t(x), we get the same matrix, but for the rows, hence x&t(x) is a matrix we save as i containing: TRUE indices for the required cells, and FALSE everywhere else.

Then we use chartr to perform the required transformation on m[i] and save the result as m[i], change the center cell to a space, and return the matrix.

Importantly as user2390246 pointed out, we don't need to test n>=9 because for n<7, there aren't any cells a multiple of 3 away from the center (apart from the center which is changed to a space anyway), and for n==7, the only cells a multiple of 3 from the center are on the edge so they are excluded. Neat!

  • 2
    \$\begingroup\$ Could you move the ? to the part of the string corresponding to rep(2,9) and just increase that to rep(2,10)? \$\endgroup\$
    – cole
    Sep 12, 2017 at 16:53
  • \$\begingroup\$ @cole yes, that would be I think -1 byte? \$\endgroup\$
    – Giuseppe
    Sep 12, 2017 at 16:58
  • \$\begingroup\$ Looks like it, since you remove 2 without 2, and gain 1 for 10. \$\endgroup\$
    – cole
    Sep 12, 2017 at 16:59
  • 1
    \$\begingroup\$ For the letter distribution: rep(c(12,8,9,6,4:1),c(1,1:4,1,10,5)) saves 10 bytes. (You'll need to switch the order of the letters to EOAI...) \$\endgroup\$ Sep 13, 2017 at 16:02
  • \$\begingroup\$ @user2390246 wow, thanks! \$\endgroup\$
    – Giuseppe
    Sep 13, 2017 at 16:13

JavaScript (ES6), 247 242 bytes

-5 bytes with help from @Shaggy

n=>[...Array(p=n*n)].map((_,i)=>i==p>>1?" ":g(i/n|0)&g(i%n,c="AI9O8NRTEE6LSUD4G3BCMPFHVWY?2KJXQZ".replace(/(\D+)(\d)/g,(_,c,i)=>c.repeat(i))[Math.random()*100|0])?c<"A"?"!":c.toLowerCase():++i%n?c:c+`

Test Snippet

let f=
n=>[...Array(p=n*n)].map((_,i)=>i==p>>1?" ":g(i/n|0)&g(i%n,c="AI9O8NRTEE6LSUD4G3BCMPFHVWY?2KJXQZ".replace(/(\D+)(\d)/g,(_,c,i)=>c.repeat(i))[Math.random()*100|0])?c<"A"?"!":c.toLowerCase():++i%n?c:c+`

;(R.onclick=L.onchange=I.oninput=function(){let n=+I.value,res=f(n);O.innerText=n+"\n"+(L.checked?res.replace(/[A-Z\?]/g,".").replace(/[a-z!]/g,"#"):res);})()
<label><input id=L type=checkbox> Layout only</label> <button id=R>Rerun</button><br><input id=I type=range min=9 max=53 step=2 value=9 style="width:100%">
<pre id=O></pre>

  • \$\begingroup\$ You should be able to save a couple of bytes by moving the assignment of c within one of the calls to g. \$\endgroup\$
    – Shaggy
    Sep 13, 2017 at 11:14
  • \$\begingroup\$ Really like the slider and layout functionality! \$\endgroup\$ Sep 13, 2017 at 19:33
  • \$\begingroup\$ @Shaggy Thanks, I was able to save some more by improving the ternary expressions too. \$\endgroup\$ Sep 13, 2017 at 19:47

Perl 6, 162 161 154 153 bytes

{(my@b=0,|(-$^m..$m).map({$_%%3+!$_}),0).map:{((my$e='AIJKQXZG'~'NRTBCFHMPVWYG'x 2~'EEEAAIIOONRTUSLD'x 4)~'??',$e.lc~'!!',' ')[@b Xmin$_]».comb».roll}}

Try it online!

Takes (n-3)/2 as input, returns a list of lists of letters.


-> $m {
    # Idea stolen from @Xcali's answer.
    # Array containing 1 for bonus tiles, 2 for middle element, like
    #     (0,1,0,0,1,0,0,2,0,0,1,0,0,1,0)
    my @b = 0, |(-$m..$m).map({ $_ %% 3 + !$_ }), 0;
    # Map column vector.
    @b.map: {
        # Compute element-wise minimum of row vector and value from
        # column vector. Select character pools accordingly and get
        # random items.
        ($e~'??', $e.lc~'!!', ' ')[@b Xmin $_]».comb».roll

Perl 5, 246 244 + 1 (-n) = 247 245 bytes

$m=($n=$_-$_%2)/2;for(@a=(U x++$n)x$n){$_=(LUU,ULU,UUL)[$m%3]x($n/3).U x($n%3)if$z%@a&&$z%3==$m%3;substr$_,$m,1,$"if$m==$z++;s/L/-U/g;s/U/substr"EEAIONRT"x6 .AIJKQXZG."AIO?BCFHMPVWYGUUSSLLDD"x2,rand 100,1/ge;s/^-//;s/-\?/!/g;s/-(.)/lc$1/ge;say}

Try it online!

  • \$\begingroup\$ Fixed it now along with a few other things. \$\endgroup\$
    – Xcali
    Sep 13, 2017 at 4:17

[C64 basic v2, 210 209 bytes]


An input n 5 can be specified like


That "5" to the end should be changed to any odd number. Don't give more than 25, then the program will overwrite itself.

How to try it: google for "vice c64 emulator", install it, and copy-paste this basic code into it. To start the program, type: RUN. To clear the screen, press shift/home.

And the result:

enter image description here

  • \$\begingroup\$ What's the best way for me to validate this and apply the input? :) \$\endgroup\$ Sep 13, 2017 at 19:34
  • \$\begingroup\$ @DomHastings C64 emulator with a monitor (monitors are byte-level memory modifiers in the c64 world), or the help plus plus basic extender. I am not 100% sure that it will work, but 90 yes. \$\endgroup\$
    – peterh
    Sep 13, 2017 at 19:38
  • \$\begingroup\$ @DomHastings I can make a 3 byte longer, debuggable version. The important thing is that the common basic commands are given here in tokenized form. The token is always very simple: first char of the command + the second char in uppercase. For example, FOR is tokenzied as fO. Or GOTO as gO. I could have made it also interactive, but unfortunately the INPUT command is an exception, it doesn't have a token. This is why the input should be given with DATA command, whose token is dA. \$\endgroup\$
    – peterh
    Sep 13, 2017 at 19:39
  • \$\begingroup\$ I'd certainly love to see it in action, any way you can help me get to see it would be awesome! Ah, thanks for explaining, that make it read better to me. I'll come back to this in the morning!! \$\endgroup\$ Sep 13, 2017 at 19:43
  • \$\begingroup\$ @DomHastings Don't need, it is ready. :-) There were some minor bugs, I fixed them and the result become 1 byte smaller! In asm, it could have been only roughly 40 byte. Although I had to use some tricky way to the random generation. \$\endgroup\$
    – peterh
    Sep 13, 2017 at 20:19

Python 3, 214 236 240 bytes

lambda n:[[[choice([s,[[c.lower(),'!'][c<'A']for c in s]][((i-n//2)%3+(j-n//2)%3<1)*(i*j>0)*(i<n-1)*(j<n-1)])," "][i==j==n//2]for j in range(n)]for i in range(n)]
from random import*

Try it online!

The multiplicity of each character is expressed as sum of powers of two, e.g 12 = 8 + 4 => "E"*12 = "E"*2*2*2 + "E"*2*2.

((i-n//2)%3+(j-n//2)%3<1)*(i*j>0)*(i<n-1)*(j<n-1) may probably be golfed.

  • \$\begingroup\$ @Giuseppe I will fix it \$\endgroup\$
    – jferard
    Sep 13, 2017 at 18:35
  • \$\begingroup\$ I like the extra clarification via the assert! \$\endgroup\$ Sep 13, 2017 at 19:33
  • \$\begingroup\$ @Giuseppe I hope it's ok now. i<n was always true, even when i=n-1 (bottom). Same for j. \$\endgroup\$
    – jferard
    Sep 13, 2017 at 20:30
  • \$\begingroup\$ looks good to me! +1. \$\endgroup\$
    – Giuseppe
    Sep 13, 2017 at 20:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.