import random
import multiprocessing
import math
import time
# configuration options
simulations = 6000000
num_decks = 4
shuffle_perc = 75
def simulate(queue, batch_size):
deck = []
def new_deck():
std_deck = [
# 2 3 4 5 6 7 8 9 10 J Q K A
2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11,
2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11,
2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11,
2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11,
]
# add more decks
std_deck = std_deck * num_decks
random.shuffle(std_deck)
return std_deck[:]
def play_hand():
dealer_cards = []
player_cards = []
# deal initial cards
player_cards.append(deck.pop(0))
dealer_cards.append(deck.pop(0))
player_cards.append(deck.pop(0))
dealer_cards.append(deck.pop(0))
# deal player to 12 or higher
while sum(player_cards) < 12:
player_cards.append(deck.pop(0))
# deal dealer on soft 17
while sum(dealer_cards) < 18:
exit = False
# check for soft 17
if sum(dealer_cards) == 17:
exit = True
# check for an ace and convert to 1 if found
for i, card in enumerate(dealer_cards):
if card == 11:
exit = False
dealer_cards[i] = 1
if exit:
break
dealer_cards.append(deck.pop(0))
p_sum = sum(player_cards)
d_sum = sum(dealer_cards)
# dealer bust
if d_sum > 21:
return 1;
# dealer tie
if d_sum == p_sum:
return 0;
# dealer win
if d_sum > p_sum:
return -1;
# dealer lose
if d_sum < p_sum:
return 1
# starting deck
deck = new_deck()
# play hands
win = 0
draw = 0
lose = 0
for i in range(0, batch_size):
# reshuffle cards at shuffle_perc percentage
if (float(len(deck)) / (52 * num_decks)) * 100 < shuffle_perc:
deck = new_deck()
# play hand
result = play_hand()
# tally results
if result == 1:
win += 1
if result == 0:
draw += 1
if result == -1:
lose += 1
# add everything to the final results
queue.put([win, draw, lose])
start_time = time.time()
print("start!")
# simulate
cpus = multiprocessing.cpu_count()
batch_size = int(math.ceil(simulations / float(cpus)))
queue = multiprocessing.Queue()
# create n processes
processes = []
for i in range(0, cpus):
process = multiprocessing.Process(target=simulate, args=(queue, batch_size))
processes.append(process)
process.start()
# wait for everything to finish
for proc in processes:
proc.join()
finish_time = time.time() - start_time
# get totals
win = 0
draw = 0
lose = 0
for i in range(0, cpus):
results = queue.get()
win += results[0]
draw += results[1]
lose += results[2]
print
print (' cores used: %d' % cpus)
print (' total simulations: %d' % simulations)
print (' simulations/s: %d' % (float(simulations) / finish_time))
print (' execution time: %.2fs' % finish_time)
print (' win percentage: %.2f%%' % ((win / float(simulations)) * 100))
print (' draw percentage: %.2f%%' % ((draw / float(simulations)) * 100))
print (' lose percentage: %.2f%%' % ((lose / float(simulations)) * 100))
print
aW1wb3J0IHJhbmRvbQppbXBvcnQgbXVsdGlwcm9jZXNzaW5nCmltcG9ydCBtYXRoCmltcG9ydCB0aW1lCiAKIyBjb25maWd1cmF0aW9uIG9wdGlvbnMKc2ltdWxhdGlvbnMgPSA2MDAwMDAwCm51bV9kZWNrcyA9IDQKc2h1ZmZsZV9wZXJjID0gNzUKIApkZWYgc2ltdWxhdGUocXVldWUsIGJhdGNoX3NpemUpOgogICAgZGVjayA9IFtdCiAKICAgIGRlZiBuZXdfZGVjaygpOgogICAgICAgIHN0ZF9kZWNrID0gWwogICAgICAgICAgIyAyICAzICA0ICA1ICA2ICA3ICA4ICA5ICAxMCAgSiAgIFEgICBLICAgQQogICAgICAgICAgICAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5LCAxMCwgMTAsIDEwLCAxMCwgMTEsCiAgICAgICAgICAgIDIsIDMsIDQsIDUsIDYsIDcsIDgsIDksIDEwLCAxMCwgMTAsIDEwLCAxMSwKICAgICAgICAgICAgMiwgMywgNCwgNSwgNiwgNywgOCwgOSwgMTAsIDEwLCAxMCwgMTAsIDExLAogICAgICAgICAgICAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5LCAxMCwgMTAsIDEwLCAxMCwgMTEsCiAgICAgICAgXQogCiAgICAgICAgIyBhZGQgbW9yZSBkZWNrcwogICAgICAgIHN0ZF9kZWNrID0gc3RkX2RlY2sgKiBudW1fZGVja3MKIAogICAgICAgIHJhbmRvbS5zaHVmZmxlKHN0ZF9kZWNrKQogCiAgICAgICAgcmV0dXJuIHN0ZF9kZWNrWzpdCiAKICAgIGRlZiBwbGF5X2hhbmQoKToKICAgICAgICBkZWFsZXJfY2FyZHMgPSBbXQogICAgICAgIHBsYXllcl9jYXJkcyA9IFtdCiAKICAgICAgICAjIGRlYWwgaW5pdGlhbCBjYXJkcwogICAgICAgIHBsYXllcl9jYXJkcy5hcHBlbmQoZGVjay5wb3AoMCkpCiAgICAgICAgZGVhbGVyX2NhcmRzLmFwcGVuZChkZWNrLnBvcCgwKSkKICAgICAgICBwbGF5ZXJfY2FyZHMuYXBwZW5kKGRlY2sucG9wKDApKQogICAgICAgIGRlYWxlcl9jYXJkcy5hcHBlbmQoZGVjay5wb3AoMCkpCiAKICAgICAgICAjIGRlYWwgcGxheWVyIHRvIDEyIG9yIGhpZ2hlcgogICAgICAgIHdoaWxlIHN1bShwbGF5ZXJfY2FyZHMpIDwgMTI6CiAgICAgICAgICAgIHBsYXllcl9jYXJkcy5hcHBlbmQoZGVjay5wb3AoMCkpCiAKICAgICAgICAjIGRlYWwgZGVhbGVyIG9uIHNvZnQgMTcKICAgICAgICB3aGlsZSBzdW0oZGVhbGVyX2NhcmRzKSA8IDE4OgogICAgICAgICAgICBleGl0ID0gRmFsc2UKICAgICAgICAgICAgIyBjaGVjayBmb3Igc29mdCAxNwogICAgICAgICAgICBpZiBzdW0oZGVhbGVyX2NhcmRzKSA9PSAxNzoKICAgICAgICAgICAgICAgIGV4aXQgPSBUcnVlCiAgICAgICAgICAgICAgICAjIGNoZWNrIGZvciBhbiBhY2UgYW5kIGNvbnZlcnQgdG8gMSBpZiBmb3VuZAogICAgICAgICAgICAgICAgZm9yIGksIGNhcmQgaW4gZW51bWVyYXRlKGRlYWxlcl9jYXJkcyk6CiAgICAgICAgICAgICAgICAgICAgaWYgY2FyZCA9PSAxMToKICAgICAgICAgICAgICAgICAgICAgICAgZXhpdCA9IEZhbHNlCiAgICAgICAgICAgICAgICAgICAgICAgIGRlYWxlcl9jYXJkc1tpXSA9IDEKIAogICAgICAgICAgICBpZiBleGl0OgogICAgICAgICAgICAgICAgYnJlYWsKIAogICAgICAgICAgICBkZWFsZXJfY2FyZHMuYXBwZW5kKGRlY2sucG9wKDApKQogCiAgICAgICAgcF9zdW0gPSBzdW0ocGxheWVyX2NhcmRzKQogICAgICAgIGRfc3VtID0gc3VtKGRlYWxlcl9jYXJkcykKIAogICAgICAgICMgZGVhbGVyIGJ1c3QKICAgICAgICBpZiBkX3N1bSA+IDIxOgogICAgICAgICAgICByZXR1cm4gMTsKICAgICAgICAjIGRlYWxlciB0aWUKICAgICAgICBpZiBkX3N1bSA9PSBwX3N1bToKICAgICAgICAgICAgcmV0dXJuIDA7CiAgICAgICAgIyBkZWFsZXIgd2luCiAgICAgICAgaWYgZF9zdW0gPiBwX3N1bToKICAgICAgICAgICAgcmV0dXJuIC0xOwogICAgICAgICMgZGVhbGVyIGxvc2UKICAgICAgICBpZiBkX3N1bSA8IHBfc3VtOgogICAgICAgICAgICByZXR1cm4gMQogCiAgICAjIHN0YXJ0aW5nIGRlY2sKICAgIGRlY2sgPSBuZXdfZGVjaygpCiAKICAgICMgcGxheSBoYW5kcwogICAgd2luID0gMAogICAgZHJhdyA9IDAKICAgIGxvc2UgPSAwCiAgICBmb3IgaSBpbiByYW5nZSgwLCBiYXRjaF9zaXplKToKICAgICAgICAjIHJlc2h1ZmZsZSBjYXJkcyBhdCBzaHVmZmxlX3BlcmMgcGVyY2VudGFnZQogICAgICAgIGlmIChmbG9hdChsZW4oZGVjaykpIC8gKDUyICogbnVtX2RlY2tzKSkgKiAxMDAgPCBzaHVmZmxlX3BlcmM6CiAgICAgICAgICAgIGRlY2sgPSBuZXdfZGVjaygpCiAKICAgICAgICAjIHBsYXkgaGFuZAogICAgICAgIHJlc3VsdCA9IHBsYXlfaGFuZCgpCiAKICAgICAgICAjIHRhbGx5IHJlc3VsdHMKICAgICAgICBpZiByZXN1bHQgPT0gMToKICAgICAgICAgICAgd2luICs9IDEKICAgICAgICBpZiByZXN1bHQgPT0gMDoKICAgICAgICAgICAgZHJhdyArPSAxCiAgICAgICAgaWYgcmVzdWx0ID09IC0xOgogICAgICAgICAgICBsb3NlICs9IDEKIAogICAgIyBhZGQgZXZlcnl0aGluZyB0byB0aGUgZmluYWwgcmVzdWx0cwogICAgcXVldWUucHV0KFt3aW4sIGRyYXcsIGxvc2VdKQogCiAKc3RhcnRfdGltZSA9IHRpbWUudGltZSgpCiAKcHJpbnQoInN0YXJ0ISIpICAKIAojIHNpbXVsYXRlCmNwdXMgPSBtdWx0aXByb2Nlc3NpbmcuY3B1X2NvdW50KCkKYmF0Y2hfc2l6ZSA9IGludChtYXRoLmNlaWwoc2ltdWxhdGlvbnMgLyBmbG9hdChjcHVzKSkpCiAKcXVldWUgPSBtdWx0aXByb2Nlc3NpbmcuUXVldWUoKQogCiMgY3JlYXRlIG4gcHJvY2Vzc2VzCnByb2Nlc3NlcyA9IFtdCiAKZm9yIGkgaW4gcmFuZ2UoMCwgY3B1cyk6CiAgICBwcm9jZXNzID0gbXVsdGlwcm9jZXNzaW5nLlByb2Nlc3ModGFyZ2V0PXNpbXVsYXRlLCBhcmdzPShxdWV1ZSwgYmF0Y2hfc2l6ZSkpCiAgICBwcm9jZXNzZXMuYXBwZW5kKHByb2Nlc3MpCiAgICBwcm9jZXNzLnN0YXJ0KCkKIAojIHdhaXQgZm9yIGV2ZXJ5dGhpbmcgdG8gZmluaXNoCmZvciBwcm9jIGluIHByb2Nlc3NlczoKICAgIHByb2Muam9pbigpCiAKZmluaXNoX3RpbWUgPSB0aW1lLnRpbWUoKSAtIHN0YXJ0X3RpbWUKIAoKIAojIGdldCB0b3RhbHMKd2luID0gMApkcmF3ID0gMApsb3NlID0gMAogCmZvciBpIGluIHJhbmdlKDAsIGNwdXMpOgogICAgcmVzdWx0cyA9IHF1ZXVlLmdldCgpCiAgICB3aW4gKz0gcmVzdWx0c1swXQogICAgZHJhdyArPSByZXN1bHRzWzFdCiAgICBsb3NlICs9IHJlc3VsdHNbMl0KIApwcmludApwcmludCAoJyAgY29yZXMgdXNlZDogJWQnICUgY3B1cykKcHJpbnQgKCcgIHRvdGFsIHNpbXVsYXRpb25zOiAlZCcgJSBzaW11bGF0aW9ucykKcHJpbnQgKCcgIHNpbXVsYXRpb25zL3M6ICVkJyAlIChmbG9hdChzaW11bGF0aW9ucykgLyBmaW5pc2hfdGltZSkpCnByaW50ICgnICBleGVjdXRpb24gdGltZTogJS4yZnMnICUgZmluaXNoX3RpbWUpCnByaW50ICgnICB3aW4gcGVyY2VudGFnZTogJS4yZiUlJyAgJSAoKHdpbiAvIGZsb2F0KHNpbXVsYXRpb25zKSkgKiAxMDApKQpwcmludCAoJyAgZHJhdyBwZXJjZW50YWdlOiAlLjJmJSUnICUgKChkcmF3IC8gZmxvYXQoc2ltdWxhdGlvbnMpKSAqIDEwMCkpCnByaW50ICgnICBsb3NlIHBlcmNlbnRhZ2U6ICUuMmYlJScgJSAoKGxvc2UgLyBmbG9hdChzaW11bGF0aW9ucykpICogMTAwKSkKcHJpbnQK