Bingo
Bingo is a numbers game where players match randomly drawn numbers to the numbers on their cards. Each bingo card is a square grid with 25 spaces, and the columns of the grid are labeled with letters such as "B", "I", "N", "G", "O". The letters help to identify the number range in each column, for example, the "B" column may contain numbers 1 to 15.
One of the spaces in the middle of the grid is often a "free" space, and is marked as such. To play the game, each player is given a bingo card with numbers on it. A caller then draws numbers randomly and announces them, and players mark the corresponding numbers on their cards. The first player to match a pre-determined pattern of marked numbers, such as a straight line, all four corners, or a full card, calls out "Bingo!" to win the game. The game continues until one player wins, or multiple players win on the same round. [1]
Challenge
Create a set of n bingo cards that are printed / outputted in sequence separated by one or two empty lines. The center field should be marked as free.
The output should be a 5x5 grid where columns are either separated by a single space, a tab or the grid should be fixed width, and the lines are separated by a single new line.
The randomness must be uniform, i.e. all possible permutations and combinations should be equally likely to occur. All numbers must be unique.
The numbers:
B = 1 - 15
I = 16 - 30
N = 31 - 45
G = 46 - 60
O = 61 - 75
The number n must be taken as input.
Example output for n = 3:
B I N G O
1 16 31 46 61
2 17 32 47 62
3 18 free 48 63
4 19 34 49 64
5 20 35 50 65
B I N G O
6 30 40 53 64
5 20 35 50 70
4 25 free 51 69
7 22 32 52 68
11 23 38 49 67
B I N G O
10 25 40 55 70
11 26 41 56 71
12 27 free 57 73
13 28 43 58 73
14 29 44 59 74
Yes, those were definitely randomly created.
This challenge is based on the fact that I recently had to create 100 Bingo cards. It turned out that this part of the challenge was the easy one. Making it pretty, printable and presentable was the time consuming part.
-
\$\begingroup\$ Not a duplicate of this similarly named challenge. \$\endgroup\$Stewie Griffin– Stewie Griffin2023年01月31日 07:43:40 +00:00Commented Jan 31, 2023 at 7:43
-
1\$\begingroup\$ Can the "BINGO" be lowercase? \$\endgroup\$Shaggy– Shaggy2023年01月31日 12:31:16 +00:00Commented Jan 31, 2023 at 12:31
-
3\$\begingroup\$ Doesn't this challenge duplicate codegolf.stackexchange.com/questions/2631/bingo-card-generator ? \$\endgroup\$gildux– gildux2023年01月31日 19:57:44 +00:00Commented Jan 31, 2023 at 19:57
-
3\$\begingroup\$ @gildux, No. In my opinion it's not even close. Creating a list of 5 * 15 random numbers is a whole lot different than this one. The Python answer in this one is 193 bytes while a pyhton answer in the other one is 84 bytes. That alone should show the difference, in my opinion. \$\endgroup\$Stewie Griffin– Stewie Griffin2023年02月01日 06:19:46 +00:00Commented Feb 1, 2023 at 6:19
-
1\$\begingroup\$ In "my" language, MATLAB, golfing "create 5*15 random numbers" involves a completely different code than the code required to solve this challenge. Mixing numeric values with characters takes a bit of effort, and so does doing it several times without duplicating cards. I agree that in a language where replacing a numeric value with a character and adding a header to a numeric(?) array is trivial, then the differences aren't that big. In my, albeit biased, opnion, these are two different challenges that require two different approaches in some languages, and is therefore not a duplicate. \$\endgroup\$Stewie Griffin– Stewie Griffin2023年02月03日 07:23:19 +00:00Commented Feb 3, 2023 at 7:23
19 Answers 19
Vyxal J, 29 bytes
(‛6ṫ⇧Ṅ75ɾ15ẇƛÞc/o5Ẏ;∩2≬2‛λċȦV⁋¶
Explained (old)
( # input times:
75ɾ15ẇ # push the range [1, 75] split into chunks of 15 items
ƛÞc/o5Ẏ; # to each: random_permutation[:5]
∩ # transpose
2≬2‛λċȦV # tos[2][2] = "free"
‛6ṫ⇧p # .prepend("BINGO")
vṄ⁋ # .map(" ".join(x)).join("\n")
¶+, # + "\n" (then print)
QBasic, 211 bytes
DIM b(5,5)
RANDOMIZE TIMER
INPUT n
FOR i=1TO n
ERASE b
?
?"B","I","N","G","O
FOR r=0TO 4
FOR c=0TO 4
4x=INT((c+RND)*15+1)
FOR p=0TO r
IF x=b(p,c)GOTO 4
NEXT p
IF(r=2)*(c=2)THEN?"free",ELSE?x,:b(r,c)=x
NEXT c,r,i
Try it at Archive.org! Example run:
An example run with input 3, showing 3 randomly generated bingo cards
Explanation/ungolfed
DIM b(5, 5)
RANDOMIZE TIMER
INPUT n
Declare b as a 5x5 array; we'll use this to track already-selected numbers and avoid duplicates. Seed the pseudo-random number generator using the current time. Prompt the user for the number of cards n.
FOR i = 1 TO n
Loop n times:
ERASE b
PRINT
PRINT "B", "I", "N", "G", "O"
Set all values in b to zero. Print a blank line followed by the B I N G O header. The commas in the PRINT statements separate the items by tabs, using a default tab stop of 14 columns.
FOR r = 0 TO 4
FOR c = 0 TO 4
Loop over rows 0 through 4; for each row, loop over columns 0 through 4.
4 x = INT((c + RND) * 15 + 1)
Label this as line number 4, which we'll use as a GOTO target later. Set x to be a random integer, greater than or equal to c*15+1, and less than (c+1)*15+1.
FOR p = 0 TO r
IF x = b(p, c) THEN GOTO 4
NEXT p
Loop over each previous row p. If x equals the value in array b at row p, column c, go back to line 4 and generate a new random number.
IF (r = 2) * (c = 2) THEN
PRINT "free",
ELSE
PRINT x,
b(r, c) = x
END IF
If both r and c are 2, print the string "free"; otherwise, print x and set the appropriate value in the b array to x. In both cases, end the print statement with a tab rather than a newline.
NEXT c, r, i
Close all the FOR loops.
Astute readers may have noticed this doesn't print a newline at the end of each row. Because of the size of the default tab stop, it just so happens that the sixth column wraps to the next line, so printing a newline explicitly is unnecessary (and, in fact, results in unwanted blank lines). This might be bending the I/O requirements a bit. If an explicit newline must be printed, modify the B I N G O line by adding ", at the end, and add a line containing ? in between the FOR r and FOR c headers.
Factor, 123 bytes
[ [ "free"2 75 [1,b] 15 group [ 5 sample ] map flip
[ third set-nth ] keep "BINGO"1 group prefix simple-table. nl ] times ]
[ ... ] timesDo something a number of times.
"free" ! "free"
2 ! "free" 2
75 ! "free" 2 75
[1,b] ! "free" 2 { 1 2 ... 75 }
15 ! "free" 2 { 1 2 ... 75 } 15
group ! "free" 2 { { 1 2 .. 15 } { 16 17 .. 30 } ... { 61 62 .. 75 } }
[ 5 sample ] map ! "free" 2 {
! { 9 13 14 1 5 }
! { 26 24 25 27 22 }
! { 32 38 41 31 33 }
! { 54 59 55 49 57 }
! { 75 74 70 71 63 }
! }
flip ! "free" 2 {
! { 9 26 32 54 75 }
! { 13 24 38 59 74 }
! { 14 25 41 55 70 }
! { 1 27 31 49 71 }
! { 5 22 33 57 63 }
! }
[ third set-nth ] keep ! {
! { 9 26 32 54 75 }
! { 13 24 38 59 74 }
! { 14 25 "free" 55 70 }
! { 1 27 31 49 71 }
! { 5 22 33 57 63 }
! }
"BINGO" ! { ... } "BINGO"
1 group ! { ... } { "B" "I" "N" "G" "O" }
prefix ! {
! { "B" "I" "N" "G" "O" }
! { 9 26 32 54 75 }
! { 13 24 38 59 74 }
! { 14 25 "free" 55 70 }
! { 1 27 31 49 71 }
! { 5 22 33 57 63 }
! }
simple-table. ! output to stdout
nl ! newline
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
-
\$\begingroup\$ Grids need only be separated by a single newline, so you can drop the one before the recursive call to
f. \$\endgroup\$Shaggy– Shaggy2023年01月31日 17:18:11 +00:00Commented Jan 31, 2023 at 17:18 -
1\$\begingroup\$ @Shaggy Not 100% sure about that. The challenge says separated by one or two *empty* lines. \$\endgroup\$Arnauld– Arnauld2023年01月31日 17:22:32 +00:00Commented Jan 31, 2023 at 17:22
Bash + GNU utils, 120
for((;j++<1ドル;));{
for((;++i<75;));{
a+=" <(shuf -i$i-$[i+=14] -n5)"
}
eval paste$a|sed '1iB I N G O
3s/\w*/free/3
$a
'
}
Japt -mR, (削除) 39 (削除ここまで) 38 bytes
75õ òF Ëö¤v5Ãg2Èh2`fe`ÃÕi"BINGO"¬ m ̧·
75õ òF Ëö¤v5Ãg2Èh2`fe`ÃÕi"BINGO"¬ m ̧· :Implicit map of range [0,input)
75õ :Range [1,75]
ò :Partitions of length
F : 15
Ë :Map
ö¤ : Shuffle
v5 : First 5 elements
à :End map
g2 :Modify the element at 0-based index 2 by
È :Passing it through the following function
h2 : Replace the element at 0-based index 2 with
`fe` : Compressed string "free"
à :End modification
Õ :Transpose
i :Prepend
"BINGO"¬ : "BINGO", split into an array
m :Map
̧ : Join with spaces
· :Join with newlines
:Implicit output joined with newlines
-
\$\begingroup\$ Came up with this independently but it's pretty close to yours: 37 \$\endgroup\$noodle person– noodle person2023年02月02日 01:01:10 +00:00Commented Feb 2, 2023 at 1:01
-
\$\begingroup\$ 37 with explanation \$\endgroup\$noodle person– noodle person2023年02月02日 01:14:56 +00:00Commented Feb 2, 2023 at 1:14
-
\$\begingroup\$ Oops golfed yours to 36 \$\endgroup\$noodle person– noodle person2023年02月02日 01:46:23 +00:00Commented Feb 2, 2023 at 1:46
-
\$\begingroup\$ That won't work, @Jacob;
A.ö(n)allows for duplicates to be included, hence why I'm shuffling and then grabbing the first 5 items. \$\endgroup\$Shaggy– Shaggy2023年02月02日 09:22:57 +00:00Commented Feb 2, 2023 at 9:22
Charcoal, (削除) 54 (削除ここまで) (削除) 52 (削除ここまで) 49 bytes
FN«≔⪪BINGO1θF25⊞θ⎇=κ12free×ばつ15%κ5...1¦16θ⟦E⪪θ5⪫κ
Try it online! Link is to verbose version of code. Explanation: Now inspired by @Arnauld's answer.
FN«
Input n and loop that many times.
≔⪪BINGO1θ
Start with a list of BINGO as separate characters which will make up the first row.
F25
Loop 25 times.
⊞θ⎇=κ12free×ばつ15%κ5...1¦16θ
Put the word free in the middle cell, otherwise take a random element of the appropriate numeric range for the cell but subtracting all of the numbers generated so far, and push the result to the list.
⟦E⪪θ5⪫κ
Split into rows of five cells, join the rows on spaces and output the resulting board double-spaced from the next board.
Pyth, 39 bytes
VQkjd"BINGO"jjL\ Cc5X%3s.SMc5S75y6"free
Explanation
# implicitly assign Q = eval(input())
VQ # for N in range(Q):
k # print a newline
jd"BINGO" # print BINGO joined on spaces
S75 # range(1,76)
c5 # split into 5 equal parts
.SM # randomly shuffle each part
s # join the parts back together
%3 # take every third element
X y6"free # replace the 12th element with "free"
c5 # split into 5 equal parts again
C # transpose
jL\ # join each part on whitespace, converting all elements to strings
j # join on newlines and print
-
1\$\begingroup\$ @Arnauld See, I knew that was part of the challenge and then forgot to do it before posting. Fixed now for a measly 3 bytes. Thanks for keeping me honest :) \$\endgroup\$CursorCoercer– CursorCoercer2023年01月31日 17:42:40 +00:00Commented Jan 31, 2023 at 17:42
-
\$\begingroup\$ OP's note: I'll allow the single leading newline. :) \$\endgroup\$Stewie Griffin– Stewie Griffin2023年02月01日 06:26:31 +00:00Commented Feb 1, 2023 at 6:26
05AB1E, 32 bytes
F75L5äε.r5£N<i'22ドルǝ}}ø‘Èß‘Sš»,¶?
Explanation:
F # Loop the (implicit) input-integer amount of times:
75L # Push a list in the range [1,75]
5ä # Split it into five equal-sized parts
ε # Map over each part:
.r # Randomly shuffle the list
5£ # Then only keep the first 5 integers
N<i # If the index is 2:
'2ドル '# Push dictionary string "free"
2ǝ # Insert it at (0-based) index 2 into the quintuplet-list
}} # Close both the if-statement and map
ø # Zip/transpose the matrix; swapping rows/columns
‘Èß‘ # Push dictionary string "BINGO"
S # Convert it to a list of characters: ["B","I","N","G","O"]
š # Prepend it to the matrix
» # Join each inner list by spaces; and then each string by newlines
, # Pop and print it with trailing newline
¶? # And print an additional newline character
See this 05AB1E tip of mine (section How to use the dictionary?) to understand why '2ドル is "free" and ‘Èß‘ is "BINGO".
Pip, (削除) 50 (削除ここまで) 49 bytes
La{Y ZSH*,75円CH5y@2@2:"free"P_JsPBnMyH5PE"BINGO"}
Explanation
La{...}
Given command-line argument a, loop that many times:
Y ZSH*,75円CH5
,75円 ; Inclusive range from 1 to 75
CH5 ; Chop into 5 equal sections
SH* ; Randomly shuffle each section
Z ; Transpose
Y ; Yank that result into the y variable
y@2@2:"free" ; Set the element of y at index 2,2 to the string "free"
P_JsPBnMyH5PE"BINGO"
yH5 ; Take the first 5 elements of y
PE"BINGO" ; Prepend the element "BINGO"
M ; Map to each:
_Js ; Join on spaces
PBn ; and push a newline to the end of the resulting string
P ; Print that list, concatenated, with a trailing newline
C (gcc), (削除) 262 (削除ここまで) 225 bytes
- -37 thanks to c--
a[6][5]={L"BINGO"};f(n,i,j,k,r){--n&&puts("",f(n));for(j=5;j--;)for(k=i=0;k||++i<6;k||(a[i][j]=r))for(r=j*15+rand(k=i)%15+1;--k&&a[k][j]-r;);for(i=0;i<6;i+=puts(""))for(j=0;j<5;)printf(i*j-9?i?"%d ":"%c ":"free ",a[i][j++]);}
Ungolfed (` used instead of literal tab character):
a[6][5]={L"BINGO"}; // bingo array, with header
f(n, // number of cards to generate
i,j, // row and column
k, // random number row checker
r // random number
) {
--n && puts("",f(n)); // recursively print cards, adding newline
for(j=5;j--;) // for each column
for(k=i=0;k||++i<6;k||(a[i][j]=r)) // for each row (redo duplicates)
for(r=j*15+rand(k=i)%15+1;--k&&a[k][j]-r;); // detect duplicates
for(i=0;i<6;i+=puts("")) // mark middle item as free; print board
for(j=0;j<5;)
printf(i*j-9?i?"%d`":"%c`":"free`",a[i][j++]);
}
-
\$\begingroup\$ it looks like you don't need to clear the array at all, because you only check for duplicates in the elements you just set \$\endgroup\$c--– c--2023年02月01日 20:29:44 +00:00Commented Feb 1, 2023 at 20:29
-
-
\$\begingroup\$ 221 bytes \$\endgroup\$ceilingcat– ceilingcat2023年02月11日 17:58:22 +00:00Commented Feb 11, 2023 at 17:58
APL (Dyalog Extended), 46 bytes
Full program. Prompts for n.
('BINGO'{'free'} ̈@3@4⍤⍪(×ばつ...4)+⍤1⍉)⍤2↑5? ̈⎕5⍴15
⎕5⍴15 prompt for n and join that 5, then use that as dimensions for an array of 15s
5? ̈ for each 15, get 5 random numbers in the range 1 through that, without replacement
↑ mix this matrix of lists into an n-by-5-by-5 array with each row being a randome 5-of-15
(...)⍤2 on each layer:
⍉ transpose, so the 5-of-15 rows become columns
(...)+⍤1 add the following to each row:
...4 the numbers 0 through 4
×ばつ fifteen multiplied by those
'BINGO'...⍤⍪ prepend a row of the given letters, then:
...@4 at row 4:
...@3 at column 3:
{...} ̈ replace each value (though there's only one) with:
'free' the given word
-
\$\begingroup\$ @Shaggy Lack of sleep, I guess. Should be alright now. \$\endgroup\$Adám– Adám2023年01月31日 23:05:13 +00:00Commented Jan 31, 2023 at 23:05
Jelly, (削除) 38 (削除ここまで) 36 bytes
75s15Ẋ€ḣ5ドルZ"ƝM»3ドル¦3ドル¦"BINGO"ṭṚK€Y7Ṅ)
Most of the credit goes to @cairdcoinheringaahing and @UnrelatedString for helping me in chat.
- -2 thanks to UnrelatedString
This can probably be a bit shorter, though.
Explanation
75 # 75 (implicit range)
s15 # Split into 15 parts
Ẋ€ # Random permutation of each
ḣ5ドル # First 5 elements of each
Z # Transpose
"ƝM» # Replace ↓ with "free"
3ドル¦3ドル¦ # The third item in the third item (1-indexed)
"BINGO" # Push "BINGO"
ṭṚ # Append and reverse (i.e. prepend)
K€ # Join each by spaces
Y7 # Join by newlines and append a newline
Ṅ # Print
) # Repeat input number of times
Python 3, 151 bytes
from random import *
print("B I N G O")
x=lambda n:n*15+randint(0,15)
[print(*[*("free"if(i==2 and j==2)else x(j)for j in range(5))])for i in range(5)]
Raku, 94 bytes
{join "
",({S:13th/\d+/free/}((<B I N G O>,|[Z] (1..75).rotor(15)».pick(5)).join("
"))xx$_)}
(1..75).rotor(15)splits the range 1-75 into five subranges of size 15: 1-15, 16-30, etc.».pick(5)selects five distinct random elements from each of those subranges.[Z]reduces that list of lists of random choices with theZip operator. It effectively transposes the matrix, so the 1-15 selections are in the first column instead of the first row.<B I N G O>,|...prepends the header row to the lists of numbers..join("\n")joins the rows with newlines, producing a Bingo board as a single string. The actual code uses a single newline character between the quotes to save a byte.S:13th/\d+/free/changes the thirteenth occurrence of a number in that string tofree.xx $_replicates the preceding process a number of times given by the input argument$_, producing a list of board strings.join "\n\n"joins those boards together with two newlines between them.
Python 3, (削除) 198 166 (削除ここまで) 157 bytes
lambda n:exec('m=["BINGO",*map(list,zip(*[sample(range(i,i+15),5)for i in range(1,75,15)])),""];m[3][2]="free";[print(*i)for i in m];'*n)
from random import*
(ATO isn't working right now so I switched to TIO)
- -32 thanks to tsh
This can probably be golfed a bit more.
-
\$\begingroup\$ typo at line 2, need space before
or\$\endgroup\$Iuri Guilherme– Iuri Guilherme2023年02月01日 18:10:58 +00:00Commented Feb 1, 2023 at 18:10 -
\$\begingroup\$ @IuriGuilherme it's not a typo - Python will let you remove the space after a number in most cases. \$\endgroup\$The Thonnu– The Thonnu2023年02月01日 18:17:07 +00:00Commented Feb 1, 2023 at 18:17
-
1\$\begingroup\$ not in python3.11 anymore \$\endgroup\$Iuri Guilherme– Iuri Guilherme2023年02月01日 18:28:54 +00:00Commented Feb 1, 2023 at 18:28
-
\$\begingroup\$ So change to say it's Python prior 3.11 or add the space... \$\endgroup\$gildux– gildux2023年02月03日 01:36:55 +00:00Commented Feb 3, 2023 at 1:36
-
\$\begingroup\$ 166 bytes:
lambda n:exec('m=["BINGO",*map(list,zip(*[sample(range(i,i+15),5)for i in range(1,75,15)])),""];m[3][2]="FREE";[print(*i,sep="\t")for i in m];'*n)\$\endgroup\$tsh– tsh2023年02月03日 03:56:10 +00:00Commented Feb 3, 2023 at 3:56
Zsh +coreutils, 105 bytes
repeat 1ドル {A=(B I N G O `(for i ({1..75..15})shuf -i$i-$[i+14] -n5)|rs -t 5`)
A[18]=free;<<<$A|rs 6;echo}
Kotlin 2.0+, 188 bytes
fun b(x:Int)=repeat(x){println("\nB I N G O");val c=List(5){(it*15+1..it*15+15).shuffled().take(5)};repeat(5){i->println((0..4).joinToString(" "){if(i==2&&it==2)"free"else"${c[it][i]}"})}}
I take advantage of shuffled() to randomize numbers but not have duplicates, also saves an import of Kotlin's random; aside from that I just print the columns of the 2d vector c but print free if I get to the center of the 2d vector which is c[2][2]
APL(NARS), 202 chars
next←1
r←n rndu ×ばつ⍳(w<n)∨(n≤0)∨(w≥1E9)∨w≤0⋄r←⍬
next←2147483648∣12345+ne×ばつ1103515245
t←1+w∣⌊ne×ばつ⍳0<n-←1
f←{⍪{M←⊃(×ばつ0..4)+{5 rndu ⍵} ×ばつ⍵⍴1⋄M[3;3]←⊂'free'⋄⍉'BINGO',M} ̈⍵⍴5}
//7+13+36+38+41+67 rndu would return n random numbers in 1..w without repetition, or ∅ if n>w. Reference is the function rand in the book "The C Language" Brian W. Kernignan, Dennis M. Ritchie. This seems ok even if I m not 100% sure.
test:
f 3
B I N G O
8 26 35 48 74
1 17 31 60 67
12 21 free 47 75
7 20 44 58 61
13 28 42 51 71
B I N G O
1 28 40 57 64
10 23 45 55 62
9 20 free 52 72
3 29 39 48 70
13 30 34 46 73
B I N G O
10 20 39 59 67
14 29 42 58 63
12 23 free 60 68
5 25 41 51 72
3 21 31 52 66
f 1
B I N G O
15 30 34 50 73
7 16 44 46 72
13 24 free 47 67
3 18 38 58 69
10 26 35 52 66