poker classes for php
These classes are really, rough- mostly a proof of concept for my method to rank hands. Feel free to use this code or algorithms, just as long as you give me attribution.
First, the card class:
define(‘SPADES’, 3);
define(‘DIAMONDS’, 2);
define(‘HEARTS’, 1);
define(‘CLUBS’, 0);
define(‘ACE’, 14);
define(‘KING’, 13);
define(‘QUEEN’, 12);
define(‘JACK’, 11);
class Poker_Card
{
var $rank;
var $suit;
var $_rank_values = array( 2 => ‘Two’,
3 => ‘Three’,
4 => ‘Four’,
5 => ‘Five’,
6 => ‘Six’,
7 => ‘Seven’,
8 => ‘Eight’,
9 => ‘Nine’,
10 => ‘Ten’,
11 => ‘Jack’,
12 => ‘Queen’,
13 => ‘King’,
14 => ‘Ace’);
var $_suit_values = array( SPADES => ‘Spades’,
DIAMONDS => ‘Diamonds’,
HEARTS => ‘Hearts’,
CLUBS => ‘Clubs’);
function Poker_Card($rank, $suit) {
$this->rank = $rank;
$this->suit = $suit;
}
function getRank() {
return $this->_rank_values[$this->rank];
}
function getSuit() {
return $this->_suit_values[$this->suit];
}
}
And now the Hand class:
/*
hand strength ranking
assumptions:
-single deck
-no wildcards
-suit does not work as a tie-breaker
possible hands (total in parentheses):
-note: the total is bases on how many different hands there are in terms of strength.
Therefore, there is only one royal flush, since they all have the same power (remember:
suit is not used as a tie-breaker
1. Royal Flush (1)
2. Straight Flush
-high card [A-5] (9)
3. Four of a kind
-value of quads [A-2] (13)
4. Full House
-trips [A-2] (13)
-twos [A-2, minus trips] (12) (13 x 12 = 156)
5. Flush
-all card values (13 x 12 x 11 x 10 x 9 = 154440)
6. straight (aces either high or low)
-top card [A-5] (9)
7. Three of a kind
-rank of trips (13)
-2 kickers (if more than one deck) (12, 11) (13 x 12 x 11 = 1716)
8. Two pair
-high pair (13)
-low pair (12)
-kicker (11) (13 x 12 x 11 = 1716)
9. Pair
-rank of pair (13)
-3 kickers (12, 11, 10) (13 x 12 x 11 x 10 = 17160)
10. High Card
- 5 kickers (13 x 12 x 11 x 10 x 9 = 154440)
Sum:
1 + 9 + 13 + 156 + 154440 + 9 + 13 + 1716 + 1716 + 17160 + 154440 = 329673
So there are 329,673 different possible hands (in terms of strength) given the above
assumptions.
Representation:
A 12-digit string will be used to represent the strength of the hand.
Digits:
1-2 Type of hand: (10 = royal flush…..01 = high card/no hand)
3-4 The first of tie-breaker values
(5-6)-(11-12) The rest of the tie-breaker values, as needed. Pad with zereos where not needed.
http://www.math.sfu.ca/~alspach/comp18/
http://hoho.com/mike/pypoker/poker.py
*/
class Poker_Hand
{
var $_cards = array();
var $_strength;
var $_strength_desc;
var $_rank_counts;
var $_kickers;
function pushCard($card) {
return array_push($this->_cards, $card);
}
function _calculateStrength() {
//sort the hand on descending rank
$this->_sort();
$strength_array = array_fill(0,6,0);
$this->_countRanks();
//test for royal flush
if ($this->_isFlush() and $this->_isStraight() and $this->_sCards[0].rank == ACE) {
$this->_strength_desc = ‘Royal Flush’;
$strength_array[0] = 9;
} elseif ($this->_isFlush() and $this->_isStraight()) { //test for straight flush
$this->_strength_desc = ‘Straight Flush, ‘ . $this->_sCards[0]->rank . ‘ high’;
$strength_array[0] = 8;
$strength_array[1] = $this->_sCards[0]->rank;
} elseif ($quads_rank = $this->_hasQuads()) { //test for four of a kind
$this->_strength_desc = ‘Four of a kind’;
$strength_array[0] = 7;
$strength_array[1] = $quads_rank;
$strength_array[2] = $this->_kickers[0];
} elseif (list($trips_rank, $pair_rank) = $this->_isFullHouse()) { //test for full house
$this->_strength_desc = ‘Full House, ‘. $trips_rank . ‘ over ‘ . $pair_rank;
$strength_array[0] = 6;
$strength_array[1] = $trips_rank;
$strength_array[2] = $pair_rank;
} elseif ($this->_isFlush()) { //test for flush
$this->_strength_desc = ‘Flush’;
$strength_array[0] = 5;
$strength_array[1] = $this->_kickers[0];
$strength_array[2] = $this->_kickers[1];
$strength_array[3] = $this->_kickers[2];
$strength_array[4] = $this->_kickers[3];
$strength_array[5] = $this->_kickers[4];
} elseif ($this->_isStraight()) { //test for straight
$this->_strength_desc = ‘Straight’;
$strength_array[0] = 4;
$strength_array[1] = $this->_sCards[0]->rank;
} elseif ($trips_rank = $this->_hasTrips()) { //test for three of a kind
$this->_strength_desc = ‘Three of a Kind.’;
$strength_array[0] = 3;
$strength_array[1] = $trips_rank;
$strength_array[2] = $this->_kickers[0];
$strength_array[3] = $this->_kickers[1];
} elseif (list($high_pair, $low_pair) = $this->_hasTwoPair()) { //test for two pair
$this->_strength_desc = ‘Two Pair’;
$strength_array[0] = 2;
$strength_array[1] = $high_pair;
$strength_array [2] = $low_pair;
$strength_array[3] = $this->_kickers[0];
} elseif ($pair_rank = $this->_hasPair()) { //test for pair
$this->_strength_desc = ‘One Pair’;
$strength_array[0] = 1;
$strength_array[1] = $pair_rank;
$strength_array[2] = $this->_kickers[0];
$strength_array[3] = $this->_kickers[1];
$strength_array[4] = $this->_kickers[2];
} else {
$this->_strength_desc = $this->_sCards[0]->getRank().‘ high’;
$strength_array[0] = 0;
$strength_array[1] = $this->_kickers[0];
$strength_array[2] = $this->_kickers[1];
$strength_array[3] = $this->_kickers[2];
$strength_array[4] = $this->_kickers[3];
$strength_array[5] = $this->_kickers[4];
}
$this->_strength = vsprintf(‘%d%02d%02d%02d%02d%02d’, $strength_array);
}
function _isFullHouse()
{
if (array_search(3, $this->_rank_counts) and array_search(2, $this->_rank_counts)){
return array(array_search(3, $this->_rank_counts), array_search(2, $this->_rank_counts));
} else {
return false;
}
}
function _isFlush()
{
return (($this->_sCards[0]->suit == $this->_sCards[1]->suit) and
($this->_sCards[1]->suit == $this->_sCards[2]->suit) and
($this->_sCards[2]->suit == $this->_sCards[3]->suit) and
($this->_sCards[3]->suit == $this->_sCards[4]->suit));
}
// Ace should be either high or low, but this only handles it if its high
function _isStraight()
{
for($i = 0; $i < 4; $i++) {
if
(!(($this->_sCards[$i]->rank - 1) == ($this->_sCards[$i + 1]->rank))) {
return false;
}
}
return true;
}
function _hasQuads()
{
return array_search(4, $this->_rank_counts);
}
function _hasTrips()
{
return array_search(3, $this->_rank_counts);
}
function _hasTwoPair()
{
$temp = array_count_values($this->_rank_counts);
if (isset($temp[2]) and $temp[2] == 2){
return array_keys(array_filter($this->_rank_counts, array($this, ‘_two’)));
} else {
return false;
}
}
function _hasPair()
{
return array_search(2, $this->_rank_counts);
}
//simple bubble sort based on rank
function _sort()
{
$this->_sCards = $this->_cards;
for ($i=0; $i < 4 ; $i++) {
for ($j=0; $j < 4-$i; $j++){
if ($this->_sCards[$j+1]->rank > $this->_sCards[$j]->rank) { /* compare the two neighbors */
$temp = $this->_sCards[$j]; /* swap a[j] and a[j+1] */
$this->_sCards[$j] = $this->_sCards[$j+1];
$this->_sCards[$j+1] = $temp;
}
}
}
}
function isSorted()
{
for ($i=0; $i < 4; $i++) {
if ($this->_sCards[$i]->rank < $this->_sCards[$i + 1]->rank)
return false;
}
return true;
}
function _countRanks()
{
//$this->_rank_counts = array_fill(2,14,0);
foreach ($this->_sCards as $value) {
@$this->_rank_counts[$value->rank]++;
}
$this->_kickers = array_keys(array_filter($this->_rank_counts, array($this,‘_one’)));
}
function _one($v) {
return $v === 1;
}
function _two($v) {
return $v === 2;
}
}
This entry was posted on Monday, October 4th, 2004 at 11:36 pm and is filed under PHP. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

September 1st, 2006 at 2:53 pm
Ryan,
Did you ever do anything more with these?
I’m curious.. (-:
S
September 30th, 2006 at 11:05 pm
sean-
No I didn’t. It was mostly only as an intellectual exercise– one I was musing on how I would write code to sort poker hands, and I got this insight that I should just create a string representation of the hand’s strength, then sort those strings. I’ve never actually put them to use.
August 8th, 2007 at 10:57 am
Hey, I am trying to do a Yahtzee script in php and mysql it is mostly done the only thing that as to be done is the classing the hands. i can only think of a way to class them by doing them individualy example:
if($hand[0] == “1″ && $hand[1] == “2″ && $hand[2] == “3″ && $hand[3] == “4″ && $hand[4] == “5″) etc..
for a straight, but it seems i could be there all day >_> seen as there is alot of hands to class.. But when i look at your script it looks like you have a quicker way how to do it. If you could point me in the right direction that would be great. Thanks.
March 1st, 2008 at 5:53 pm
you have a bug when checking for Royal Flush:
$this->_sCards[0].rank == ACE needs to be:
$this->_sCards[0]->rank == ACE
:)
July 4th, 2008 at 11:35 pm
I agree, this is a really rough class but man the scoring system you came up with is brilliant. Also when checking for a royal flush you can’t just assume “royalflush or straightflush = (flush&&straight);” because it’s possible for there to be a flush and a straight out of the seven cards with out there being either or. You have to check for a flush, extract those specific cards from the array and then check to see if it’s a straight. Then you can determine whether or not it’s a royal/striaght flush. And when checking for a straight you can just do “if in_array 14 -> add the value “1″ to the array that holds solely the values of the cards” so if there are 7 cards and the array that holds the values is $x['cards'] you can just do if in array 14 $x['cards'][7] = 1 or merge it into the array or whatever, but I’m sure you get what i’m saying. But again, the scoring system is brilliant and I’m totally going to put it to work and in turn save my self a lot of time. Thanks for this post.