poker 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|