Skip to main content
10 of 10
added 466 characters in body
Giuseppe
  • 28.8k
  • 3
  • 31
  • 105

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!

Giuseppe
  • 28.8k
  • 3
  • 31
  • 105