# monte_carlo_with_cards.py

# We know from math that if you deal 3 cards from a shuffled deck,
# the odds of getting a pair (where exactly 2 of the 3 match in rank,
# so not all 3 match in rank) is 3744/22100, or 0.1694.
# Here, we will assume we don't know that, and we will use
# Monte Carlo methods to simulate dice rolls and demonstrate this result.

import random

def getUnshuffledDeck():
    deck = [ ]
    for suit in 'CDHS':
        for rank in 'A23456789TJQK':
            card = rank + suit
            deck.append(card)
    return deck

def getShuffledDeck():
    deck = getUnshuffledDeck()
    random.shuffle(deck)
    return deck

def trialSucceeds():
    deck = getShuffledDeck()
    cards = [ deck.pop(), deck.pop(), deck.pop() ]
    ranks = [ cards[0][0], cards[1][0], cards[2][0] ]
    if ((ranks[0] == ranks[1]) and (ranks[1] == ranks[2])):
        # we got trips (3 of a kind)
        return False
    else:
        return ((ranks[0] == ranks[1]) or
                (ranks[1] == ranks[2]) or
                (ranks[0] == ranks[2]))

def oddsOfGettingAPairWithThreeCards(trials=100):
    successes = 0
    for trial in range(trials):
        if trialSucceeds() == True:
            successes += 1
    return successes / trials

trials = 100
answer = oddsOfGettingAPairWithThreeCards(trials)
realAnswer = 3744/22100 # from math

print('Estimating the odds of getting at a pair (and not trips) in 3 cards')
print('Using', trials, 'trials (the more, the better)')
print('Computed answer (from Monte Carlo):', answer)
print('Actual answer (from math):', realAnswer)
print('Accuracy:',1 - abs(answer - realAnswer)/realAnswer)
if (trials < 10000):
    print('*** Increase the # of trials to get more accuracy! ***')
