Setup¶
In [3]:
from random import choice as rC
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
Functions¶
In [74]:
def bigBoard(N) :
"""
Create a list of 2N+2 lists representing the Big NxN Board. N rows, N columns, and 2 diagonals
Parameters:
N - Dimension of Big Board
Returns:
bbl - Big Board List of 2N+2 lists.
"""
#Get M² random numbers from the first N² numbers.
bbl = [[]*N]*(2*N+2)
for r in range(N) :
for c in range(N) :
num = r*N + c
#Enter random number in appropriate row list.
bbl[r] = bbl[r].copy() + [num]
#Enter random number in appropriate column list.
bbl[N+c] = bbl[N+c].copy() + [num]
#Enter number in diagonal.
if r == c :
bbl[2*N] = bbl[2*N].copy() + [num]
if r+c+1 == N :
bbl[2*N+1] = bbl[2*N+1].copy() + [num]
#Reverse the order of the last diagonal
bbl[2*N+1] = bbl[2*N+1][::-1]
return(bbl)
def lilBoard(M, N) :
"""
Create a list of 2M+2 lists representing the Lil MxM Board. M rows, M columns, and 2 diagonals
Parameters:
M - Dimension of Lil Board.
N - Dimension of Big Board.
Returns:
lbl - Lil Board List of 2M+2 lists.
"""
bbl = bigBoard(N)
squares = list(range(0, N**2))
#Get M² random numbers from the first N² numbers.
lbl = [[]*M]*(2*M+2)
for r in range(M) :
for c in range(M) :
#Lil Board creation.
rand = rC(squares)
squares.remove(rand)
#Enter random number in appropriate row list.
lbl[r] = lbl[r].copy() + [rand]
#Enter random number in appropriate column list.
lbl[M+c] = lbl[M+c].copy() + [rand]
#Enter number in diagonal.
if r == c :
lbl[2*M] = lbl[2*M].copy() + [rand]
if r+c+1 == M :
lbl[2*M+1] = lbl[2*M+1].copy() + [rand]
#Big Board destruction. If the random number is in one of the lists in
#Big Board, clear that list.
for l in range(len(bbl)) :
if rand in bbl[l] :
bbl[l] = []
#Big Board cleanup. Remove empty lists.
bbl = [sl for sl in bbl if sl != []]
lbl[2*M+1] = lbl[2*M+1][::-1]
return(lbl, bbl)
def playAsymmetricBingo(lbl, bbl, K, M, N) :
"""
Determine the result of K trials of a lbl & bbl Asymmetric Bingo game.
Parameters:
lbl - Lil Board list of lists.
bbl - Big Board list of lists.
K - number of trials.
Returns:
ev - Expected value of points you would gain.
"""
ev = 0
count = 0
#Sequence of M or N in a row, respectively.
LIL_BINGO = [1]*M
BIG_BINGO = [1]*N
#Numbers on the Little Board and
#*useful numbers on the Big Board.
LIL_LIST = [i for sublist in lbl for i in sublist]
BIG_LIST = [i for sublist in bbl for i in sublist]
for k in range(K) :
#Initiate the marker boards on top of the Little Board and Big Board.
#For each iteration that follows, a 1 will be placed in the corresponding
#location of a random number called out.
lil_potential = [[]*M]*(2*M+2)
big_potential = [[]*M]*(len(bbl))
squares = list(range(0, N**2))
for n in range(N**2) :
rand = rC(squares)
squares.remove(rand)
#Is the new random number in a useful line on the big bingo board?
if rand in BIG_LIST :
#For each useful line in the big bingo board...
for b in range(len(bbl)) :
#If new random number is in that line, update potential.
if rand in bbl[b] :
big_potential[b] = big_potential[b].copy() + [1]
#That line could be bingo!
if big_potential[b] == BIG_BINGO :
count += 1
#No score update!
break
#Break again, if true.
if big_potential[b] == BIG_BINGO :
count += 1
#No score update!
break
#Is the new random number in the little bingo board?
elif rand in LIL_LIST :
#For each line in the little bingo board...
for m in range(2*M+2) :
#If new random number is in that line, update potential.
if rand in lbl[m] :
lil_potential[m] = lil_potential[m].copy() + [1]
#That line could be bingo!
if lil_potential[m] == LIL_BINGO :
ev += 1/K
count += 1
break
#Break again, if true.
if lil_potential[m] == LIL_BINGO :
count += 1
#No score update!
break
#The new random number is in a nonuseful space on the big bingo board.
else :
pass
return(ev)
def monteCarlo(J, K, M, N) :
"""
Determine the result of K trials of J lbl & bbl Asymmetric Bingo.
Parameters:
J - number of games.
K - number of trials per game.
M - Dimension of Lil Board.
N - Dimension of Big Board.
Returns:
ev - Expected value of points you would gain.
"""
bingo = 0
for i in range(J) :
lbl, bbl = lilBoard(M, N)
#Big board may have no winning moves left.
if bbl == [] :
bingo += 1
#But if it does...
elif K != 0:
bingo += playAsymmetricBingo(lbl, bbl, K, M, N)
return(bingo/J)
monteCarlo(1000000, 0, 5, 8)
Out[74]:
0.768803
In [3]:
monteCarlo(1000000, 1000, 5, 8)
Out[3]:
0.9915768839999936
In [11]:
M = []
N = []
Z = []
for m in range(2, 11) :
for n in range(m, 13) :
#Lil Board has annihilated Big Board.
if m == n :
z = 1
else :
z = monteCarlo(10000, 2000, m, n)
M.append(m)
N.append(n)
Z.append(z)
print(m, n, z)
2 2 1 2 3 0.8985031999999604 2 4 0.855537949999962 2 5 0.8574679999999593 2 6 0.8704201999999581 2 7 0.8857126499999506 2 8 0.9001836499999551 2 9 0.9128208499999593 2 10 0.9236308999999594 2 11 0.9329776999999584 2 12 0.9407672499999459 3 3 1 3 4 0.9816528499999897 3 5 0.9416706499999634 3 6 0.920546549999956 3 7 0.9136119999999502 3 8 0.9155759499999585 3 9 0.9208201999999533 3 10 0.9269709499999553 3 11 0.9339633999999533 3 12 0.9404953499999475 4 4 1 4 5 0.997557749999999 4 6 0.9850222999999867 4 7 0.9708415499999684 4 8 0.96126454999995 4 9 0.9565198499999479 4 10 0.9549583499999474 4 11 0.9555362499999402 4 12 0.9575705999999456 5 5 1 5 6 0.9997935499999998 5 7 0.9968296499999962 5 8 0.9915901999999891 5 9 0.9862413499999789 5 10 0.9814396999999617 5 11 0.9784805999999564 5 12 0.9764621999999413 6 6 1 6 7 0.9999884 6 8 0.999453049999999 6 9 0.9980123499999967 6 10 0.9955823999999908 6 11 0.9928570499999821 6 12 0.9909379999999681 7 7 1 7 8 1.0 7 9 0.99992925 7 10 0.9995439499999998 7 11 0.9987577499999967 7 12 0.9977273999999939 8 8 1 8 9 1.0 8 10 1.0 8 11 0.9998778999999995 8 12 0.9997090499999988 9 9 1 9 10 1.0 9 11 1.0 9 12 0.99997605 10 10 1 10 11 1.0 10 12 1.0
In [75]:
data = pd.DataFrame({'M': M,
'N': N,
'Z': Z})
data.loc[(data['Z'] > 0.9998) & (data['M'] != data['N']), 'Z'] = 0.9999
In [78]:
def heatMapHelper():
fig = plt.figure(figsize = (10, 7))
ax = fig.add_subplot(xlim = (1.5, 12.5),
ylim = (1.5, 10.5))
heatmap = plt.scatter(x = data['N'],
y = data['M'],
c = data['Z'],
cmap = 'viridis_r',
marker = "s",
s = 1700,
alpha = 0.8,
vmin = data['Z'].min(),
vmax = data['Z'].max())
#Title setup.
ax.set_title("Asymmetric Bingo‽", fontsize = 24)
ax.set_xlabel("Opponent Board Dimension", fontsize = 18)
ax.set_ylabel("Your Board Dimension", fontsize = 18)
ax.set_facecolor('#999999FF')
#Borders between cells.
for i in range(2, 13) :
ax.axhline(y = i-0.5, xmin = 0, xmax = 12, c = 'k', lw = 1)
ax.axvline(x = i-0.5, ymin = 0, ymax = 10, c = 'k', lw = 1)
#Print P in each territory
for row in data.iterrows():
plt.annotate(round(row[1]['Z'], 4),
(row[1]['N'], row[1]['M']),
c = "k",
fontsize = 10,
ha = "center",
va = "center")
#Colorbar.
cb = plt.colorbar(heatmap, format = '%.3f')
#cb.set_ticklabels(labels)
cb.set_label('Probability of Winning',
labelpad = -95,
rotation = 90,
fontsize = 20)
cb.ax.tick_params(labelsize = 16)
fig.savefig("2026.09.04EC.png",
bbox_inches = 'tight')
In [79]:
heatMapHelper()