poker 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/README +1 -0
- data/Rakefile +2 -0
- data/example/aces_equity.rb +45 -0
- data/example/random_hu.rb +53 -0
- data/ext/handeval/Makefile +187 -0
- data/ext/handeval/arrays.h +1969 -0
- data/ext/handeval/extconf.rb +7 -0
- data/ext/handeval/handeval.c +21 -0
- data/ext/handeval/poker.h +43 -0
- data/ext/handeval/pokerlib.c +205 -0
- data/lib/poker.rb +3 -0
- data/lib/poker/card.rb +38 -0
- data/lib/poker/deck.rb +13 -0
- data/lib/poker/hand.rb +97 -0
- data/lib/poker/version.rb +3 -0
- data/poker.gemspec +22 -0
- data/test/poker/card_test.rb +35 -0
- data/test/poker/deck_test.rb +27 -0
- data/test/poker/hand_test.rb +79 -0
- data/test/test_helper.rb +7 -0
- metadata +74 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
VALUE HandEval = Qnil;
|
4
|
+
void Init_handeval();
|
5
|
+
|
6
|
+
VALUE HandEval_hand_rank(VALUE self, int val);
|
7
|
+
VALUE HandEval_eval(VALUE self, int c1, int c2, int c3, int c4, int c5);
|
8
|
+
|
9
|
+
void Init_handeval() {
|
10
|
+
HandEval = rb_define_module("HandEval");
|
11
|
+
rb_define_singleton_method(HandEval, "hand_rank", HandEval_hand_rank, 1);
|
12
|
+
rb_define_singleton_method(HandEval, "eval", HandEval_eval, 5);
|
13
|
+
}
|
14
|
+
|
15
|
+
VALUE HandEval_hand_rank(VALUE self, int val) {
|
16
|
+
return INT2NUM(hand_rank(val));
|
17
|
+
}
|
18
|
+
|
19
|
+
VALUE HandEval_eval(VALUE self, int c1, int c2, int c3, int c4, int c5) {
|
20
|
+
return INT2NUM(eval_5cards(c1 / 2, c2 / 2, c3 / 2, c4 / 2, c5 / 2));
|
21
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#define STRAIGHT_FLUSH 1
|
2
|
+
#define FOUR_OF_A_KIND 2
|
3
|
+
#define FULL_HOUSE 3
|
4
|
+
#define FLUSH 4
|
5
|
+
#define STRAIGHT 5
|
6
|
+
#define THREE_OF_A_KIND 6
|
7
|
+
#define TWO_PAIR 7
|
8
|
+
#define ONE_PAIR 8
|
9
|
+
#define HIGH_CARD 9
|
10
|
+
|
11
|
+
#define RANK(x) ((x >> 8) & 0xF)
|
12
|
+
|
13
|
+
static char *value_str[] = {
|
14
|
+
"",
|
15
|
+
"Straight Flush",
|
16
|
+
"Four of a Kind",
|
17
|
+
"Full House",
|
18
|
+
"Flush",
|
19
|
+
"Straight",
|
20
|
+
"Three of a Kind",
|
21
|
+
"Two Pair",
|
22
|
+
"One Pair",
|
23
|
+
"High Card"
|
24
|
+
};
|
25
|
+
|
26
|
+
#define CLUB 0x8000
|
27
|
+
#define DIAMOND 0x4000
|
28
|
+
#define HEART 0x2000
|
29
|
+
#define SPADE 0x1000
|
30
|
+
|
31
|
+
#define Deuce 0
|
32
|
+
#define Trey 1
|
33
|
+
#define Four 2
|
34
|
+
#define Five 3
|
35
|
+
#define Six 4
|
36
|
+
#define Seven 5
|
37
|
+
#define Eight 6
|
38
|
+
#define Nine 7
|
39
|
+
#define Ten 8
|
40
|
+
#define Jack 9
|
41
|
+
#define Queen 10
|
42
|
+
#define King 11
|
43
|
+
#define Ace 12
|
@@ -0,0 +1,205 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include "arrays.h"
|
3
|
+
#include "poker.h"
|
4
|
+
|
5
|
+
// Poker hand evaluator
|
6
|
+
//
|
7
|
+
// Kevin L. Suffecool
|
8
|
+
// suffecool@bigfoot.com
|
9
|
+
//
|
10
|
+
|
11
|
+
void srand48();
|
12
|
+
double drand48();
|
13
|
+
|
14
|
+
// perform a binary search on a pre-sorted array
|
15
|
+
//
|
16
|
+
int findit( int key )
|
17
|
+
{
|
18
|
+
int low = 0, high = 4887, mid;
|
19
|
+
|
20
|
+
while ( low <= high )
|
21
|
+
{
|
22
|
+
mid = (high+low) >> 1; // divide by two
|
23
|
+
if ( key < products[mid] )
|
24
|
+
high = mid - 1;
|
25
|
+
else if ( key > products[mid] )
|
26
|
+
low = mid + 1;
|
27
|
+
else
|
28
|
+
return( mid );
|
29
|
+
}
|
30
|
+
fprintf( stderr, "ERROR: no match found; key = %d\n", key );
|
31
|
+
return( -1 );
|
32
|
+
}
|
33
|
+
|
34
|
+
//
|
35
|
+
// This routine initializes the deck. A deck of cards is
|
36
|
+
// simply an integer array of length 52 (no jokers). This
|
37
|
+
// array is populated with each card, using the following
|
38
|
+
// scheme:
|
39
|
+
//
|
40
|
+
// An integer is made up of four bytes. The high-order
|
41
|
+
// bytes are used to hold the rank bit pattern, whereas
|
42
|
+
// the low-order bytes hold the suit/rank/prime value
|
43
|
+
// of the card.
|
44
|
+
//
|
45
|
+
// +--------+--------+--------+--------+
|
46
|
+
// |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
|
47
|
+
// +--------+--------+--------+--------+
|
48
|
+
//
|
49
|
+
// p = prime number of rank (deuce=2,trey=3,four=5,five=7,...,ace=41)
|
50
|
+
// r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12)
|
51
|
+
// cdhs = suit of card
|
52
|
+
// b = bit turned on depending on rank of card
|
53
|
+
//
|
54
|
+
init_deck( int *deck )
|
55
|
+
{
|
56
|
+
int i, j, n = 0, suit = 0x8000;
|
57
|
+
|
58
|
+
for ( i = 0; i < 4; i++, suit >>= 1 )
|
59
|
+
for ( j = 0; j < 13; j++, n++ )
|
60
|
+
deck[n] = primes[j] | (j << 8) | suit | (1 << (16+j));
|
61
|
+
}
|
62
|
+
|
63
|
+
|
64
|
+
// This routine will search a deck for a specific card
|
65
|
+
// (specified by rank/suit), and return the INDEX giving
|
66
|
+
// the position of the found card. If it is not found,
|
67
|
+
// then it returns -1
|
68
|
+
//
|
69
|
+
int
|
70
|
+
find_card( int rank, int suit, int *deck )
|
71
|
+
{
|
72
|
+
int i, c;
|
73
|
+
|
74
|
+
for ( i = 0; i < 52; i++ )
|
75
|
+
{
|
76
|
+
c = deck[i];
|
77
|
+
if ( (c & suit) && (RANK(c) == rank) )
|
78
|
+
return( i );
|
79
|
+
}
|
80
|
+
return( -1 );
|
81
|
+
}
|
82
|
+
|
83
|
+
|
84
|
+
//
|
85
|
+
// This routine takes a deck and randomly mixes up
|
86
|
+
// the order of the cards.
|
87
|
+
//
|
88
|
+
shuffle_deck( int *deck )
|
89
|
+
{
|
90
|
+
int i, n, temp[52];
|
91
|
+
|
92
|
+
for ( i = 0; i < 52; i++ )
|
93
|
+
temp[i] = deck[i];
|
94
|
+
|
95
|
+
for ( i = 0; i < 52; i++ )
|
96
|
+
{
|
97
|
+
do {
|
98
|
+
n = (int)(51.9999999 * drand48());
|
99
|
+
} while ( temp[n] == 0 );
|
100
|
+
deck[i] = temp[n];
|
101
|
+
temp[n] = 0;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
print_hand( int *hand, int n )
|
107
|
+
{
|
108
|
+
int i, r;
|
109
|
+
char suit;
|
110
|
+
static char *rank = "23456789TJQKA";
|
111
|
+
|
112
|
+
for ( i = 0; i < n; i++ )
|
113
|
+
{
|
114
|
+
r = (*hand >> 8) & 0xF;
|
115
|
+
if ( *hand & 0x8000 )
|
116
|
+
suit = 'c';
|
117
|
+
else if ( *hand & 0x4000 )
|
118
|
+
suit = 'd';
|
119
|
+
else if ( *hand & 0x2000 )
|
120
|
+
suit = 'h';
|
121
|
+
else
|
122
|
+
suit = 's';
|
123
|
+
|
124
|
+
printf( "%c%c ", rank[r], suit );
|
125
|
+
hand++;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
|
130
|
+
int
|
131
|
+
hand_rank( short val )
|
132
|
+
{
|
133
|
+
if (val > 6185) return(HIGH_CARD); // 1277 high card
|
134
|
+
if (val > 3325) return(ONE_PAIR); // 2860 one pair
|
135
|
+
if (val > 2467) return(TWO_PAIR); // 858 two pair
|
136
|
+
if (val > 1609) return(THREE_OF_A_KIND); // 858 three-kind
|
137
|
+
if (val > 1599) return(STRAIGHT); // 10 straights
|
138
|
+
if (val > 322) return(FLUSH); // 1277 flushes
|
139
|
+
if (val > 166) return(FULL_HOUSE); // 156 full house
|
140
|
+
if (val > 10) return(FOUR_OF_A_KIND); // 156 four-kind
|
141
|
+
return(STRAIGHT_FLUSH); // 10 straight-flushes
|
142
|
+
}
|
143
|
+
|
144
|
+
|
145
|
+
short
|
146
|
+
eval_5cards( int c1, int c2, int c3, int c4, int c5 )
|
147
|
+
{
|
148
|
+
int q;
|
149
|
+
short s;
|
150
|
+
|
151
|
+
q = (c1|c2|c3|c4|c5) >> 16;
|
152
|
+
|
153
|
+
/* check for Flushes and StraightFlushes
|
154
|
+
*/
|
155
|
+
if ( c1 & c2 & c3 & c4 & c5 & 0xF000 )
|
156
|
+
return( flushes[q] );
|
157
|
+
|
158
|
+
/* check for Straights and HighCard hands
|
159
|
+
*/
|
160
|
+
s = unique5[q];
|
161
|
+
if ( s ) return ( s );
|
162
|
+
|
163
|
+
/* let's do it the hard way
|
164
|
+
*/
|
165
|
+
q = (c1&0xFF) * (c2&0xFF) * (c3&0xFF) * (c4&0xFF) * (c5&0xFF);
|
166
|
+
q = findit( q );
|
167
|
+
|
168
|
+
return( values[q] );
|
169
|
+
}
|
170
|
+
|
171
|
+
|
172
|
+
short
|
173
|
+
eval_5hand( int *hand )
|
174
|
+
{
|
175
|
+
int c1, c2, c3, c4, c5;
|
176
|
+
|
177
|
+
c1 = *hand++;
|
178
|
+
c2 = *hand++;
|
179
|
+
c3 = *hand++;
|
180
|
+
c4 = *hand++;
|
181
|
+
c5 = *hand;
|
182
|
+
|
183
|
+
return( eval_5cards(c1,c2,c3,c4,c5) );
|
184
|
+
}
|
185
|
+
|
186
|
+
|
187
|
+
// This is a non-optimized method of determining the
|
188
|
+
// best five-card hand possible out of seven cards.
|
189
|
+
// I am working on a faster algorithm.
|
190
|
+
//
|
191
|
+
short
|
192
|
+
eval_7hand( int *hand )
|
193
|
+
{
|
194
|
+
int i, j, q, best = 9999, subhand[5];
|
195
|
+
|
196
|
+
for ( i = 0; i < 21; i++ )
|
197
|
+
{
|
198
|
+
for ( j = 0; j < 5; j++ )
|
199
|
+
subhand[j] = hand[ perm7[i][j] ];
|
200
|
+
q = eval_5hand( subhand );
|
201
|
+
if ( q < best )
|
202
|
+
best = q;
|
203
|
+
}
|
204
|
+
return( best );
|
205
|
+
}
|
data/lib/poker.rb
ADDED
data/lib/poker/card.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Poker
|
2
|
+
class Card
|
3
|
+
RANKS = %w{ 2 3 4 5 6 7 8 9 T J Q K A }
|
4
|
+
SUITS = %w{ c d h s }
|
5
|
+
PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
|
6
|
+
|
7
|
+
attr_reader :rank, :rank_value, :suit, :suit_value
|
8
|
+
|
9
|
+
def initialize(str)
|
10
|
+
@str = str
|
11
|
+
@rank, @suit = str.split(//)
|
12
|
+
@rank_value = RANKS.index(@rank)
|
13
|
+
@suit_value = SUITS.index(@suit)
|
14
|
+
end
|
15
|
+
|
16
|
+
# An integer is made up of four bytes. The high-order
|
17
|
+
# bytes are used to hold the rank bit pattern, whereas
|
18
|
+
# the low-order bytes hold the suit/rank/prime value
|
19
|
+
# of the card.
|
20
|
+
#
|
21
|
+
# +--------+--------+--------+--------+
|
22
|
+
# |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
|
23
|
+
# +--------+--------+--------+--------+
|
24
|
+
#
|
25
|
+
# p = prime number of rank (deuce=2,trey=3,four=5,five=7,...,ace=41)
|
26
|
+
# r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12)
|
27
|
+
# cdhs = suit of card
|
28
|
+
# b = bit turned on depending on rank of card #
|
29
|
+
def value
|
30
|
+
PRIMES[rank_value] | (rank_value << 8) | 0x8000 >> suit_value |
|
31
|
+
(1 << (16 + rank_value))
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
@str
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/poker/deck.rb
ADDED
data/lib/poker/hand.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../ext/handeval/handeval'
|
2
|
+
|
3
|
+
module Poker
|
4
|
+
class Hand
|
5
|
+
include Enumerable
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
def self.best(cards)
|
9
|
+
best = nil
|
10
|
+
cards.combination(5).each do |c|
|
11
|
+
hand = Hand.new(*c)
|
12
|
+
best = hand if !best || hand > best
|
13
|
+
end
|
14
|
+
best
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(*str_or_ary)
|
18
|
+
if str_or_ary.size == 1
|
19
|
+
@cards = str_or_ary[0].split(/ /).map { |s| Card.new(s) }
|
20
|
+
else
|
21
|
+
if str_or_ary[0].kind_of?(Card)
|
22
|
+
@cards = str_or_ary
|
23
|
+
else
|
24
|
+
@cards = str_or_ary.map { |s| Card.new(s) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def value
|
30
|
+
HandEval.eval(*(@cards.map { |c| c.value }))
|
31
|
+
end
|
32
|
+
|
33
|
+
def rank
|
34
|
+
v = value
|
35
|
+
return "High Card" if v > 6185
|
36
|
+
return "Pair" if v > 3325
|
37
|
+
return "Two Pair" if v > 2467
|
38
|
+
return "Three of a Kind" if v > 1609
|
39
|
+
return "Straight" if v > 1599
|
40
|
+
return "Flush" if v > 322
|
41
|
+
return "Full House" if v > 166
|
42
|
+
return "Four of a Kind" if v > 10
|
43
|
+
return "Straight Flush" if v > 1
|
44
|
+
return "Royal Flush" if v == 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def <=>(hand)
|
48
|
+
- (value <=> hand.value)
|
49
|
+
end
|
50
|
+
|
51
|
+
def [](index)
|
52
|
+
@cards[index]
|
53
|
+
end
|
54
|
+
|
55
|
+
def each(&block)
|
56
|
+
@cards.each(&block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def size
|
60
|
+
@cards.size
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
r = rank
|
65
|
+
if r == "Pair" || r == "Two Pair" || r == "Three of a Kind" ||
|
66
|
+
r == "Full House" || r == "Four of a Kind"
|
67
|
+
h = @cards.inject({}) { |h, c|
|
68
|
+
r = c.rank_value + 1
|
69
|
+
h[r] = h[r] ? h[r] + 1 : 0
|
70
|
+
h
|
71
|
+
}
|
72
|
+
return @cards.sort_by { |c|
|
73
|
+
if h[c.rank_value + 1] > 0
|
74
|
+
- ((h[c.rank_value + 1] * 10_000) + (c.rank_value + 1) * 100) +
|
75
|
+
c.suit_value
|
76
|
+
else
|
77
|
+
- c.rank_value + c.suit_value
|
78
|
+
end
|
79
|
+
}.join(" ")
|
80
|
+
elsif r == "Straight Flush" || r == "Straight"
|
81
|
+
return @cards.sort_by { |c|
|
82
|
+
if c.rank_value == 12
|
83
|
+
10_000
|
84
|
+
else
|
85
|
+
- (c.rank_value + 1) * 100 - c.suit_value
|
86
|
+
end
|
87
|
+
}.join(" ")
|
88
|
+
end
|
89
|
+
@cards.sort_by { |c| - (c.rank_value + 1) * 100 - c.suit_value }.
|
90
|
+
join(" ")
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_str
|
94
|
+
to_s
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/poker.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "poker/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "poker"
|
7
|
+
s.version = Poker::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Wojciech Mach"]
|
10
|
+
s.email = ["wojtekmach1@gmail.com"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/poker"
|
12
|
+
s.summary = %q{Poker hand evaluator}
|
13
|
+
s.description = %q{Poker hand evaluator}
|
14
|
+
|
15
|
+
s.rubyforge_project = "poker"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib", "ext"]
|
21
|
+
s.extensions = ["ext/handeval/extconf.rb"]
|
22
|
+
end
|