]> gitweb.factorcode.org Git - factor.git/blob - extra/poker/poker.factor
Merge branch 'master' of git://factorcode.org/git/factor
[factor.git] / extra / poker / poker.factor
1 ! Copyright (c) 2009 Aaron Schaefer. All rights reserved.
2 ! The contents of this file are licensed under the Simplified BSD License
3 ! A copy of the license is available at http://factorcode.org/license.txt
4 USING: accessors arrays ascii binary-search combinators kernel locals math
5     math.bitwise math.combinatorics math.order poker.arrays random sequences
6     sequences.product splitting ;
7 IN: poker
8
9 ! The algorithm used is based on Cactus Kev's Poker Hand Evaluator with
10 ! the Senzee Perfect Hash Optimization:
11 !     http://www.suffecool.net/poker/evaluator.html
12 !     http://www.senzee5.com/2006/06/some-perfect-hash.html
13
14 <PRIVATE
15
16 ! Bitfield Format for Card Values:
17
18 !     +-------------------------------------+
19 !     | xxxbbbbb bbbbbbbb ssssrrrr xxpppppp |
20 !     +-------------------------------------+
21 !       xxxAKQJT 98765432 CDHSrrrr xxpppppp
22 !     +-------------------------------------+
23 !     | 00001000 00000000 01001011 00100101 |  King of Diamonds
24 !     | 00000000 00001000 00010011 00000111 |  Five of Spades
25 !     | 00000010 00000000 10001001 00011101 |  Jack of Clubs
26
27 ! p = prime number value of rank (deuce = 2, trey = 3, four = 5, ..., ace = 41)
28 ! r = rank of card (deuce = 0, trey = 1, four = 2, ..., ace = 12)
29 ! s = bit turned on depending on suit of card
30 ! b = bit turned on depending on rank of card
31 ! x = bit turned off, not used
32
33 CONSTANT: CLUB     8
34 CONSTANT: DIAMOND  4
35 CONSTANT: HEART    2
36 CONSTANT: SPADE    1
37
38 CONSTANT: DEUCE  0
39 CONSTANT: TREY   1
40 CONSTANT: FOUR   2
41 CONSTANT: FIVE   3
42 CONSTANT: SIX    4
43 CONSTANT: SEVEN  5
44 CONSTANT: EIGHT  6
45 CONSTANT: NINE   7
46 CONSTANT: TEN    8
47 CONSTANT: JACK   9
48 CONSTANT: QUEEN  10
49 CONSTANT: KING   11
50 CONSTANT: ACE    12
51
52 CONSTANT: STRAIGHT_FLUSH   0
53 CONSTANT: FOUR_OF_A_KIND   1
54 CONSTANT: FULL_HOUSE       2
55 CONSTANT: FLUSH            3
56 CONSTANT: STRAIGHT         4
57 CONSTANT: THREE_OF_A_KIND  5
58 CONSTANT: TWO_PAIR         6
59 CONSTANT: ONE_PAIR         7
60 CONSTANT: HIGH_CARD        8
61
62 CONSTANT: SUIT_STR { "C" "D" "H" "S" }
63
64 CONSTANT: RANK_STR { "2" "3" "4" "5" "6" "7" "8" "9" "T" "J" "Q" "K" "A" }
65
66 CONSTANT: VALUE_STR { "Straight Flush" "Four of a Kind" "Full House" "Flush"
67     "Straight" "Three of a Kind" "Two Pair" "One Pair" "High Card" }
68
69 : card-rank-prime ( rank -- n )
70     RANK_STR index { 2 3 5 7 11 13 17 19 23 29 31 37 41 } nth ;
71
72 : card-rank ( rank -- n )
73     {
74         { "2" [ DEUCE ] }
75         { "3" [ TREY  ] }
76         { "4" [ FOUR  ] }
77         { "5" [ FIVE  ] }
78         { "6" [ SIX   ] }
79         { "7" [ SEVEN ] }
80         { "8" [ EIGHT ] }
81         { "9" [ NINE  ] }
82         { "T" [ TEN   ] }
83         { "J" [ JACK  ] }
84         { "Q" [ QUEEN ] }
85         { "K" [ KING  ] }
86         { "A" [ ACE   ] }
87     } case ;
88
89 : card-suit ( suit -- n )
90     {
91         { "C" [ CLUB    ] }
92         { "D" [ DIAMOND ] }
93         { "H" [ HEART   ] }
94         { "S" [ SPADE   ] }
95     } case ;
96
97 : card-rank-bit ( rank -- n )
98     RANK_STR index 1 swap shift ;
99
100 : card-bitfield ( rank rank suit rank -- n )
101     {
102         { card-rank-bit 16 }
103         { card-suit 12 }
104         { card-rank 8 }
105         { card-rank-prime 0 }
106     } bitfield ;
107
108 :: (>ckf) ( rank suit -- n )
109     rank rank suit rank card-bitfield ;
110
111 : >ckf ( str -- n )
112     #! Cactus Kev Format
113     >upper 1 cut (>ckf) ;
114
115 : parse-cards ( str -- seq )
116     " " split [ >ckf ] map ;
117
118 : flush? ( cards -- ? )
119     HEX: F000 [ bitand ] reduce 0 = not ;
120
121 : rank-bits ( cards -- q )
122     0 [ bitor ] reduce -16 shift ;
123
124 : lookup ( cards table -- value )
125     [ rank-bits ] dip nth ;
126
127 : map-product ( seq quot -- n )
128     [ 1 ] 2dip [ dip * ] curry [ swap ] prepose each ; inline
129
130 : prime-bits ( cards -- q )
131     [ HEX: FF bitand ] map-product ;
132
133 : perfect-hash-find ( q -- value )
134     #! magic to convert a hand's unique identifying bits to the
135     #! proper index for fast lookup in a table of hand values
136     HEX: E91AAA35 +
137     dup -16 shift bitxor
138     dup   8 shift w+
139     dup  -4 shift bitxor
140     [ -8 shift HEX: 1FF bitand adjustments-table nth ]
141     [ dup 2 shift w+ -19 shift ] bi
142     bitxor values-table nth ;
143
144 : hand-value ( cards -- value )
145     dup flush? [ flushes-table lookup ] [
146         dup unique5-table lookup dup 0 > [ nip ] [
147             drop prime-bits perfect-hash-find
148         ] if
149     ] if ;
150
151 : >card-rank ( card -- str )
152     -8 shift HEX: F bitand RANK_STR nth ;
153
154 : >card-suit ( card -- str )
155     {
156         { [ dup 15 bit? ] [ drop "C" ] }
157         { [ dup 14 bit? ] [ drop "D" ] }
158         { [ dup 13 bit? ] [ drop "H" ] }
159         [ drop "S" ]
160     } cond ;
161
162 : hand-rank ( value -- rank )
163     {
164         { [ dup 6185 > ] [ drop HIGH_CARD ] }        ! 1277 high card
165         { [ dup 3325 > ] [ drop ONE_PAIR ] }         ! 2860 one pair
166         { [ dup 2467 > ] [ drop TWO_PAIR ] }         !  858 two pair
167         { [ dup 1609 > ] [ drop THREE_OF_A_KIND ] }  !  858 three-kind
168         { [ dup 1599 > ] [ drop STRAIGHT ] }         !   10 straights
169         { [ dup 322 > ]  [ drop FLUSH ] }            ! 1277 flushes
170         { [ dup 166 > ]  [ drop FULL_HOUSE ] }       !  156 full house
171         { [ dup 10 > ]   [ drop FOUR_OF_A_KIND ] }   !  156 four-kind
172         [ drop STRAIGHT_FLUSH ]                      !   10 straight-flushes
173     } cond ;
174
175 : card>string ( card -- str )
176     [ >card-rank ] [ >card-suit ] bi append ;
177
178 PRIVATE>
179
180 TUPLE: hand
181     { cards sequence }
182     { value integer initial: 9999 } ;
183
184 M: hand <=> [ value>> ] compare ;
185 M: hand equal?
186     over hand? [ [ value>> ] bi@ = ] [ 2drop f ] if ;
187
188 : <hand> ( str -- hand )
189     parse-cards dup hand-value hand boa ;
190
191 : best-hand ( str -- hand )
192     parse-cards 5 hand new
193     [ dup hand-value hand boa min ] reduce-combinations ;
194
195 : >cards ( hand -- str )
196     cards>> [ card>string ] map " " join ;
197
198 : >value ( hand -- str )
199     value>> hand-rank VALUE_STR nth ;
200
201 TUPLE: deck
202     { cards sequence } ;
203
204 : <deck> ( -- deck )
205     RANK_STR SUIT_STR 2array [ concat >ckf ] product-map deck boa ;
206
207 : shuffle ( deck -- deck )
208     [ randomize ] change-cards ;
209