No 9.
A Bingo card is just a 5 x 5 matrix of random numbers. The five columns are labeled B, I, N, G and O. The first column as 5 random numbers from 1 to 15, the second from 16 to 30 and so forth, with the last column being from 61 to 75. This looks like an easy challenge for a Python program, so lets just see how we could generate 5 random numbers for the first column:
for i in range(0,5):
num = random.randint(1, 15)
print(num, end=" "
)
This looks pretty straightforward, but sometimes it prints out duplicate numbers like this one:
12 14 5 5 6
So, to prevent duplicates, we could just add the numbers to a set, which eliminates duplicates, and add them until we have five:
bset=set()
while len(bset) < 5:
num = random.randint(1, 15)
bset.add(num)
print(bset)
And this will give us sets of non-repeating numbers, such as
{1, 3, 11, 13, 14}
That looks OK, but the numbers should be in random order. And, in fact the specification for a Python set says that the numbers will be unordered. Unfortunately for us, for very small numbers Python sorts the set by the hash code for each integer, and these come out in ascending order.
So we have to take that set, convert it to a list and shuffle it:
bara = list(bset)
random.shuffle(bara)
That works, giving you a shuffled array:
[13, 7, 8, 11, 5]
Creating a Bingo column class
So now, we want to create those five columns. The cleanest way is to create a column class and then create five instances for the five columns. Here we create the class. Note that we have a getRowval method that returns those values as a string. We’ll need that when we build our card.
# Bingo column class
class BingoCol():
def __init__(self, name, min, max):
self.name = name # save the column name
bset=set() # create an empty set
while len(bset) < 5: # add random numbers
num = random.randint(min,max)
bset.add(num)
self.bara = list(bset) # convert to list
random.shuffle(self.bara) # and shuffle the numbers
# return a string value for each entry
def getRowval(self, i):
return str(self.bara[i])
Creating the columns
Now, we need to create five class instance for the five columns: This seems simple enough:
cols=[]
cols.append(BingoCol("B"
, 1, 15))
cols.append(BingoCol("I"
, 16, 30))
cols.append(BingoCol("N"
, 31, 45))
cols.append(BingoCol("G"
, 46, 60))
cols.append(BingoCol("O"
, 61, 75))
Then we print out the columns, a row at a time and have our first stab at a Bingo card. We first print out the column headers, eight spaces apart
for r in cols:
print(f"{r.name:>8s}", end="")
print()
And then the five rows themselves:
for i in range(0,5):
for r in cols:
print(f"
{r.getRowval(i):
>8s
}
"
, end=
""
)
print()
And here is our result:
B I N G O
3 19 39 52 62
13 24 45 60 63
14 26 35 51 75
5 16 32 54 74
9 22 31 56 65
Uh oh. We forgot something
Well, we have our 5 columns of random integers in the correct ranges, but if you look at an actual Bingo card, the center cell is not a number, it’s a “Free” space. How to fix this?
The simplest way is to derive a special class for the N column, which returns the text “Free” if the row index is 2 (the indices run from 0 through 4).
class NBingoCol(BingoCol):
def __init__(self, name, min,max):
super().__init__(name,min,max)# return number except for row 2
def getRowval(self, i):
if(i==2):
return "Free"
else:
return str(self.bara[i])
Note that the base BingoCol class does most of the work generating the 5 random numbers. This derived class just modifies the getRowval method to return the “Free” text if the index is 2. So here is our revised result:
B I N G O
10 19 36 56 75
4 16 33 49 72
8 30 Free 51 74
6 17 41 47 71
9 26 42 46 61
Making Bingo cards
You could create Bingo cards using the Treeview visual widget, and they look pretty reasonable.
And the code for loading the Treeview isn’t too complex. Here is most of it:
tree.heading('B',text='B')
tree.heading('I', text='I')
tree.heading('N', text='N')
tree.heading('G', text='G')
tree.heading('O', text='O')
for i in range(0, 5):
rowval=""
a=[]
for r in cols:
a.append(r.getRowval(i))
tree.insert("", 'end', text="", values=(a))
tree.pack()
But , the Treeview doesn’t allow us to create visible column lines, at least in Windows. So, we also create a really nice card by drawing the lines and text on a Canvas widget:
Here is much of that code:
EDGE=10
x = y = EDGE
WIDTH = 50
# draw grid lines
for i in range(0,7):
self.canvas.create_line(x, y, x+5*WIDTH, y, fill='black')
y=y+WIDTH
self.canvas.update()
x = y = EDGE
for i in range(0,6):
self.canvas.create_line(x, y, x, y+6*WIDTH, fill='black')
x += WIDTH
# put Text labels in top row
x = y = EDGE+WIDTH/2
for r in self.cols:
self.canvas.create_text(x, y, fill="darkblue",
font="Times 20 italic bold",
text=r.name)
x += WIDTH
# fill in numbers
x = EDGE+WIDTH/2
y = EDGE+WIDTH+WIDTH/2
for i in range(0, 5):
for r in self.cols:
self.canvas.create_text(x, y, fill="black",
font="Times 20",
text=r.getRowval(i))
x += WIDTH
x = EDGE + WIDTH / 2
y = EDGE + (i+2)*WIDTH + WIDTH / 2
At the very end, we create a filled blue box around the column labels and move it behind the drawn text:
# create a light blue rectangle
rect=self.canvas.create_rectangle(EDGE,EDGE,5*WIDTH+EDGE,
WIDTH+EDGE, fill='lightblue')
# and put the rectangle behind the text
self.canvas.tag_lower(rect)
self.canvas.update()
Now, you can print these Bingo cards by screen capture, or use can use the Canvas widget postscript method to save each card to a Postscript file.
self.canvas.postscript(file=”bcard.ps”, colormode=”color”)
Conclusions
Here is a great, simple example of the power of using classes in Python. You create five instances for the five columns, each holding their own column data, and you derive a special N-column class which will always return “Free” for the third row.
All code can be downloaded from GitHub under jameswcooper/newsletter.
Subscribe to this newsletter so you don’t miss a single Monday’s issue!
How do we get access to the github? I wasnt sure how to access the newsletter :(