Some routines to explore poker hands. Two of my own scripts - "mystats" and "WS" - are referenced but are not necessary to the basic functionality of these routines.
Start with basics - load some utility routines, basic definitions.
NB.* poker.ijs: functions to run poker simulations. PKRDIR=: '\amisc\work\AgentBasedSimulation\' load 'stats plot mystats WS' RANK=: (]&.>'23456789'),'10';'J';'Q';'K';'A' SUIT=: 'CDHS'
First thing to do is read in the basic globals or create them.
3 : 0 ''
if. fexist 'RH5.DAT' do.
pvars=. 'RANK';'SUIT';'HANDRATED';'RH5'
;0{&>(<1!:43 '')unfileVar_WS_ &.>pvars
else. initPokerVars '' end.
)The global variables here take a while (15 minutes or more) to compute, so we may store them in files to read in more quickly.
NB.* getPokerVars: get poker variables from file.
getPokerVars=: 3 : 0
vnms=. 'RH5i';'HRi';'HRbase'
NB. RH5i: SZ$integer vec: base 52 5-card hands ordered from worst to best.
NB. HRi: SZ$integer vec: (0{"1) type of hand (0-8); (1{"1) subtype when
NB. expanded by (0,HRbase)#:HRi.
(<PKRDIR) unfileVar_WS_&.>vnms
SZ=: #RH5i
if. 0~:#vnms do. smoutput 'Vars created: ','.',~e2l vnms;'SZ' end.
qts ''
)"RANK" and "SUIT" should be obvious; "RH5" is all possible hands, a (SZ,5) table of integers in order from weakest to strongest; "HANDTYPE" is (SZ,2) matrix of types - column 0 is the type (0-8: nothing to straight flush) and column 1 is the sub-ranking within a given type (lower number is weaker hand).
Following are some basic routines for playing games very simply and accumulating list of winning hands.
NB.* buildHR: build hands ranking mat: 0{hand type (0-8); 1{sub-type.
buildHR=: 3 : 0
npnt=. 2 9$1020 384 144 64 1020 4 24 4 4 1277 2860 858 858 10 1277 156 156 10
HR=. ((*/npnt)#i.9),:;(0{npnt)#&.>i.&.>1{npnt
)
whmax0col=: ((=>./)@([:;0&{"1)) NB. Where are all = highest # in col0.
tallyMany=: 3 : 0
'ndp ng'=. y NB. Number of different players, number of games with each number of players.
wins=: 0 2$a:
for_np. ndp do. 'tt pp wins1M'=: tallyWinningHands np,ng
wins=: wins,np;wins1M
end.
wins
)
tallyWinningHands=: 3 : 0
'np ng'=. y NB. Number of players, number of games
winner=. 0 0$0
while. _1<ng=. <:ng do.
best=. play1FullGame np
whmax=. whmax0col best NB. Narrow to best hand type ties.
rating=. rateHandI"1 >whmax#1{"1 best
whr=. \:rating NB. Rate ties with tie-breaking
't b'=. (split whr){&.><rating NB. sub-type->Top 1 & Bottoms.
istie=. t e. b NB. Check if any tied with top.
ww=. whr#~1,t*./ . =|:b NB. Where ties are in best hands.
bix=. ww{b2i whmax NB. Where ties are in all hands.
hc=. bix{HANDS NB. Hole cards,
fr=. rating{~ww NB. final hand rating,
NB. fh=. >1{"1 best{~bix NB. final hand value,
NB. winner=. winner,hc,.fr,.fh,.istie NB. is tie or not.
winner=. winner,hc,.fr,.istie NB. is tie or not.
end.
NB. ptn=. 1 0 1 0 1 0 0 0 0 1
NB. tit=. 'Hole';'Type';'Hand';'Tie'
ptn=. 1 0 1 0 1
tit=. 'Hole';'Type';'Tie'
tit;ptn;winner
NB.EG 'titles ptn winner'=. tallyWinningHands 5 1000
)
play1FullGame=: 3 : 0
np=. y NB. Number of players per game
DECK=: 52?52
maxp=. <.-:5-~#DECK
if. np<1 do. np=. maxp end. NB. Default to max possible if np<1
assert. np<maxp
HANDS=: |:(2,np)$DECK
COMMON=: _5{.DECK
ixs=. 3 4 AllCombos&.>5
fillout=. ixs{&.><COMMON
best=. fillout findBestPoss"1 HANDS
)
makeAll2Combo5=: 3 : 0
'apc h2'=. y NB. All possible 3-cards combos; 4-card combos.
allposs=. h2,"1 >0{apc NB. Combine w/all 3-card combos->5 card hands
allposs=. allposs,,/h2 ,"(0 1)/>1{apc NB. w/all 4-card->5 card hands
)
findBestPoss2=: 4 : 0
whb=. 0{\:ratings=. rateHandI"1 ap=. makeAll2Combo5 x;y
whb{&.>ratings;<ap
)
findBestPoss=: 4 : 0
NB.* findBestPoss: find best possible combination of 2 card hand+all poss.
NB. combos of common cards.
allposs=. makeAll2Combo5 x;y
best=. bestHand"1 allposs NB. Best of each possibility
top=. >./best
whbest=. allposs#~best=top
best=. whbest{~(i.>./)rankWithinHandtype whbest
top;best NB. Best handtype overall and which hand
)Following are rating functions.
strictord=: 3 : '(/:|:|.suitRank y){y'
rateHandI=: 3 : '0 2861#:HRi{~RH5i i. 52#.strictord y'
rateHand=: 3 : 'HANDRATED{~"1 RH5 i. strictord y'
bestHand=: 3 : 0
if. isNothing y do. 0 return. end.
if. isPair y do. 1 return. end.
if. is2Pair y do. 2 return. end.
if. is3ok y do. 3 return. end.
if. isOnlyStraight y do. 4 return. end.
if. isOnlyFlush y do. 5 return. end.
if. isFullHouse y do. 6 return. end.
if. is4ok y do. 7 return. end.
if. isStraightFlush y do. 8 return. end.
_1
)
lookAtSomeRanks=: 3 : 0
allhigh2=. ~.,(,&.>/)~i.52
allhigh2=. /:~allhigh2
allhigh2=. allhigh2#~;~:/&.>allhigh2
allhigh2=. 52 51$allhigh2
aa=. >handRank01&.>5{."(1)13 14 15 16 17{allhigh2
aa;<allhigh2 NB. "aa" should be symmetric.
)
handRank01=: 3 : 0
mean EQHANDRANK#~possHandDist y
)For a given partial (less than 5-card) hand, "possHandDist" gives a SZ length boolean of all possible hands.
possHandDist=: 3 : 0"1
NB.* possHandDist: distribution of possibilities for a partial hand.
NB. myc=. 0{"1 HANDS NB. e.g. 13 28 37
whmine=. (#y)=(|:RH5) +/ . e. y
NB. plot whmine*%+/whmine
NB. pd 'save \GTAAP\distAfter2cards.wmf 1900 1400'
NB. max=. SZ%~SZ-4 NB. This is max rank because 4 hands tie for highest.
)
showCards=: 3 : 0
NB.* showCards: show char version of cards (e.g. 'AD';'10S') or vice
NB.* versa if not numeric.
if. isNum y do. NB. Num->char
if. 2~:#$y do. y=. suitRank y end.
(RANK{~1{y),&.>SUIT{~0{y
else. (SUIT i. ;{:&.>y),:(,&.>RANK) i. }:&.>y NB. char->num
end.
NB.EG showCards 3 7 20 34 24
NB.EG showCards '5C';'9C';'9D';'10H';'KD' NB. 5 Clubs, 9 Clubs, etc.
)
showWhichHand=: 3 : 0
hh=. showCards y{ORDH5
hs=. y{"1 HANDRATED
hh=. hh,(HANDTYPE{~0{hs),<1{hs
)
rankAllHands=: 3 : 0
NB.* rankAllHands: rank all hands by type of hand and within type.
'whall h5'=. y
whall=. |:whall
allranks=. (2,#h5)$_1
for_hctr. i.#whall do.
ht=. hctr{whall
subrank=. rankWithinHandtype ht#h5
NB. Row 0 is hand type; 1 is rank within type
allranks=. ((ht*hctr)+(-.ht)*0{allranks) 0}allranks
allranks=. (ht}(1{allranks),:(ht exp subrank)) 1}allranks
end.
NB. Fix wheels: zero-out subtype rank of straights and straight flushes
NB. that are wheels (A-5) so they rate lower than 6-highs.
as=. 13*i.4 NB. All suits
whs=. (0{allranks)e. 4 8 NB. Straights and straight-flushes
whsa=. whs*.((0{"1 h5)e. as)*.(4{"1 h5)e.12+as NB. with a 2 and an Ace
allranks=. ((-.whsa)*1{allranks) 1}allranks NB. Make these subtype 0
NB. since this is lowest straight because the one case where Ace=1.
)
rankWithinHandtype=: 3 : 0
hrnk=. 1{1 0 2|:suitRank"1 y
NB. "hr" is ,2 col. mat: col. 0 is count (how many dupes), col. 1 is card;
NB. so hand is rated by value of which card has most duplicates, value of
NB. which card has 2nd most dupes, etc.
hr=. (,@\:~@|:@(~.,:~((+/@|:)@=)))"(1) hrnk
gv=. /:hr
whdif=. 1,2(+./ . ~:)/\gv{hrnk
subrank=. (/:gv){+/\whdif
)
rankWithinNothing=: 3 : 0
NB.* rankWithinNothing: assign rank number for nothing hand.
nothings=. whno#h5
rn=. suitRank"1 nothings{~/:nothings
rn=. 1{1 0 2|:rn
gv=. /:|."1 rn
whdif=. 2(+./ . ~:)/\gv{rn
subrank=. (/:gv){+/\1,whdif
allranks=. (2,#h5)$_1
NB. Row 0 is hand type; 1 is rank within type
allranks=. ((0*whno)+(-.whno)*0{allranks) 0}allranks
allranks=. (whno}(1{allranks),:(whno exp subrank)) 1}allranks
)
NB. 6!:2 'whall=. (isNothing"1 h5),.(isPair"1 h5),.(is2Pair"1 h5),.(is3ok"1 h5),.(isOnlyStraight"1 h5),.(isOnlyFlush"1 h5),.(isFullHouse"1 h5),.(is4ok"1 h5),.isStraightFlush"1 h5'Basic hand-ranking functions follow.
isNothing=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
'suit rank'=. /:~&.><"1 y
NB. >1 suit, and no duplicates,
rc=. (1~:#~.suit)*.(#rank)=#~.rank
NB. and >1 apart, including special case of low Ace.
rc=. rc*.-.(1*./ . =2-~/\rank)+.1*./ . =2-~/\/:~(rank=12)}rank,:_1
)
isAnyFlush=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
*./2=/\0{y
)
isOnlyFlush=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
(isAnyFlush y)*.-.isAnyStraight y NB. Not straight flush
)
isAnyStraight=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
rc=. _1 *./ . =2-/\/:~rnk=. 1{y
if. 12 e. rnk do.
rc=. rc +. _1 *./ . =2-/\/:~(rnk=12)}rnk,:_1
end.
rc
)
isOnlyStraight=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
(isAnyStraight y)*.-.isAnyFlush y NB. Not straight flush
)
isPair=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
NB. 1=+/2=/\/:~1{y
1 1 1 2-:/:~;#&.>(1,2~:/\/:~rnk)<;.1 rnk=. 1{y
)
is2Pair=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
NB. 2=#~.rnk#~0,~2=/\rnk=. /:~1{y
1 2 2-:/:~;#&.>(1,2~:/\/:~rnk)<;.1 rnk=. 1{y
)
is3ok=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
1 1 3-:/:~;#&.>(1,2~:/\/:~rnk)<;.1 rnk=. 1{y
)
isFullHouse=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
NB. 0 1 1 1-:/:~2=/\/:~1{y
2 3-:/:~;#&.>(1,2~:/\/:~rnk)<;.1 rnk=. 1{y
)
is4ok=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
1 4-:/:~;#&.>(1,2~:/\/:~rnk)<;.1 rnk=. 1{y
)
isStraightFlush=: 3 : 0
if. 2~:#$y do. y=. suitRank y end.
(isAnyFlush y)*.isAnyStraight y NB. Is straight and flush.
)
NB. suitRank=: |:@((4 13)&#:)
suitRank=: 3 : 0
NB.* suitRank: convert nums: 2 rows: suit, rank or 2 rows->nums 0-51.
if. 2=#$y do.
13#.|:y
else.
|:@((4 13)&#:)y
end.
)
tallyPairs=: 3 : 0
'hpi ni'=. y NB. Hands per iteration, Number of iterations
cts=. ni$0
for_ii. i.ni do.
hands=. >/:~&.>13|&.>5?&.>hpi$52
cts=. (+/1=+/"1(}."1 hands)=}:"1 hands) ii}cts
end.
cts
)Create the large globals used in the above routines if they weren't already created and written to files.
NB.* initPokerVars: initialize poker variables from scratch.
initPokerVars=: 3 : 0
h5=: 5 AllCombos 52
h5=: h5{"1~/:"(1) 13|h5 NB. Order each hand by card rank.
6!:2 'whall=. (isNothing"1 h5),.(isPair"1 h5),.(is2Pair"1 h5),.(is3ok"1 h5),.(isOnlyStraight"1 h5),.(isOnlyFlush"1 h5),.(isFullHouse"1 h5),.(is4ok"1 h5),.isStraightFlush"1 h5'
handRanks=: rankAllHands whall;<h5
handGV=. /:|:handRanks
RH5i=: 52#.rh5=. handGV{h5
NB. CHORDH5=: rh5{a. NB. base-52 integer vec more compact.
handRated=. handGV{&.|:handRanks
HRi=: (HRbase=: >./,handRated)#.handRated NB. 2860=HRbase
equihand=. -.*./"(1) 0,2=/\|:handRated
EQUIRANK=: +/\equihand
lens=. 2-~/\b2i equihand,1
EQHANDRANK=: lens#b2i equihand
'RH5i';'HRi';'HRbase';'EQUIRANK';'EQHANDRANK'
)
exp=: #^:_1
3 : 0 ''
if. fexist 'RH5I.DAT' do.
getPokerVars ''
else. initPokerVars '' end.
)Some examples usage follows.
egs=: 0 : 0
showCards"1 (5$52)#:_3{.RH5i NB. Ranked hands as base-52 integer vector.
+---+--+--+--+--+
|10D|JD|QD|KD|AD|
+---+--+--+--+--+
|10H|JH|QH|KH|AH|
+---+--+--+--+--+
|10S|JS|QS|KS|AS|
+---+--+--+--+--+
showCards"1 (5$52)#:3{.RH5i NB. 3 lowest hands
+--+--+--+--+--+
|2C|3C|4C|5C|7D|
+--+--+--+--+--+
|2C|3C|4C|5C|7H|
+--+--+--+--+--+
|2C|3C|4C|5C|7S|
+--+--+--+--+--+
2 5{.HANDRATED NB. 5 lowest rated hands: subtype 1 nothings
0 0 0 0 0
1 1 1 1 1
2 _5{.HANDRATED NB. 5 highest hands: straight flushes, subtypes 9 & 10.
8 8 8 8 8
9 10 10 10 10
3 3$HANDTYPE NB. 0{HANDRATED indexes into HANDTYPE
+-----------+-----------+--------------+
|nothing |pair |2-pair |
+-----------+-----------+--------------+
|3-of-a-kind|straight |flush |
+-----------+-----------+--------------+
|full house |4-of-a-kind|straight flush|
+-----------+-----------+--------------+
$~.|:HANDRATED NB. Number of distinct hand type, subtypes
7464 2
>./|:HANDRATED
8 2860
0 2861#:_5{.HRi NB. Int vec version of HANDRATED: same relative ranking
8 8
8 10
8 10
8 10
8 10
$HR=: |:0 2861#: HRi
2 2598960
+/htp=. 1,2~:/\0{HR NB. Hand-type partition
9
]nht=. ;$&.>htp<;.(1)1{HR NB. number of each hand type
1302540 1098240 123552 54912 10200 5108 3744 624 40
]npt=. ;($@~.)&.>htp<;.(1) 1{HR NB. number subtypes per hand type
1277 2860 858 858 10 1277 156 156 10
pcts=. 100*&.>1E_7 roundNums pht,.(+/\pht=. nht%+/nht),.|.+/\(|.nht)%+/nht
pcts=. pcts,.]&.>0.1 roundNums %pht
htstats=. HANDTYPE,.]&.>nht,.npt,.>pcts
tit=. 'Hand Type';'# Type';'# Sub-type';'% Type';'Cum. %';'Rev. Cum. %';'Type/Hands'
tots=. ('Total';]&.>+/>_3}."1}."1 htstats),3$<' - '
tit,htstats,tots
+--------------+-------+----------+--------+--------+-----------+----------+
|Hand Type |# Type |# Sub-type|% Type |Cum. % |Rev. Cum. %|Type/Hands|
+--------------+-------+----------+--------+--------+-----------+----------+
|nothing |1302540|1277 |50.11774|50.11774|100 |2 |
+--------------+-------+----------+--------+--------+-----------+----------+
|pair |1098240|2860 |42.2569 |92.37464|49.88226 |2.4 |
+--------------+-------+----------+--------+--------+-----------+----------+
|2-pair |123552 |858 |4.7539 |97.12854|7.62536 |21 |
+--------------+-------+----------+--------+--------+-----------+----------+
|3-of-a-kind |54912 |858 |2.11285 |99.24139|2.87146 |47.3 |
+--------------+-------+----------+--------+--------+-----------+----------+
|straight |10200 |10 |0.39246 |99.63385|0.75861 |254.8 |
+--------------+-------+----------+--------+--------+-----------+----------+
|flush |5108 |1277 |0.19654 |99.83039|0.36615 |508.8 |
+--------------+-------+----------+--------+--------+-----------+----------+
|full house |3744 |156 |0.14406 |99.97445|0.16961 |694.2 |
+--------------+-------+----------+--------+--------+-----------+----------+
|4-of-a-kind |624 |156 |0.02401 |99.99846|0.02555 |4165 |
+--------------+-------+----------+--------+--------+-----------+----------+
|straight flush|40 |10 |0.00154 |100 |0.00154 |64974 |
+--------------+-------+----------+--------+--------+-----------+----------+
|Total |2598960|7462 |100 | - | - | - |
+--------------+-------+----------+--------+--------+-----------+----------+
)