Sign in to follow this  

poker- how to compare hands

This topic is 4646 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

If you were making a poker game, what would be the best way to check one hand against the other hand if the game was texas hold'em? Assume that the cards are in an array A-2-3..Q-K(clubs)A-2-3..Q-K(spades)A-2-3..Q-K(diamonds)A-2-3..Q-K(heart). If there is a better way to structure the data for these comparisons, I'm open to suggestions. I've never made a card based game, so I want to make a game with the fastest response time possible. I want to make this a network game that will have a form of a ranking system for regular 10 man tables, but that's a long way from now..

Share this post


Link to post
Share on other sites
Funnily enough I've been thinking about how you'd evaluate a poker hand recently too.

There are two separate things here (just to make sure we're all on the same page):

(Let's assume we're talking about Texas Holdem)

1) Evaluate a hand based on the cards in play - find out what it (currently) is - eg. Flush, High Card etc and rank it based on this (eg 100%=Royal Flush)
2) Evaluate a hand based on the possibility of improvement. For example you currently have 2 clubs in your hand and 2 clubs from the flop - what is the probability of improvment to a Flush etc

Number 2 can also be used to find out what 'the nuts' is - if you just gave it the community cards.

I haven't thought too much about number 2 yet - but I have some ideas on number 1. Which are you interested in?

Share this post


Link to post
Share on other sites
It should just randomly throw out cards, not have a percentage to hit a hand like a flush or a straight.

Example, have a multidimensioan array of size 52 for every card, representing all cards from each suit and if they are in use, or something.

So then you just deal to each player a card and continue clock-wise around the table until every player has 2 cards. Then, just discard a randomly generated card using the same method and not assigning it to a current player's ID, but instead assigning it to MUCK ID.

Works well and makes sure you don't have duplicates or rigging.

EDIT: This might become fairly inefficient if it generates a random int between 0 and 51, and keeps picking a number that has already been generated, so another card has to be picked.

Share this post


Link to post
Share on other sites
The 100%, I believe, was meant to indicate that a royal flush beats all other hands. It doesn't say anything about the probability of getting such a hand (which is, for reference, 4 / (52 choose 5)).

Honestly, I wouldn't worry about speed for the moment. You're unlikely to be doing anything very processor-intensive with the rest of the game as it is; this isn't exactly the latest 3D FPS. Work on making an accurate game first. Then run a profiler on your code to see what parts need to be optimized, if any. Only then should you really start worrying about speed. In the meantime, just don't make dumbassed algorithm decisions.

Share this post


Link to post
Share on other sites
Here's what I'd do Each card has these identifiers when your calculating

int hand
int power

so if you have a Royal Flush you'd have a hand of 100.
If you have four of a kind you'd have a hand of 90
if you have a straight flush you'd have a hand of 80.

and so on (I don't know all the hands so just give them divisions of 10 and makes sure to do a "has no hand" at 0. then for power

If you have a Royal Flush (assuming all suits are the same) give power of 13.
If you have a four of a kind then Power=rank of card. (so 4 2s would get a power of 2 and 4 10s would get a power of 10, because a 4 10s beat a 4 2s)
For flushes and straights you take the high card.

Then you compare the hands, if you have the same hand you check the power.

This CAN fail you when the high cards are exactly the same, so maybe you should use power[5] so you can compare all five cards, in flushes but that's another story.

Share this post


Link to post
Share on other sites
Don't forget kickers!

After all, AAAKQ beats AAAQJ

sourceforge has a poker library that can handle all of the stuff for you if you'd like.

also, here's code I've written to do the same thing [albeit for Omaha Hi/Lo, but the high test is identical to Hold Em]:


//
// omahasim.cc
//
// Simple brute force app to calculate winning percentages of hands in the
// poker game Omaha hi/lo.
//


#include <fstream>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <time.h>
#else
#include <sys/time.h>
#endif
#include <stdio.h>

long cmdlen=512;
//int logging=1;
//char *logname="omahasim.log";
int logging=0;
char *logname="";

#include "vine.h"

vine *vinetocharvine(vine *inv){if (inv->type==CMD){return(inv->clone());}}


#define T 10
#define J 11
#define Q 12
#define K 13
#define A 14
char *cardletters[]={"","","2","3","4","5","6","7","8","9","T","J","Q","K","A"};
enum suits {C,H,D,S};
char *suitletters[]={"c","h","d","s"};
enum hands {KICKER,PAIR,TRIPS,QUADS,STRAIGHT,LOW};
char *handchar[]={"kicker","pair","trips","quads","straight","low"};

#define DEFAULTPLAYERS 9
#define DEFAULTDEALS 10
#define DEFAULTGAMES 1000

unsigned short deck[52];
long resultcounter[10]; // hands + flush + fh + strfl + 2p + low
int players=DEFAULTPLAYERS;
int deals=DEFAULTDEALS;
int games=DEFAULTGAMES;


char *usage=" Usage: omahasim [-p {# of players (2-9)}] [-s {# of simulations}] [-g {# of games per simulation}]\n\n";



void initdeck ();
void resetcounter ();
void vinecat (vine **);
void sorthands (vine **);
vine *omahahigh (struct omahahole *, struct community *);
vine *cardstohands (vine **);
vine *cardstolowhands (vine **);
int cmphands (vine *, vine *);
void handkiller (vine *);
void winner (vine **);
void winnerlow (vine **);
void parsecli (int,char **);


void initdeck(){

for (int x=0;x<52;x++){
deck[x]=0;
}
}


void resetcounter(){

for (int x=0;x<10;x++){
resultcounter[x]=0;
}
}

struct card{
suits suit;
short rank; // 2-14
card (short inr, suits ins){
suit=ins;
rank=inr;
deck[ins*13+inr-2]=1;
}
card(){
suit=C;
rank=0;
}
~card(){
deck[suit*13+rank-2]=0;
}
vine *cardtoa(){
char *tmp;
tmp=(char *)malloc(cmdlen);
bzero(tmp,cmdlen);
strcat(tmp,cardletters[rank]);
strcat(tmp,suitletters[suit]);
return(new vine(tmp));
}
};


struct phand:
public card{
hands hand;
unsigned short flushflag;
phand(hands inh,unsigned short inff,short inr,suits ins):card(inr,ins){
hand=inh;
flushflag=inff;
}
phand(hands inh,unsigned short inff,card *inc){
suit=inc->suit;
rank=inc->rank;
hand=inh;
flushflag=inff;
}
sout(){
vine *v=cardtoa();
cout << handchar[hand] << " ";
if (flushflag){
cout << "flush ";
}
v->sout();
v->nuke(&v);
}

};





card *drawcard(){
//
// Deal a card from the deck and return it.
//
// WARNING: dealing from an empty deck will lead to inf-loop.
// Shouldn't be a problem for standard poker.
//
suits rs;
short rr;
int x;

while (1){
//
// NOTE: slight bug here. rand() sucks, and will produce decidedly unrandom results
// should the suit be done seperately from the rank.
//
//
x=rand()%52;
rs=(suits)x/13;
rr=x%13;
//rs=(suits)rand()%4;
//rr=rand()%13;

if (!deck[rs*13+rr]){
return(new card(rr+2,rs));
}
}
}


struct omahahole{
card *h1;
card *h2;
card *h3;
card *h4;
omahahole(){
h1=drawcard();
h2=drawcard();
h3=drawcard();
h4=drawcard();
}
~omahahole(){
delete h1;
delete h2;
delete h3;
delete h4;
}
vine *holetoa(){
vine *v=0;
(h1->cardtoa())->bottom(&v);
(h2->cardtoa())->bottom(&v);
(h3->cardtoa())->bottom(&v);
(h4->cardtoa())->bottom(&v);
vinecat(&v);
return(v);
}

};


struct community{
card *h1;
card *h2;
card *h3;
card *h4;
card *h5;
community(){
h1=drawcard();
h2=drawcard();
h3=drawcard();
h4=drawcard();
h5=drawcard();
}
~community(){
delete h1;
delete h2;
delete h3;
delete h4;
delete h5;
}
vine *holetoa(){
vine *v=0;
(h1->cardtoa())->bottom(&v);
(h2->cardtoa())->bottom(&v);
(h3->cardtoa())->bottom(&v);
(h4->cardtoa())->bottom(&v);
(h5->cardtoa())->bottom(&v);
vinecat(&v);
return(v);
}

};


vine *omahahigh(omahahole *inoh, community *inc){
//
// Send an omaha hand through the checker to determine player's best cards.
//

vine *v,*max=0;
card *oh[4];
card *co[5];
phand *h;
int i[5];
int maxx[5];

oh[0]=inoh->h1;
oh[1]=inoh->h2;
oh[2]=inoh->h3;
oh[3]=inoh->h4;

co[0]=inc->h1;
co[1]=inc->h2;
co[2]=inc->h3;
co[3]=inc->h4;
co[4]=inc->h5;

for (i[0]=0;i[0]<3;i[0]++){
for (i[1]=i[0]+1;i[1]<4;i[1]++){
for (i[2]=0;i[2]<3;i[2]++){
for (i[3]=i[2]+1;i[3]<4;i[3]++){
for (i[4]=i[3]+1;i[4]<5;i[4]++){
v=0;
(new vine(CARD,oh[i[0]]))->bottom(&v);
(new vine(CARD,oh[i[1]]))->bottom(&v);
(new vine(CARD,co[i[2]]))->bottom(&v);
(new vine(CARD,co[i[3]]))->bottom(&v);
(new vine(CARD,co[i[4]]))->bottom(&v);
v=cardstohands(&v);
if (1==cmphands(v,max)){
handkiller(max);
max->nuke(&max);
max=v;

}else{
handkiller(v);
v->nuke(&v);
}
}
}
}
}
}

return(max);
}


vine *omahalow(omahahole *inoh, community *inc){
//
// Send an omaha hand through the checker to determine player's best low cards.
//

vine *v,*max=0;
card *oh[4];
card *co[5];
phand *h;
int i[5];
int maxx[5];
vine *om;

oh[0]=inoh->h1;
oh[1]=inoh->h2;
oh[2]=inoh->h3;
oh[3]=inoh->h4;

co[0]=inc->h1;
co[1]=inc->h2;
co[2]=inc->h3;
co[3]=inc->h4;
co[4]=inc->h5;

for (i[0]=0;i[0]<3;i[0]++){
for (i[1]=i[0]+1;i[1]<4;i[1]++){
for (i[2]=0;i[2]<3;i[2]++){
for (i[3]=i[2]+1;i[3]<4;i[3]++){
for (i[4]=i[3]+1;i[4]<5;i[4]++){
v=0;
om=max;
(new vine(CARD,oh[i[0]]))->bottom(&v);
(new vine(CARD,oh[i[1]]))->bottom(&v);
(new vine(CARD,co[i[2]]))->bottom(&v);
(new vine(CARD,co[i[3]]))->bottom(&v);
(new vine(CARD,co[i[4]]))->bottom(&v);
v=cardstolowhands(&v);
if (v){
if (!max){
max=v;
}else{
if (-1==cmphands(v,max)){
handkiller(max);
max->nuke(&max);
max=v;

}else{
handkiller(v);
v->nuke(&v);
}
}
if (om && !max){
cout << "max existed, then nulled.\n";
}
}
}
}
}
}
}

return(max);
}




void result(vine *inhand){
//
// Take high hand and add result-counter
//
phand *h;
vine *v;

if (!inhand){
return;
}
h=(phand *)inhand->data;
if (h->hand==STRAIGHT && h->flushflag){
resultcounter[7]++;
return;
}
if (h->flushflag){
resultcounter[5]++;
return;
}
if (h->hand==TRIPS){
for (v=inhand;v;v=v->next){
h=(phand *)v->data;
if (h->hand==PAIR){
// then it's a boat, not just trips.
resultcounter[6]++;
return;
}
}
resultcounter[TRIPS]++;
return;
}
if (h->hand==PAIR){
v=inhand->next->next;
h=(phand *)v->data;
if (h->hand==PAIR){
resultcounter[8]++;
return;
}
resultcounter[PAIR]++;
return;
}
if (h->hand==LOW){
resultcounter[9]++;
return;
}
resultcounter[h->hand]++;
}




void printresults(){
//
// Print results of resultcounter
//
long total;

total=0;
for (int x=0;x<9;x++){
total+=resultcounter[x];
}
cout << "high only: " << resultcounter[0] << "(" << ((float)resultcounter[0]/total)*100 << "%)\n"
<< "1p: " << resultcounter[1] << "(" << ((float)resultcounter[1]/total)*100 << "%)\n"
<< "2p: " << resultcounter[8] << "(" << ((float)resultcounter[8]/total)*100 << "%)\n"
<< "trips: " << resultcounter[2] << "(" << ((float)resultcounter[2]/total)*100 << "%)\n"
<< "quads: " << resultcounter[3] << "(" << ((float)resultcounter[3]/total)*100 << "%)\n"
<< "straight: " << resultcounter[4] << "(" << ((float)resultcounter[4]/total)*100 << "%)\n"
<< "flush: " << resultcounter[5] << "(" << ((float)resultcounter[5]/total)*100 << "%)\n"
<< "full house: " << resultcounter[6] << "(" << ((float)resultcounter[6]/total)*100 << "%)\n"
<< "str flush: " << resultcounter[7] << "(" << ((float)resultcounter[7]/total)*100 << "%)\n"
<< "low: " << resultcounter[9] << "(" << ((float)resultcounter[9]/total)*100 << "%)\n";

}





vine *cardstolowhands(vine **inv){
//
// convert vine of card to a vine of poker hands [low, assume 8 min]
//

int lowtotal[8];
vine *v,*w;
vine *rtn=0;
card *c1,*c2;
int y;
int x;

for (x=0;x<8;x++){
lowtotal[x]=0;
}
if (!*inv){
return(0);
}
if ((*inv)->count()!=5){
return(0);
}
for (v=*inv;v;v=v->next){
c1=(card *)v->data;
if (c1->rank==A){
lowtotal[0]++;
}else{
if (c1->rank<=8){
lowtotal[c1->rank-1]++;
}else{
(*inv)->nuke(inv);
return(0);
}
}
}
y=0;
for (x=0;x<8;x++){
y+=lowtotal[x];
}
if (y!=5){
(*inv)->nuke(inv);
return(0);
}
for (x=0;x<8;x++){
if (lowtotal[x]>1){
(*inv)->nuke(inv);
return(0);
}
}
for (v=*inv;v;v=v->next){
c1=(card *)v->data;
if (c1->rank==A){
c1->rank=1;
}
(new vine(HAND,new phand(LOW,0,c1)))->top(&rtn);
if (c1->rank==1){
c1->rank=A;
}
}
(*inv)->nuke(inv);
sorthands(&rtn);
return(rtn);
}







vine *cardstohands(vine **inv){
//
// convert vine of card to a vine of poker hands
//

vine *v,*w;
vine *rtn=0;
int p=0;
int f=0;
card *c1,*c2;
int s[5];
int x;

if (!*inv){
return(0);
}
if ((*inv)->count()!=5){
return(0);
}
// first, check for flush
v=*inv;
c1=(card *)v->data;
for (v=(*inv)->next;v;v=v->next){
c2=(card *)v->data;
if (c2->suit==c1->suit){
f++;
}
}
if (f==4){
f=1;
}else{
f=0;
}

// next, run through cards
for (v=(*inv)->next;v;v=v->next){
p=0;
c1=(card *)v->data;
for (w=*inv;w;w=w->next){
if (w!=v){
c2=(card *)w->data;
if (c2->rank==c1->rank){
p++;
}
}
}
(new vine(HAND,new phand((hands)p,f,c1)))->top(&rtn);
}


// next check for straights.
for (v=(*inv)->next;v;v=v->next){
for (x=0;x<5;x++){s[x]=0;}
s[0]=1;
c1=(card *)v->data;
for (w=*inv;w;w=w->next){
if (w!=v){
c2=(card *)w->data;
if (c1->rank==A){
if (c2->rank-1<5 && c2->rank-1>0){
s[c2->rank-1]++;
}
}else{
if (c2->rank-c1->rank<5 && c2->rank-c1->rank>0){
s[c2->rank-c1->rank]++;
}
}
}
}
if (s[0] && s[1] && s[2] && s[3] && s[4]){
(new vine(HAND,new phand(STRAIGHT,f,c1)))->top(&rtn);
}
}
(*inv)->nuke(inv);
sorthands(&rtn);
return(rtn);
}



void sorthands(vine **inv){
//
// Sort hands according to hand
//

phand *h1, *h2;
vine *v, *vv;
int flag;

flag=1;
//cout << "in sort\n";
while(flag){
flag=0;
for (v=*inv;(v && !flag);v=v->next){
vv=v->next;
if (vv){
h1=(phand *)v->data;
h2=(phand *)vv->data;
if (h2->hand>h1->hand){
v->swapwith(vv,inv);
flag=1;
}else if (h2->hand==h1->hand && h2->flushflag==1 && h1->flushflag==0){
v->swapwith(vv,inv);
flag=1;
}else if (h2->hand==h1->hand && (h2->rank > h1->rank)){
v->swapwith(vv,inv);
flag=1;
}
}
}
}
}




int cmphands(vine *a,vine *b){
//
// compare hands
//
// 0 if equal, 1 if a, -1 if b
//
// Returns the "better" hand, so reverse for low tests.
// [and ensure both hands exist...]
//

phand *h1,*h2,*ht;
vine *v,*vv;

if (!b && !a){
return(0);
}
if (!b){
return(1);
}
if (!a){
return(-1);
}
h1=(phand *)a->data;
h2=(phand *)b->data;

//cout << "in cmphands\n";

// Straight flushes
if (h1->hand==STRAIGHT && h1->flushflag==1){
if (h2->hand!=STRAIGHT || h2->flushflag==0){
return(1);
}
if (h1->rank > h2->rank){
return(1);
}else if(h1->rank==h2->rank){
return(0);
}else{
return(-1);
}
}
if (h2->hand==STRAIGHT && h2->flushflag==1){
return(-1);
}


// Quads
if (h1->hand==QUADS){
if (h2->hand!=QUADS){
return(1);
}
if (h1->rank > h2->rank){
return(1);
}else{
return(-1);
}
}
if (h2->hand==QUADS){
return(-1);
}

// Flushes
if (h1->flushflag==1){
if (h2->flushflag==1){
v=a;
vv=b;
while (v){
h1=(phand *)v->data;
h2=(phand *)vv->data;

if (h1->rank>h2->rank){
return(1);
}else if (h2->rank>h1->rank){
return(-1);
}

v=v->next;
vv=vv->next;
}
return(0);
}else if (h2->hand!=TRIPS){
return(1);
}else{
// possible full house beating the flush
for (v=b;v;v=v->next){
ht=(phand *)v->data;
if (ht->hand==PAIR){
return(-1);
}
}
// oops, no 2nd pair, flush beats trips.
return(1);
}
}
if (h2->flushflag==1){
if (h1->hand!=TRIPS){
return(-1);
}else{
// possible full house beating the flush
for (v=a;v;v=v->next){
ht=(phand *)v->data;
if (ht->hand==PAIR){
return(1);
}
}
// oops, no 2nd pair, flush beats trips.
return(-1);
}
}

// Straights
if (h1->hand==STRAIGHT){
if (h2->hand==STRAIGHT){
if (h1->rank>h2->rank){
return(1);
}else if (h1->rank==h2->rank){
return(0);
}else{
return(-1);
}
}else{
return(1);
}
}
if (h2->hand==STRAIGHT){
return(-1);
}

// Trips
if (h1->hand==TRIPS){
if (h2->hand!=TRIPS){
return(1);
}
v=a->next->next->next;
vv=b->next->next->next;
while(v){
h1=(phand *)v->data;
h2=(phand *)vv->data;
if (h1->hand==PAIR && h2->hand!=PAIR){
// full house beats trips.
return(1);
}
if (h1->hand!=PAIR && h2->hand==PAIR){
// full house beats trips.
return(-1);
}
if (h1->hand==PAIR && h2->hand==PAIR){
// check trip size of the boat.
if (((phand *)a->data)->rank > ((phand *)b->data)->rank){
// bigger boat wins.
return(1);
}
if (((phand *)a->data)->rank < ((phand *)b->data)->rank){
// bigger boat wins.
return(-1);
}
// check the little size of the boat
if (h1->rank>h2->rank){
return(1);
}
if (h2->rank>h1->rank){
return(-1);
}
// and you can't split with boats.
}
// otherwise, just trips.
if (((phand *)a->data)->rank > ((phand *)b->data)->rank){
return(1);
}
if (((phand *)a->data)->rank < ((phand *)b->data)->rank){
return(-1);
}
// otherwise, even trips, check the kickers.
if (h1->rank>h2->rank){
return(1);
}
if (h1->rank<h2->rank){
return(-1);
}
// else, first kicker even
v=v->next;
vv=vv->next;
}
// else even kickers with the trips.
return(0);
}
if (h2->hand==TRIPS){
return(-1);
}

// pairs
if (h1->hand==PAIR){
if (h2->hand!=PAIR){
return(1);
}
v=a->next->next;
vv=b->next->next;
while(v){
h1=(phand *)v->data;
h2=(phand *)vv->data;
if (h1->hand==PAIR && h2->hand!=PAIR){
return(1);
}
if (h1->hand!=PAIR && h2->hand==PAIR){
return(-1);
}
if (h1->hand==PAIR && h2->hand==PAIR){
if (((phand *)a->data)->rank > ((phand *)b->data)->rank){
return(1);
}
if (((phand *)a->data)->rank < ((phand *)b->data)->rank){
return(-1);
}
if (h1->rank>h2->rank){
return(1);
}
if (h2->rank>h1->rank){
return(-1);
}
}
// otherwise, just pair.
if (((phand *)a->data)->rank > ((phand *)b->data)->rank){
return(1);
}
if (((phand *)a->data)->rank < ((phand *)b->data)->rank){
return(-1);
}
if (h1->rank>h2->rank){
return(1);
}
if (h1->rank<h2->rank){
return(-1);
}
v=v->next;
vv=vv->next;
}
return(0);
}
if (h2->hand==PAIR){
return(-1);
}

// Only high card.
v=a;
vv=b;
while(v){
h1=(phand *)v->data;
h2=(phand *)vv->data;
if (h1->rank>h2->rank){
return(1);
}
if (h1->rank<h2->rank){
return(-1);
}
v=v->next;
vv=vv->next;
}
return(0);
}




void handkiller(vine *inv){
//
// Delete hands out of vines
//

phand *h;

for (vine *v=inv;v;v=v->next){
if (v->type==HAND){
h=(phand *)v->data;
delete h;
}
}
}







void vinecat(vine **inv){
//
// Concatenated cmd vines without newlines.
//
vine *v;

if (!*inv){
return;
}

for (v=*inv;v;v=v->next){
while(v->next && v->type==CMD && v->next->type==CMD && !strstr((char *)v->data,"\n") && cmdlen > strlen((char *)v->data) + strlen((char *)v->next->data)){
strcat((char *)v->data,(char *)v->next->data);
v->next->remove(inv);
}
}
}



void winner(vine **inv){
//
// Take vine *foo[9] and pair down the array to only winning hands.
//
vine *v,*vv;
int x,y;
int rtn;

for (x=0;x<9;x++){
for (y=0;y<9;y++){
if (inv[x]){
v=inv[x];
if (inv[y]){
vv=inv[y];
if (y!=x){
rtn=cmphands(v,vv);
if (rtn==1){
handkiller(vv);
inv[y]->nuke(&inv[y]);
}else if (rtn==-1){
handkiller(v);
inv[x]->nuke(&inv[x]);
}else{
}
}
}
}
}
}
}



void winnerlow(vine **inv){
//
// Take vine *foo[9] and pair down the array to only winning hands.
//
vine *v,*vv;
int x,y;
int rtn;

for (x=0;x<9;x++){
for (y=0;y<9;y++){
if (inv[x]){
v=inv[x];
if (inv[y]){
vv=inv[y];
if (y!=x){
rtn=cmphands(v,vv);
if ((rtn<0 && v)||!vv){
handkiller(vv);
inv[y]->nuke(&inv[y]);
}else if (rtn==1){
handkiller(v);
inv[x]->nuke(&inv[x]);
}else{
}
}
}
}
}
}
}



void parsecli(int argc,char *argz[]){
//
// Parse through argv and set info.
//
int x;
int flag;

flag=0;
for (x=1;x<argc;x++){
if (argz[x]==strstr(argz[x],"-p")){
if (x+1!=argc){
players=atoi(argz[x+1]);
flag++;
}
}else if (argz[x]==strstr(argz[x],"-s")){
if (x+1!=argc){
deals=atoi(argz[x+1]);
flag++;
}
}else if (argz[x]==strstr(argz[x],"-g")){
if (x+1!=argc){
games=atoi(argz[x+1]);
flag++;
}
}
}
if (!flag && argc>1){
cout << usage;
exit(1);
}
if (players<2){ players=2; }
if (players>9){ players=9; }
if (deals<1){ deals=1; }
if (games<1){ games=1; }
}






void main(int argc, char *argz[]){
//
//
//
omahahole *oh[9];
community *co;
int rtn;
vine *v,*vv,*c;
vine *vh[9];
int t;
float w;
float lw;
long lp;
int z,zz;
int winnah;

#ifdef _WIN32
long t;
time(&t);
srand(x);
#else
srand(time(0));
#endif
for (z=0;z<9;z++){
oh[z]=0;
vh[z]=0;
}

parsecli(argc,argz);

cout << "Simulating "<< deals << " times.\n" << players-1 <<" opponents will play "<< games << " hands.\n\n";
for (int x=0;x<deals;x++){
initdeck();
oh[0]=new omahahole();
c=(oh[0]->holetoa());
c->sout();
c->nuke(&c);
t=0;w=0;lw=0;lp=0;
for (int y=0;y<games;y++){
t++;
co=new community();
for (z=1;z<players;z++){
oh[z]=new omahahole();
}
for (z=0;z<players;z++){
vh[z]=omahahigh(oh[z],co);
}
result(vh[0]);
winner(vh);
zz=0;
if (vh[0]){ winnah=1; }else{ winnah=0; }
for (z=0;z<9;z++){
if (vh[z]){
zz++;
handkiller(vh[z]);
vh[z]->nuke(&vh[z]);
}
}
if (winnah){
w=w+1/zz;
}

for (z=0;z<players;z++){
vh[z]=omahalow(oh[z],co);
}
result(vh[0]);
winnerlow(vh);
if (vh[0]){ winnah=1; }else{ winnah=0; }
zz=0;
for (z=0;z<9;z++){
if (vh[z]){
zz++;
handkiller(vh[z]);
vh[z]->nuke(&vh[z]);
}
}
if (zz){
lp++;
if (winnah){
lw=lw+1/zz;
}
}

for (z=1;z<players;z++){
delete oh[z];
}

delete co;


}
cout << "high wins: "<<w/t*100<<"% low wins: "<<lw<<"("<<lw/t*100<<"% of total; "<<lw/lp*100<<"% of low; "<<lw/resultcounter[9]*100<<"% of participated) \n";
printresults();
cout << "\n";
resetcounter();
//cout << "vinecount: " << vinecount << "\n";
delete oh[0];
}

}

Share this post


Link to post
Share on other sites

To compare two hands:

- In each hand, if all cards are the same, mark hand as a flush.
- In each hand:
- Group matching cards. Represent each group as a pair (rank, number of cards).
- Sort the groups by number of cards, then by rank. If there are five groups:
- We know there are no matched cards. If the rank differs by 4 between first and last:
- mark hand as a straight.

- If both hands are straight AND flush:
- Compare ranks of first group, and report the winner from that.

- Elsif one hand is straight AND flush:
- That hand wins.

(Otherwise, neither hand is a straight flush...)
- Elsif both hands are straight OR flush: (must be exclusive or at this point)
- If one is flush and the other is straight, the flush wins.
- Else, compare groups from the beginning down (to handle flushes).

- Elsif one hand is a straight OR flush (and thus the other isn't):
- If the non-straight/flush hand has two groups:
- It is four of a kind or full house, and wins.
- Otherwise, the other hand wins.

(Otherwise, neither hand is a straight or a flush).
- Compare group sizes from first group to last. If there is any mismatch:
- The hand with the larger group wins.
- Otherwise, the hand types are the same; compare group ranks from first group to last.


[Edited by - Zahlman on March 8, 2005 10:26:39 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I have written extensive hand evaluation functions designed to be extremely efficient (for high speed monte-carlo's, complete enumerations, and so on) so listen up.

The idea here is to denote your ranks as powers of two.. duece = 1, three = 2, four = 4, five = 8, etc.. this allows you to use a bitwise 'or' operation to combine cards into bitmasks/hashes quite easily.

If you decimate the 7 cards a player has into 4 of these masks (A, B, C, and D.. one for each suit) then you have already greatly simplified the process of hand evaluation as you no longer need to sort the cards or anything.. also combine these 4 using bitwise 'or' into a 5th mask (E) representing all the ranks that appear in the hand.

Some helper functions/tables you will need include a bitwise population count ("how many bits are in this mask?") and a routine to seperate the lowest set bit from the rest of the mask (two outputs.. X and Y, X is the lowest set bit, and Y is the mask with that bit removed) .. These can be calculated using either lookup tables or bitwise manipulations. The tables are quite small (8192 entries) so its a pretty good option.

Also useful is a lookup table for straights. Straights are slow to evaluate without a lookup table. You can check for any straight using mask E, and then if needed, check for straight flushes using the other 4 masks (A, B, C, and D)

The population count of the masks A to D are usefull for finding flushes fast. If Count >= 5 then you have at least a flush.

The population count of the E mask is useful for deciding what types of pairish hands to look for.. if 7 bits are set then you know there arent any duplicate ranks.. if 6 bits are set then you know you need to find a single pair.. if 5 are set then the hand might be two pair or trips.. if 4 are set then it might be 2 pair (three pair really) a full house or quads.. if 3 are set then it might be a full house or quads.. if 2 are set then you definately got quads.. the technique for finding which pairs and so forth consist of several if statements combined with bitwise logic.

On to the most important thing.. ranking the hands high a low..

The 9 "primary" classifications of hands there are:

Straight Flush (8), Quads (7), Full House (6), Flush (5), Straight (4), Trips (3), Two Pair (2), One Pair (1), and No Pair (0).

There are nine of them. You can store this part of the evaluation in the upper 4 bits of the hand evaluation. This leaves the lower bits to store the remaining information necessary to discern precisely what the hand is, keeping in mind that we must be able to sort hands of the same primary type. (we can now already sort hands of different primary types)

There needs to be enough space left in the hand evaluation to store the one pair hands.. the one pair hands use the most space due to the amount of information you need to store.. the pair and 3 kickers. Each of these can be stored in 4 bits each by denoting the ranks as a value between 0 and 12 inclusive. This puts a limit on the evaluations at 20-bits (4-bits for primary, 16-bits for secondary), which luckily fits into a 32-bit integer and also semi-lucky is that each part aligns to a single hex digit.

It might seem like no pair and flush hands have 5 kickers, and they do, but we can use a trick to storing these hands just by using 13 bits of the 16 bits allocated to the secondary classification and simply store the bitmask of the 5 ranks.

Straight Flushes: (hex) 8000x
where x = the rank of the highest card in the straight

Quads: (hex) 700xy
where x = the quaded rank and y = the kicker

Full house: (hex) 600xy
where x = the triped rank and y = the paired rank

Flush: (hex) 5xxxx
where x = the bitbask of the 5 cards that make up the flush

Straight: (hex) 4000x
where x = the rank of the highest card in the straight

Trips: (hex) 30xyz
where x = the triped rank, y and z are the kickers (high to low)

Two pair: (hex) 20xyz
where x = the highest pair, y = the second highest pair, and z = the kicker.

One pair: (hex) 1xyzw
where x = the paired rank, y, z, and w = the kickers (high to low)

No pair: (hex) 0xxxx
where x = the bitmask of the 5 ranks that make up the 5 kickers.


Now given an eval() function which returns said evaluation, you can simply compare the values to see which hand is better (or if they are tied).

Things to keep in mind: Lookup tables are you friend.. a lookup table to detect straights can simply store the hand evaluation for all legal straights and 0 otherwise (0 is not mapped to a legal hand in this system) .. as well a single lookup table for flushes and straight flushes is possible and those too can simply store the hand evaluation

- Rockoon (Joseph Koss)

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by stubble
Hey AP - I found some code which took this/similar approach and had been hoping to step through it when I had the time/strength - your post should help a lot - thanks!


Well when you do finally get into the meat of it.. you will find that there is just a little bit of tedious, but simple, coding involved!

Some post-coding advice.. write a hold'em horse race simulator which pits two starting hands against each other and enumerates all possible outcomes and tabulate the results.. you can compare the results with the simulator at http://www.twodimes.net/poker/ which I am quite certain is flawless (it not only agrees with my own simulator, but several other peoples who I have come across over the past few years) .. consider the results of the twodimes.net simulator to be the standard with which to measure your own.

Share this post


Link to post
Share on other sites
I don't have anything to add to the whole evaluating hands dealt discussion, but I would like to recommend a method of handling card randomization/dealing that I used in an old poker sim I wrote a while back (it's method of evaluating the hands and picking a winner were rather clumsy and inefficient at the time).

What I did was to actually simulate the shuffling of the deck. I quickly slapped the following tidbit of code together as an example. This would be a perfect candidate to turn into a deck class that handled everything for you...


#include "stdafx.h"
#include "stdlib.h"
#include "iostream.h"
#include "time.h"

struct card{
int value;
int suit;
};

card deck[52];
int cardsdealt = 0;
void shuffle();
void createdeck();
void dealcard();

void main(int argc, char* argv[])
{
int x;

srand(static_cast<unsigned>(time(0)));
createdeck();
shuffle();

cout << "Hand one: ";
for(x = 0; x < 5; x++){
dealcard();
cout << " ";
}

cout << endl << "Hand two: ";
for(x = 0; x < 5; x++){
dealcard();
cout << " ";
}


}

void createdeck(){

int suit, card, index = 0;

for(suit = 0; suit<4; suit++){
for(card=0; card<13; card++){
deck[index].value = card;
deck[index].suit = suit;
index++;
}
}

}
void shuffle(){

int iterate, swap;
card holder;

for(iterate = 0; iterate<52; iterate++){
swap = rand()%52;
holder = deck[swap];
deck[swap] = deck[iterate];
deck[iterate] = holder;

}


}

void dealcard(){

char value[2];
char suit;
int temp;

//If cardsdealt>51, deck is empty
//Needs code to account for empty deck

switch(deck[cardsdealt].value){
case 0:
value[0] = 'A';
value[1] = 0;
break;
case 10:
value[0] = 'J';
value[1] = 0;
break;
case 11:
value[0] = 'Q';
value[1] = 0;
break;
case 12:
value[0] = 'K';
value[1] = 0;
break;
default:
temp = deck[cardsdealt].value + 1;
itoa(temp, value, 10);
}

switch(deck[cardsdealt].suit){
case 0:
suit = 'H';
break;
case 1:
suit = 'S';
break;
case 2:
suit = 'C';
break;
case 3:
suit = 'D';
}

cout << value << suit;

cardsdealt++;

}




So you make 52 calls to rand(), and only 52 calls to rand, and you don't have to check if a card dealt is a duplicate or not.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by jRaskell

*** Source Snippet Removed ***

So you make 52 calls to rand(), and only 52 calls to rand, and you don't have to check if a card dealt is a duplicate or not.


Seems to me that this shuffle will not give a good random distribution. The effects of the problem are very small for a deck of 52 cards but are easily noticed in a deck of 3. A repeated test of this shuffle method on an ordered input deck will show that order matters.

A small modification to this shuffle routine is all thats needed.

While you select a swap card from the entire deck, it should be selecting a swap card from only the remaining unshuffled deck.

So change:

swap = rand() % 52

to

swap = iterate + rand() % (52 - iterate)

In english the shuffle becomes "The next card is chosen randomly from the cards that are left"

- Rockoon (Joseph Koss)

Share this post


Link to post
Share on other sites
Well, realistically you should shuffle the deck once before each play and then throw out cards. You can randomly build a stack of cards and then just pop the ones off the top. That's exactly how I would do it; and it's fairly easy to write, and you don't have to worry about having duplicates (if there is only one card per stack).

Share this post


Link to post
Share on other sites

This topic is 4646 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this