Generating Word Grids

Jelly,  67 65 64 66 64  63 bytes

”?;ØAx“³Ċu~Ṿr¥rṇ⁽ȦƑ’ḃ12¤µŒl⁾?!yW,WKF€
H3ṬṚ¤ṁ‘1¦ṚŒḄ0,0j«þ`ị¢X€€Y

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)

How?

”?;Ø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

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

function(n){m=matrix(sample(el(strsplit("EOAINRTLSUDGBCMPFHVW?YKJXQZ","")),n^2,T,rep(c(12,8,9,6,4:1),c(1,1:4,1,10,5))),,n)
K=n/2+.5
L=col(m)
m[i]=chartr("A-Z?","a-z!",m[i<-(x=!(L-K)%%3&L-1&L-n)&t(x)])
m[K,K]=" "
m}

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!


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+`
`,g=x=>x&&x<n-1&((n>>1)-x)%n%3==0).join``

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+`
`,g=x=>x&&x<n-1&((n>>1)-x)%n%3==0).join``

;(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>