Assignment

Analyzing Cards

1 Description
For thisassessment, you may use your own Deck class, or the one provided on Moodle, just as long as it allows for thedrawing and returning of cards to the deck.
The purpose of this assessment is to check your understanding of coding in context of a problem, and yourability to read and understand external libraries such as NumPy, MatPlotLib, and Pandas.

1.1 Proving Fairness
A deck is considered balanced if there is an equal chance at drawing a card. Thus, because the deck was created
by us, we must prove that it is indeed fair. Write code that draws from a deck that contains cards from 1 to
King in four suits (heart, diamond, spade, and club), saves the face value of that card and then returns it to
the deck.
Do this 1000 times and show that the mean average of the draw is around 6.5-7.5.
The mean average is obtained by the following formula:
mean = sum(Face Values Drawn) / Number of Attempts                                                      (1)
1.2 Chances of Flush
Inpoker there is a hand called a “royal flush.” A royal flush is a hand of 10, Jack, Queen, King, and Ace all
in hearts. A royal flush cannot be beaten by any other hand, and so your task is to discover the chances of
obtaining a royal flush if you draw 5 cards in a row.
Use the same technique from 1.1 to discover the chances of drawing a royal flush, except use the equation:
probability = Number of Successes / Number of Attempts                                              (2)
You may find that the number of trials needs to be more than 1000 to show a result. The more trials you
do, the more accurate your result, however it takes more time for each trial you attempt.
You will need to attempt these 1000 trials multiple times, and then take the average of the probability found
from all the sets of trials. How many times is up to you.

1.3 Change in Chance
Lastly you are to run the experiment from section 1.2 when the amount of suits increases from 1 to 10. Each
set of trials must contain the same number of trials for this to work.
You should obtain a list of chances from 1 to 10.
1.4 The Report
The final product for this assignment is a report that details your findings from the sections above. In order to
communicate what you have found you will need to make graphs using MatPlotLib. For sections 1.1 and 1.2,
you may wish to demonstrate your  findings via a bar graph of the different card tallies. For section 1.3 you will
need to make a scatter plot to show how time changes as suit number increases.
You must make at least 1 graph for each section, but you can feel freeto make any that provides useful information, and any amount of them.

 card.py

class Card:

def __init__(self,faceValue,suitType):

self.face = faceValue

self.suit = suitType

defgetFace(self):

returnself.face

defgetSuit(self):

returnself.suit

def __str__(self):

returnstr(self.getFace()) + “:” + str(self.getSuit())

 deck.py

 from card import Card

from random import seed,randint

class Deck:

def __init__(self, valueStart, valueEnd, numSuits):

self.pile = []

self.size = 0

values = []

i = valueStart

while i <= valueEnd:

values.append(i)

i += 1

i = 0

while i <numSuits:

for value in values:

newCard = Card(value,i)

self.pile.append(newCard)

self.size += 1

i += 1

def __str__(self):

newString = “”

for card in self.pile:

newString += (str(card) + ‘,’)

newString = newString[:-1]

returnnewString

def __len__(self):

returnself.size

#adds an item at location where

defaddCard(self, card, where):

if where > -1 and where <= self.size:

self.pile = self.pile[:where] + [card] + self.pile[where:]

self.size += 1

else:

print(“I can’t add there.”)

#draw a card from the top of the deck

defdrawCard(self):

item = self.pile.pop()

self.size -= 1

return item

defplaceCardTop(self, card):

self.addCard(card, self.size)

defplaceCardBottom(self,card):

self.addCard(card, 0)

def shuffle(self,**kwargs):

newPile = []

if “seed” in kwargs:

seed(kwargs[“seed”])

whilelen(self.pile) > 0:

item = randint(0, len(self.pile)-1)

newPile.append(self.pile[item])

del(self.pile[item])

self.pile = newPile

def main():

new_deck = Deck(1, 3, 2)

print(new_deck)

testCard = Card(6,6)

new_deck.placeCardTop(testCard)

new_deck.placeCardBottom(testCard)

print(new_deck)

new_deck.shuffle()

print(new_deck)

if __name__ == “__main__”:

main()

 Solution

 make_plots.py

 importmatplotlib.pyplot as pl

importnumpy as np

# # part1 # #

data = np.array([6.98, 7.07, 6.84, 7.01, 7.06, 7.12, 7.06, 6.97, 6.96, 6.96])

ind = np.arange(data.size)

pl.ylim(6.5, 7.5)

pl.xticks([])

pl.title(‘The Mean Face Values Drawn from the Deck for 10 Runs’)

pl.axhline(7, ls=’–‘, color=’k’)

pl.bar(ind, data, color=’y’)

pl.savefig(‘plot1.pdf’, bbox_inches=’tight’)

pl.close()

# # part2 # #

pairProbs = np.array([0.4832, 0.4926, 0.4986, 0.5054, 0.484, 0.5026, 0.4948, 0.507, 0.4906, 0.4958])

ind = np.arange(pairProbs.size)

pl.ylim(0.45, 0.55)

pl.xticks([])

pl.title(‘The Chances of Obtaining a Pair’)

pl.axhline(pairProbs.mean(), ls=’–‘, color=’k’)

pl.bar(ind, pairProbs, color=’r’)

pl.savefig(‘plot2.pdf’, bbox_inches=’tight’)

pl.close()

flushProbs = np.array([0.0020, 0.0030, 0.0012, 0.0022, 0.0020, 0.0024, 0.0022, 0.0028, 0.0014, 0.0012, 0.0023])

ind = np.arange(flushProbs.size)

pl.ylim(0.001, 0.003)

pl.xticks([])

pl.title(‘The Chances of Obtaining a Flush’)

pl.axhline(flushProbs.mean(), ls=’–‘, color=’k’)

pl.bar(ind, flushProbs, color=’g’)

pl.savefig(‘plot3.pdf’, bbox_inches=’tight’)

pl.close()

print(pairProbs.mean())

print(flushProbs.mean())

# # part3 # #

suits = np.arange(1, 11)

pairProbs = [0.0, 0.37649999999999995, 0.45728, 0.48828000000000005, 0.51762, 0.5233399999999999, 0.5344200000000001, 0.5437999999999998, 0.54744, 0.55028]

flushProbs = [1.00000, 0.03878, 0.00640, 0.00202, 0.00058, 0.00018, 0.00022, 0.00012, 0.00016, 0.00004]

pl.xlabel(“Number of Suits”)

pl.ylabel(“Probability”)

pl.ylim(0, 0.7)

pl.title(‘The Chances of Obtaining a Pair’)

pl.scatter(suits, pairProbs, s=50, color=’r’)

pl.savefig(‘plot5.pdf’, bbox_inches=’tight’)

pl.close()

pl.xlabel(“Number of Suits”)

pl.ylabel(“Probability”)

pl.title(‘The Chances of Obtaining a Flush’)

pl.ylim(1e-5, 1)

pl.yscale(‘log’)

pl.scatter(suits, flushProbs, s=50, color=’g’)

pl.savefig(‘plot6.pdf’, bbox_inches=’tight’)

pl.close()

part1.py

 from deck import Deck

defmeanFaceValue(deck, n):

# sum of values drawn

total = 0

for i in range(n):

# shuffle before drawing a card

deck.shuffle()

# draw from the deck

card = deck.drawCard()

# accumulate face values drawn

total = total + card.getFace()

# returns card to the deck

deck.placeCardBottom(card)

mean = total / n

return mean

def main():

n = 1000  # the number of attempts

# a deck with cards from 1 to King in 4 suits

d = Deck(1, 13, 4)

# compute the mean

mean = meanFaceValue(d, n)

print(“The mean face value of {} draws is {:.2f}”.format(n, mean))

if __name__ == ‘__main__’:

main()

part2.py

from deck import Deck

defisPair(drawn):

# make a list of all possible pairs combined from 5 drawn cards

face_pairs = []

for i in range(len(drawn)):

for j in range(i + 1, len(drawn)):

face_pairs.append((drawn[i].getFace(), drawn[j].getFace()))

# check if we have at least one pair with the same face values

havePair = False

for pair in face_pairs:

if pair[0] == pair[1]:

havePair = True

break

returnhavePair

defisFlush(drawn):

haveFlush = True

# the suit of the first card drawn

suit = drawn[0].getSuit()

for card in drawn:

# check if all cards drawn have the same suit

ifcard.getSuit() != suit:

haveFlush = False

break   # no need to continue

returnhaveFlush

defisRoyalFlush(drawn):

# make sure we have a flush

ifisFlush(drawn):

if drawn[0].getSuit() != 1: # shold be flush of hearts only

return False

# get a list of cards’ face values

faces = [card.getFace() for card in drawn]

faces.sort()        # sort face values in ascending order

return faces == [1, 10, 11, 12, 13]  # from 10 to Ace

else:

return False

defchanceOfHand(deck, predicate, n):

# sum of values drawn

count = 0

for i in range(n):

# shuffle before drawing a card

deck.shuffle()

drawn = []

# draw 5 cards from the top

for i in range(5):

card = deck.drawCard()

drawn.append(card)

if predicate(drawn):

count = count + 1

# put cards back to the deck

for i in range(5):

card = drawn[i]

deck.placeCardBottom(card)

prob = count / n

returnprob

def main():

n = 5000  # the number of attempts

# a deck with cards from 1 to King in 4 suits

d = Deck(1, 13, 4)

pairProb = 0

print(“The chance of drawing a Pair”)

for k in range(10):

pr = chanceOfHand(d, isPair, n)

pairProb += pr

print(“Run {} out of 10: {}”.format(k + 1, pr))

pairProb = pairProb / k

flushProb = 0

print(“The chance of drawing a Flush”)

for k in range(10):

pr = chanceOfHand(d, isFlush, n)

flushProb += pr

print(“Run {} out of 10: {}”.format(k + 1, pr))

flushProb = flushProb / k

royalFlushProb = 0

print(“The chance of drawing a Royal Flush”)

for k in range(10):

pr = chanceOfHand(d, isRoyalFlush, 200*n)

royalFlushProb += pr

print(“Run {} out of 10: {}”.format(k + 1, pr))

royalFlushProb = royalFlushProb / k

print(“”)

print(“The chance of drawing a Pair        is {:.4f}”.format(pairProb))

print(“The chance of drawing a Flush       is {:.4f}”.format(flushProb))

print(“The chance of drawing a Royal Flush is {:.4f}”.format(royalFlushProb))

if __name__ == ‘__main__’:

main() 

part3.py 

from deck import Deck

defisPair(drawn):

# make a list of all possible pairs combined from 5 drawn cards

face_pairs = []

for i in range(len(drawn)):

for j in range(i + 1, len(drawn)):

face_pairs.append((drawn[i].getFace(), drawn[j].getFace()))

# check if we have at least one pair with the same face values

havePair = False

for pair in face_pairs:

if pair[0] == pair[1]:

havePair = True

break

returnhavePair

defisFlush(drawn):

haveFlush = True

# the suit of the first card drawn

suit = drawn[0].getSuit()

for card in drawn:

# check if all cards drawn have the same suit

ifcard.getSuit() != suit:

haveFlush = False

break   # no need to continue

returnhaveFlush

defisRoyalFlush(drawn):

# make sure we have a flush

ifisFlush(drawn):

if drawn[0].getSuit() != 1: # shold be flush of hearts only

return False

# get a list of cards’ face values

faces = [card.getFace() for card in drawn]

faces.sort()        # sort face values in ascending order

return faces == [1, 10, 11, 12, 13]  # from 10 to Ace

else:

return False

defchanceOfHand(deck, predicate, n):

# sum of values drawn

count = 0

for i in range(n):

# shuffle before drawing a card

deck.shuffle()

drawn = []

# draw 5 cards from the top

for i in range(5):

card = deck.drawCard()

drawn.append(card)

if predicate(drawn):

count = count + 1

# put cards back to the deck

for i in range(5):

card = drawn[i]

deck.placeCardBottom(card)

prob = count / n

returnprob

defmeanProb(deck, predicate, K, nTimes):

prob = 0

for k in range(nTimes):

prob += chanceOfHand(deck, predicate, K)

returnprob / nTimes

def main():

n = 5000  # the number of attempts

# a deck with cards from 1 to King in 4 suits

pairProbs = []

flushProbs = []

royalFlushProbs = []

print(“Num Suits  Pair”)

fornSuits in range(1, 11):

d = Deck(1, 13, nSuits)

pr = meanProb(d, isPair, n, 10)

pairProbs.append(pr)

print(“{:9d}  {}”.format(nSuits, pr))

print(“Num Suits  Flush”)

fornSuits in range(1, 11):

d = Deck(1, 13, nSuits)

pr = meanProb(d, isFlush, n, 10)

flushProbs.append(pr)

print(“{:9d}  {}”.format(nSuits, pr))

print(“Num Suits  Royal Flush”)

fornSuits in range(1, 11):

d = Deck(1, 13, nSuits)

pr = meanProb(d, isRoyalFlush, 200*n, 10)

royalFlushProbs.append(pr)

print(“{:9d}  {}”.format(nSuits, pr))

if __name__ == ‘__main__’:

main() 

report.pdf 

  1. Proving Fairness
    The mean average of the draw for 10 experiments is show on the bar plot below. All the mean average values are close to 7 and fit into 6.5-7.5 range. This result proves that the deck generated it is indeed fair. The outcome of the experiment is close to 7 because it is expected value of the uniform distribution in 1-13 range (which corresponds to the card faces from 1 to King). 
  1. 2. Chances of Hands
    The probability of drawing a Pair and a Flush from the top 5 cards of the deck is studied. For each hand 10 experiments were carried with 5000 trials each. The mean values for each of those hands probabilities are the following:
    Pr(Pair) = 0:495, Pr(Flush) = 0: The outcomes of each of 10 experiments are shown on the corresponding bar plots.
    The chances of drawing a Pair are quite high (nearly 50%) because there are many different card combinations which result in a Pair.
  1. 3. Change in Chance
    The experiment similar to the one in part 2 was carried but for the number of suits in 1-10 range. The scatter plots for those experiments show interesting results. In particular, the chance of getting a pair increases from zero (for a single suit, where no pair possible) with a logarithmic dependence on the number of suits.
    On the other hand, a chance of getting a Flush decreases exponentially with the number on suits starting from 100% probability (for a single suit).