JavaScript (ES6), 132 bytes
f=N=>(F=n=>N?n<30?F[k=n%5]^(F[k]|=1<<(v=Math.random()*15))?(n++-17?"_BINGO"[n]||k*15-~v:'free')+`
`[k>>2]+F(n):F(n):`
`+f(N-1):n)``
Commented
f = N => ( // outer function taking the number N of grids
F = n => // inner function taking a counter n
N ? // if there's still a grid to process:
n < 30 ? // if n is less than 30:
F[k = n % 5] // k is the column index in [0..4]
^ // if F[k] is not equal to its updated value
( F[k] |= 1 << ( // where the floor(v)-th bit is set
v = // where v is
Math.random() // randomly picked in [0,15[
* 15 //
)) ? // then:
( n++ - 17 ? // if this is not the center cell:
"_BINGO"[n] // append either a BINGO letter (if n <= 5)
|| // or
k * 15 - ~v // the number k * 15 + floor(v + 1)
: // else:
'free' // append the word 'free'
) + //
` \n`[k >> 2] + // append a line-feed if k = 4,
// or a space otherwise
F(n) // append the result of a recursive call to F
: // else:
F(n) // try again
: // else:
`\n` + // append a line-feed followed by
f(N - 1) // the result of a recursive call to f
: // else:
n // stop the recursion
)`` // initial call to F with n zero'ish
Arnauld
- 205.5k
- 21
- 187
- 670