Skip to content

Instantly share code, notes, and snippets.

@jsyang
Created October 20, 2011 18:57
Show Gist options
  • Save jsyang/1301975 to your computer and use it in GitHub Desktop.
Save jsyang/1301975 to your computer and use it in GitHub Desktop.
crown and anchor heuristics
'''
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