## Tuesday, July 21, 2009

### Python for Simulation: Poker

Here is a poker simulation I developed in introducing Python to my Bioinformatics class. The cards are simply strings of the form `'SP4' or 'DI11'`. Evaluation of a hand of 5 cards proceeds by testing first for pairs or higher (using a dictionary keyed by the card ranks), then a test for straights and finally flush.

Here is are the results from 1E6 trials:

 `1000000 trials:('12HT 9HT 10HT 11HT 13HT', 'straight flush')(' 9HT 12HT 11HT 13HT 10HT', 'straight flush')(' 8HT 6HT 7HT 10HT 9HT', 'straight flush')('12DI 11DI 1DI 13DI 10DI', 'straight flush')(' 4DI 5DI 6DI 8DI 7DI', 'straight flush')(' 5DI 6DI 3DI 2DI 4DI', 'straight flush')(' 9HT 6HT 8HT 7HT 10HT', 'straight flush')(' 7HT 10HT 11HT 8HT 9HT', 'straight flush')(' 4CL 3CL 2CL 6CL 5CL', 'straight flush')('10DI 13DI 11DI 12DI 1DI', 'straight flush')(' 8SP 10SP 11SP 12SP 9SP', 'straight flush')(' 5DI 4DI 6DI 7DI 8DI', 'straight flush')('10HT 9HT 8HT 11HT 12HT', 'straight flush')(' 6CL 5CL 2CL 3CL 4CL', 'straight flush') 501903 - 422728 two 46969 two twos 21266 three 3527 straight 1936 - flush 1431 full house 226 four 14 straight flush`

The total number of possible hands is:

 `52 "choose" 5 = 52! / (47! x 5!) = 52*51*50*49*48 / 5*4*3*2 = 311875200 / 120 = 2,598,960`

A simulation of 1E7 hands should be enought to test the statistics. For the run of 1E6 above, we saw 14 straight flushes where the expected number is 8*4 / 2.6 = 12. (Note: I didn't bother to explicitly mark a royal flush---two are seen in the output, also about as expected).

Python scripts are so simple to write, and this is seductive but dangerous. It is a temptation to put as little effort into testing as you did programming. I showed the demo 6 months ago, but only this morning found a bug in my code to test for straights!

Here is the whole listing:

 `import random,sysN = [str(n) for n in range(1,14)]suits = ['SP','HT','CL','DI']Deck = [n+s for n in N for s in suits]def count(H): D = dict() L = [s[:-2] for s in H] for n in L: try: D[n] += 1 except KeyError: D[n] = 1 return Ddef straight(H): # depends on no pairs or higher L = [int(s[:-2]) for s in H] L.sort() # ace high if L[0] == 1: if L == [1,10,11,12,13]: return True else: # we need else if L[-1] - L[0] == 4: return True return Falsedef flush(H): L = [s[-2:] for s in H] return len(list(set(L))) == 1def evaluate(H): D = count(H) v = D.values() n = len(D.values()) if n == 5: s = '-' if n == 4: s = 'two' if n == 3: if 3 in v: s = 'three' else: s = 'two twos' if n == 2: if 4 in v: s = 'four' else: s = 'full house' # don't test if has >= pair if s == '-' and straight(H): s = 'straight' if flush(H): s += ' flush' return s def oneHand(): random.shuffle(Deck) H = Deck[:5] pL = [s.rjust(4) for s in H] return ' '.join(pL),evaluate(H) def test(N=100): for i in range(N): print oneHand() sys.exit()#test()def simulate(N=int(1E6)): print N, 'trials:' D = dict() for i in range(N): t = oneHand() s = t[1] try: D[s] += 1 except KeyError: D[s] = 1 if 'flush' in s: if 'straight' in s: print t print def f(k): return D[k] for k in reversed(sorted(D.keys(),key=f)): print str(D[k]).rjust(len(str(N))),ksimulate()`

Larif sofiene said...

Nice work Tom the python has all the right tools to do good simulation especially its numeric precision (nupy,scipy library),on a project for molecular modeling and simulation i got until 10 decimal !!!!!.
Talking about simulation's,on my blog i did a DNA Replication, Transcription and Translation Simulator(very simple one) with the very basic Python commands :
http://bioticcomputer.blogspot.com/

Miklos Andor said...

+1 bug:
if hand is: A,2,3,4,5 your straight says it is nothing.
I suggest change this line in def straight(H)
if L == [1,10,11,12,13]: return True
to:
if L == [1,10,11,12,13] or L == [1,2,3,4,5]: return True

However nice code!
BR
M

telliott99 said...

Thanks Miklos. I'm not a poker player (or even a card player). I've only played games with "Aces high", not low, but I see either one is legal for poker.