Created
October 20, 2011 18:57
-
-
Save jsyang/1301975 to your computer and use it in GitHub Desktop.
crown and anchor heuristics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
Crown and Anchor (spinning wheel version) | |
Photo of the wheel: http://www.jackofallspades.ca/wheel-rentals.php | |
symbols = [ "diamonds", "clubs", "hearts", "spade", "crowns", "anchors" ] | |
We'll use the indices to refer to the suit. | |
Players bet ($0.50 to $2.00) on a symbol. Once all bets are placed, spinner is spun. | |
Banker pays out the bet to the player who bet on a symbol that appeared on the wheel. | |
Bet $0.50 on 2. Spun 224. | |
2 appears 2 times, player gets $0.50*2 for payout. | |
Bet $1.00 on 4. Spun 126. | |
4 appears 0 times, player gets nothing, loses bet amount. | |
Normally this game is played with three 6-sided dice, in this instance the die rolls | |
are substituted with a spinner which has combinations arranged on it as follows: | |
28 outcomes on spinner, 14 unique outcomes for a semi-circle (180 degrees). | |
In sequence, this is a circular linked-list of length 28: | |
'000', '544', '222', '332', '555', '100', '444', '152', '304', '111', '322', '554', '333', '110', | |
Repeat sequence for next 14. | |
Spin # of unique symbols | |
000 1 | |
544 2 | |
222 1 | |
332 2 | |
555 1 | |
100 2 | |
444 1 | |
152 3 <-_____ weird stuff | |
304 3 <-' | |
111 1 | |
322 2 | |
554 2 | |
333 1 | |
110 2 | |
Any bet gives us a positive payout if we hit at least 1 symbol on the spin. So, | |
spins with more unique symbols are the safer bets than spins with less. But we're | |
not exactly gambling for the sake of playing it safe now, are we? | |
Now, onto the modelling: | |
By observing a few rounds, we can make out a few potential parameters that will | |
determine a spin of the next round given the previous round. | |
Each spin position takes up 12 degrees of the wheel. Assuming the spinning of the | |
wheel each round is within a consistency of +/- 45 degrees (90 degrees range) from | |
the last position, we can get at least a starting heuristic for choosing what symbol | |
to bet on next, given the last spin. | |
The one which sticks out is to gather the expected payouts of the symbols based | |
on the last position and choose the symbol with the highest payout. | |
''' | |
import random | |
import numpy | |
spins = ('000', '544', '222', '332', '555', '100', '444', '152', '304', '111', '322', '554', '333', '110') | |
def nextSpin( bias, stddev ): | |
''' | |
Generate next spin's index from normal with | |
bias = last spin's index | |
stddev = stddev of spinning | |
''' | |
return int(round(random.normalvariate(bias, stddev)))%12; | |
def payout( outcome, bet, amount ): | |
n = 0 | |
for i in outcome: | |
if (i == str(bet)): n+=1 | |
if n > 0: return n*amount | |
return -amount | |
def playNrounds( N, bank, heuristic, stddev ): | |
''' | |
Play a game of {N} rounds, starting with ${bank}, using | |
{heuristic} to decide which symbol to pick next, with | |
nextSpin(lastSpinIndex,stddev) as the next outcome. | |
''' | |
wins = 0 | |
stopped = 0 | |
lastSpinIndex = random.randint(0,len(spins)) | |
for n in xrange(N): | |
bet = heuristic( lastSpinIndex, bank ) | |
lastSpinIndex = nextSpin( lastSpinIndex, stddev ) | |
lastPayout = payout( spins[lastSpinIndex], bet["symbol"], bet["amount"] ) | |
bank += lastPayout | |
if(lastPayout>=0): wins+=1 | |
if bank<=0: | |
stopped = n+1 | |
break | |
# Ran out of money. Lost the game before N rounds. | |
if(stopped): return {"rate":float(wins)/stopped*100, "bank":bank, "stopped":stopped} | |
return {"rate":float(wins)/N*100, "bank":bank, "stopped":stopped} | |
''' | |
Heuristics for choosing next symbol | |
''' | |
# Naives. | |
def alwaysChooseDiamonds(*args): | |
return {"symbol":0, "amount":1} | |
def alwaysChooseClubs(*args): | |
return {"symbol":1, "amount":1} | |
def alwaysChooseHearts(*args): | |
return {"symbol":2, "amount":1} | |
def alwaysChooseSpades(*args): | |
return {"symbol":3, "amount":1} | |
def alwaysChooseCrown(*args): | |
return {"symbol":4, "amount":1} | |
def alwaysChooseAnchor(*args): | |
return {"symbol":5, "amount":1} | |
# Expectation Table | |
spinHeuristic = [] | |
def buildHeuristicTable(stddevSpin): | |
global spinHeuristic | |
sd=int(2*stddevSpin) # 95% confidence interval of next spin | |
pOutcome=1/(2*float(sd)+1) # P(an outcome in the interval) | |
# Index of symbol to choose next, based on highest expected payout, | |
# given the last outcome. | |
nextBetSymbol=[0.0]*len(spins) | |
for i in xrange(len(spins)): | |
expectedPayout=[0]*6 | |
for j in range(i-sd,i+sd): | |
k=j%len(spins) | |
for n in xrange(6): | |
expectedPayout[n]+=payout(spins[k],n,1) | |
nextBetSymbol[i]=expectedPayout.index(max(expectedPayout)) | |
spinHeuristic=nextBetSymbol | |
def useTable(lastSpinIndex,bank): | |
return {"symbol":spinHeuristic[lastSpinIndex], "amount":1} | |
def useTableAnchorBetHigh(lastSpinIndex,bank): | |
amountToBet=1 | |
if(spinHeuristic[lastSpinIndex]==5): amountToBet=2 | |
return {"symbol":spinHeuristic[lastSpinIndex], "amount":amountToBet} | |
def useTableLowPass(lastSpinIndex,bank): | |
amountToBet=[0,0,2,0,2,2] | |
return {"symbol":spinHeuristic[lastSpinIndex], "amount":amountToBet[spinHeuristic[lastSpinIndex]]} | |
def useTableHighPass(lastSpinIndex,bank): | |
amountToBet=[0.5,0.5,0,0.5,0,0] | |
return {"symbol":spinHeuristic[lastSpinIndex], "amount":amountToBet[spinHeuristic[lastSpinIndex]]} | |
def useRandom(*args): | |
return {"symbol":random.randint(0,6), "amount":1} | |
# *************** Batch runs *************** | |
def playNgamesMrounds(N,M,bank,heuristic,stddev): | |
games=[playNrounds(M,bank,heuristic,stddev) for i in range(N)] | |
winrates=[] | |
endbanks=[] | |
losses=0 | |
for g in games: | |
winrates.append(g["rate"]) | |
endbanks.append(g["bank"]) | |
if(g["stopped"]>0): losses+=1 | |
print "%(name)022s %(meanwin)#2.2f%%,%(stdwin)03.2f%% %(meanend)02.2f %(lossrate)02.2f" % \ | |
{"name":heuristic.__name__,\ | |
"meanwin":numpy.average(winrates),\ | |
"stdwin":numpy.std(winrates),\ | |
"meanend":numpy.average(endbanks),\ | |
"lossrate":float(losses)/N*100} | |
# *************** run! *************** | |
# use standard deviation of 2 spots from last spin. | |
buildHeuristicTable(2) | |
print " Mean, Std.Dev. Mean " | |
print " Heuristic Winrate Ending$ Loss Rate" | |
print "______________________________________________________________" | |
playNgamesMrounds(1000,20,20,useRandom,2) | |
print "______________________________________________________________" | |
playNgamesMrounds(1000,20,20,alwaysChooseDiamonds,2) | |
playNgamesMrounds(1000,20,20,alwaysChooseClubs,2) | |
playNgamesMrounds(1000,20,20,alwaysChooseHearts,2) | |
playNgamesMrounds(1000,20,20,alwaysChooseSpades,2) | |
playNgamesMrounds(1000,20,20,alwaysChooseCrown,2) | |
playNgamesMrounds(1000,20,20,alwaysChooseAnchor,2) | |
print "______________________________________________________________" | |
playNgamesMrounds(1000,20,20,useTable,2) | |
playNgamesMrounds(1000,20,20,useTableAnchorBetHigh,2) | |
playNgamesMrounds(1000,20,20,useTableLowPass,2) | |
playNgamesMrounds(1000,20,20,useTableHighPass,2) | |
''' | |
A typical result: | |
Mean, Std.Dev. Mean | |
Heuristic Winrate Ending$ Loss Rate | |
______________________________________________________________ | |
useRandom 24.90%,9.97% 13.43 0.50 | |
______________________________________________________________ | |
alwaysChooseDiamonds 24.65%,9.66% 14.80 0.80 | |
alwaysChooseClubs 24.93%,12.81% 13.28 2.40 | |
alwaysChooseHearts 32.99%,10.53% 18.14 0.10 | |
alwaysChooseSpades 25.16%,10.53% 11.74 0.70 | |
alwaysChooseCrown 32.87%,11.45% 18.08 0.00 | |
alwaysChooseAnchor 33.47%,10.56% 18.41 0.00 | |
______________________________________________________________ | |
useTable 29.70%,10.70% 17.38 0.10 | |
useTableAnchorBetHigh 30.00%,10.73% 17.02 1.80 | |
useTableLowPass 71.59%,11.45% 18.59 1.20 <<<< | |
useTableHighPass 57.77%,11.72% 18.96 0.00 | |
''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment