Poker-AI.org

Poker AI and Botting Discussion Forum
It is currently Mon Nov 13, 2023 2:19 pm

All times are UTC




Post new topic Reply to topic  [ 10 posts ] 
Author Message
PostPosted: Mon May 11, 2015 4:34 am 
Offline
New Member

Joined: Tue Jan 27, 2015 4:16 am
Posts: 8
First, great forum! Sorry for first post being one asking for help, but...

I am trying to analyze a game that is a standard 52 card deck plus 2 jokers which act like Pai Gow "bugs". So it can complete straights, flushes, straight flushes, or act as an Ace. It seems the PokerSource evaluator has a joker evaluator, but with a couple of restrictions:

1. It only supports 1 Joker
2. It doesn't let the Joker complete straight flushes. I'm not sure if this is intended...

For example, this returns quads instead of a straight flush:

Code:
JokerDeck_CardMask quadSF = jokerHandStringToMask("XxAs5s4s3sAdAc");
int val = JokerDeck_JokerRules_EVAL_N(quadSF, 7);
int type = HandVal_HANDTYPE(val);
printf("Hand: %s, Value: %d, Type: %d\n", JokerDeck_maskString(quadSF), val, type);

Any advice on how to proceed? Is there a different evaluator that would work better?


Last edited by cartwright on Tue May 12, 2015 5:18 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Mon May 11, 2015 11:04 pm 
Offline
New Member

Joined: Tue Jan 27, 2015 4:16 am
Posts: 8
I updated eval_joker.h adding the following snipped after we check quints and got the results I wanted.

Code:
if (HandVal_HANDTYPE(retval) == JokerRules_HandType_STFLUSH)
    return retval;


Now I need to figure out how to handle hands with 2 jokers. I've tried adding a second joker card to the deck_joker code as a first step, and it isn't quite working. Anyone ever get pokerSource working with 2 jokers? Is there a different approach anyone might recommend?


Top
 Profile  
 
PostPosted: Mon May 11, 2015 11:29 pm 
Offline
New Member

Joined: Tue Jan 27, 2015 4:16 am
Posts: 8
Here is what I've done so far:

- in deck_joker.h I updated the JokerDeck_N_CARDS to 54 since I have 2 jokers, therefore 54 cards.
- created a JokerDeck_JOKER2 set to 53 and ensured JokerDeckJOKER is 52 as before.
- JokerDeck_IS_JOKER will return true if it is JokerDeckJOKER or JokerDeckJOKER2
- Updated deck_joker.c JokerDeck_cardToString to output Zx for the JokerDeck_JOKER2 card

From there I can create a deck and output it, works fine. But if I try to create a hand, I have an issue.

- The t_jokercardmasks.c file that is generated has 54 cards (Shouldn't it have had 53?)
- The last 2 values in the t_jokercardmasks.c are the same. I presume that means the code can't differentiate between the 2 jokers, therefore can't have both.
- I don't know how to modify mktab_joker.c to generate the table I want. Any help?

Once I have that working, I guess I'll need to update the doStraightTable function to also account for the second joker. Would appreciate help there as well.


Top
 Profile  
 
PostPosted: Tue May 12, 2015 5:17 am 
Offline
New Member

Joined: Tue Jan 27, 2015 4:16 am
Posts: 8
If anyone is following along, I made some progress. I now have a joker deck that supports 2 jokers properly. It can still only evaluate hands with 1 joker, but it is progress! The change was pretty simple:

in deck_joker.h we need to change the suit bit to one with 2 slots <- I guess, still not totally clear on how this works...

Code:
#ifdef WORDS_BIGENDIAN
    uint32 joker   : 2;
    uint32         : 1;
    uint32 spades  : 13;
    uint32         : 3;
    uint32 clubs   : 13;
    uint32         : 3;
    uint32 diamonds: 13;
    uint32         : 3;
    uint32 hearts  : 13;
#else
    uint32 spades  : 13;
    uint32         : 3;
    uint32 clubs   : 13;
    uint32         : 3;
    uint32 diamonds: 13;
    uint32         : 3;
    uint32 hearts  : 13;
    uint32         : 1;
    uint32 joker   : 2;
#endif


Then in mktab_joker.c we need to cover both cards:

Code:
if (i == JokerDeck_JOKER)
      c.cards.joker = (1 << 0);
    else if (i == JokerDeck_JOKER2)
      c.cards.joker = (1 << 1);
    else {


Now I can give it a hand with Zx or Xx and both will evaluate properly. Next step is to update the evaluator to handle hands with 2 jokers. Still seeking advice if anyone has some.


Top
 Profile  
 
PostPosted: Tue May 12, 2015 12:19 pm 
Offline
Site Admin
User avatar

Joined: Sun Feb 24, 2013 9:39 pm
Posts: 642
Sorry to see you are not getting much help. I found pokersource pretty hard going myself and it wouldn't surprise me if others feel the same. I eventually used the 2plus2 evaluator which is faster, and would probably use its approach on other game variants. viewtopic.php?f=24&t=2390


Top
 Profile  
 
PostPosted: Tue May 12, 2015 5:17 pm 
Offline
New Member

Joined: Tue Jan 27, 2015 4:16 am
Posts: 8
spears wrote:
Sorry to see you are not getting much help. I found pokersource pretty hard going myself and it wouldn't surprise me if others feel the same. I eventually used the 2plus2 evaluator which is faster, and would probably use its approach on other game variants. http://www.poker-ai.org/phpbb/viewtopic.php?f=24&t=2390


I have the 2p2 evaluator built and working as well, but it doesn't do jokers. My biggest concern was the expanded size of the lookup table when you add jokers to the mix. Maybe I should just build a separate lookup table for hands with jokers?

I have found it kind of fun digging through pokersource since it is so heavily optimized. Relearning all the crazy C tricks!


Top
 Profile  
 
PostPosted: Tue May 12, 2015 5:47 pm 
Offline
New Member

Joined: Tue Jan 27, 2015 4:16 am
Posts: 8
cartwright wrote:
spears wrote:
Sorry to see you are not getting much help. I found pokersource pretty hard going myself and it wouldn't surprise me if others feel the same. I eventually used the 2plus2 evaluator which is faster, and would probably use its approach on other game variants. http://www.poker-ai.org/phpbb/viewtopic.php?f=24&t=2390


I have the 2p2 evaluator built and working as well, but it doesn't do jokers. My biggest concern was the expanded size of the lookup table when you add jokers to the mix. Maybe I should just build a separate lookup table for hands with jokers?

I have found it kind of fun digging through pokersource since it is so heavily optimized. Relearning all the crazy C tricks!


One other point, you said the 2p2 evaluator is faster, but it seems from the tests run in that original 2p2 thread, it is faster for enumeration, but for random hands (like a monte carlo situation) it isn't faster. My experience as been the same, for me pokersource is quite a bit faster under those conditions.


Top
 Profile  
 
PostPosted: Tue May 12, 2015 7:34 pm 
Offline
Site Admin
User avatar

Joined: Sun Feb 24, 2013 9:39 pm
Posts: 642
You may be right that pokersource is faster - I was just speaking from memory or probably my arse. Could you point me at the benchmark that compares them? Note that there were two versions of the java 2p2 code - The pretty version was about 5 times slower than the more optimised one. http://www.poker-ai.org/archive/www.pok ... &sk=t&sd=a

Also random hands are not particularly realistic for the cases I've encountered. The 2p2 code does minimal work when one only card changes from the last eval - which is the normal case.

I think you and I have a different idea of fun :D


Top
 Profile  
 
PostPosted: Tue May 12, 2015 8:56 pm 
Offline
New Member

Joined: Tue Jan 27, 2015 4:16 am
Posts: 8
spears wrote:
You may be right that pokersource is faster - I was just speaking from memory or probably my arse. Could you point me at the benchmark that compares them? Note that there were two versions of the java 2p2 code - The pretty version was about 5 times slower than the more optimised one. http://www.poker-ai.org/archive/www.pok ... &sk=t&sd=a

Also random hands are not particularly realistic for the cases I've encountered. The 2p2 code does minimal work when one only card changes from the last eval - which is the normal case.

I think you and I have a different idea of fun :D


Steve Brecher make a compairson in the 2p2 thread, his results are below. I pulled this from the XPokerEval package downloaded from codingthewheel.com.

I haven't done any Java development, perhaps some of the optimaztions done would help with C++ as well. I was a full time java developer for a few years and have permanently moved on ;). All of my stuff is C/C++. For the work I'm doing I do both enumeration and random hands, but I'm not building a poker bot exactly. But for sure with enumeration 2p2 is wicked fast.

Code:
LOOKUP/RANDOM1M:
           BAD!! = 0
       High Card = 174303
            Pair = 438408
        Two Pair = 235438
 Three of a Kind = 47901
        Straight = 46004
           Flush = 30026
      Full House = 26001
  Four of a Kind = 1632
  Straight Flush = 287
Total Hands = 1000000

Validation seconds = 0.5780
Total HighPrecision Clocks = 1977461472
HighPrecision clocks per lookup = 1977.461472

HANDEVAL/RANDOM1M:
           BAD!! = 0
       High Card = 174303
            Pair = 438408
        Two Pair = 235438
 Three of a Kind = 47901
        Straight = 46004
           Flush = 30026
      Full House = 26001
  Four of a Kind = 1632
  Straight Flush = 287
Total Hands = 1000000

Validation seconds = 0.0310
Total HighPrecision Clocks = 112314720
HighPrecision clocks per lookup = 112.314720

EVALN/RANDOM1M:
           BAD!! = 0
       High Card = 174303
            Pair = 438408
        Two Pair = 235438
 Three of a Kind = 47901
        Straight = 46004
           Flush = 30026
      Full House = 26001
  Four of a Kind = 1632
  Straight Flush = 287
Total Hands = 1000000

Validation seconds = 0.0310
Total HighPrecision Clocks = 91655640
HighPrecision clocks per lookup = 91.655640

LOOKUP/ENUMERATE:
           BAD!! = 0
       High Card = 23294460
            Pair = 58627800
        Two Pair = 31433400
 Three of a Kind = 6461620
        Straight = 6180020
           Flush = 4047644
      Full House = 3473184
  Four of a Kind = 224848
  Straight Flush = 41584
Total Hands = 133784560

Validation seconds = 0.6400
Total HighPrecision Clocks = 2157517656
HighPrecision clocks per lookup = 16.126806

HANDEVAL/ENUMERATE:
           BAD!! = 0
       High Card = 23294460
            Pair = 58627800
        Two Pair = 31433400
 Three of a Kind = 6461620
        Straight = 6180020
           Flush = 4047644
      Full House = 3473184
  Four of a Kind = 224848
  Straight Flush = 41584
Total Hands = 133784560

Validation seconds = 3.1400
Total HighPrecision Clocks = 10640291544
HighPrecision clocks per lookup = 79.533031

EVALN/ENUMERATE:
           BAD!! = 0
       High Card = 23294460
            Pair = 58627800
        Two Pair = 31433400
 Three of a Kind = 6461620
        Straight = 6180020
           Flush = 4047644
      Full House = 3473184
  Four of a Kind = 224848
  Straight Flush = 41584
Total Hands = 133784560

Validation seconds = 3.2180
Total HighPrecision Clocks = 10912503860
HighPrecision clocks per lookup = 81.567737


Top
 Profile  
 
PostPosted: Wed May 13, 2015 6:02 am 
Offline
New Member

Joined: Tue Jan 27, 2015 4:16 am
Posts: 8
I have something working now. I don't have any data to validate against, but I'm pretty sure it is coming up with the correct results.

First thing I did was update the code to generate a 2nd straight lookup table, one where there are 2 jokers. Then I updated eval_joker.h with a function for handling the 2 joker situation.

An enumeration came up with these results, which do seem reasonable:

Code:
BAD:              0
High Card:        26266380
One Pair:         69844920
Two Pair:         39921192
Trips:            8498332
Straight:         17340116
Flush:            8895804
Full House:       5197776
Quads:            487104
Straight Flush:   642120
Quints:           6816
Total Hands: 177100560


The code to generate the table:

Code:
static void
doDoubleStraightTable(void) {
  int i, j, k;

  MakeTable_begin("doublejokerStraightTable",
                  DBLJST_FILENAME,
                  "uint8",
                  StdDeck_N_RANKMASKS);
  MakeTable_comment(DBLJST_COMMENT_STRING);
  for (i=0; i < StdDeck_N_RANKMASKS; i++) {
    int maxSf = 0, sf;
    for (j=StdDeck_Rank_FIRST; j <= StdDeck_Rank_LAST-1; j++) {
          for (k=StdDeck_Rank_FIRST+1; k <= StdDeck_Rank_LAST; k++) {
            sf = straight_func(i | (1 << j)  | (1 << k));
            if (sf > maxSf)
              maxSf = sf;
          };
    };
   
    MakeTable_outputUInt8(maxSf);
  };

  MakeTable_end();
}


My new eval method looks like this, it is called when 2 jokers are present:
Code:
static inline HandVal
JokerDeck_DoubleJokerRules_EVAL_N(JokerDeck_CardMask cards, int n_cards) {
    uint32 ranks, ss, sh, sd, sc, jrank, jrank2,
    n_ranks, n_dups, two_mask, three_mask, four_mask;
    HandVal retval;
   
    /* OK, we know we have 2 jokers */
   
    ss = JokerDeck_CardMask_SPADES(cards);
    sc = JokerDeck_CardMask_CLUBS(cards);
    sd = JokerDeck_CardMask_DIAMONDS(cards);
    sh = JokerDeck_CardMask_HEARTS(cards);
   
    retval = 0;
    ranks = SC | SD | SH | SS;
    n_ranks = nBitsTable[ranks];
   
    /* Check for straight, flush, or straight flush */
    if (n_ranks >= 3) {
        if (nBitsTable[SS] >= 3) {
            if (doublejokerStraightTable[SS])
                retval = HandVal_HANDTYPE_VALUE(JokerRules_HandType_STFLUSH)
                + HandVal_TOP_CARD_VALUE(doublejokerStraightTable[SS]);
            else
                retval = __flushVal(SS);
        }
        else if (nBitsTable[SC] >= 3) {
            if (doublejokerStraightTable[SC])
                retval = HandVal_HANDTYPE_VALUE(JokerRules_HandType_STFLUSH)
                + HandVal_TOP_CARD_VALUE(doublejokerStraightTable[SC]);
            else
                retval = __flushVal(SC);
        }
        else if (nBitsTable[SD] >= 3) {
            if (doublejokerStraightTable[SD])
                retval = HandVal_HANDTYPE_VALUE(JokerRules_HandType_STFLUSH)
                + HandVal_TOP_CARD_VALUE(doublejokerStraightTable[SD]);
            else
                retval = __flushVal(SD);
        }
        else if (nBitsTable[SH] >= 3) {
            if (doublejokerStraightTable[SH])
                retval = HandVal_HANDTYPE_VALUE(JokerRules_HandType_STFLUSH)
                + HandVal_TOP_CARD_VALUE(doublejokerStraightTable[SH]);
            else
                retval = __flushVal(SH);
        }
        else {
            int st;
           
            st = doublejokerStraightTable[ranks];
            if (st)
                retval = HandVal_HANDTYPE_VALUE(JokerRules_HandType_STRAIGHT)
                + HandVal_TOP_CARD_VALUE(st);
        };
    };
   
    /* OK, lets add 2 aces that aren't already set and see what happens; if
     all the aces are already set, then we have quints and can return */
    jrank = 1 << JokerDeck_Rank_ACE;
    if (!(ss & jrank))
        ss |= jrank;
    else if (!(sc & jrank))
        sc |= jrank;
    else if (!(sd & jrank))
        sd |= jrank;
    else if (!(sh & jrank))
        sh |= jrank;
    else
        return HandVal_HANDTYPE_VALUE(JokerRules_HandType_QUINTS)
        + HandVal_TOP_CARD_VALUE(JokerDeck_Rank_ACE);

    jrank2 = 1 << JokerDeck_Rank_ACE;
    if (!(ss & jrank2))
        ss |= jrank2;
    else if (!(sc & jrank2))
        sc |= jrank2;
    else if (!(sd & jrank2))
        sd |= jrank2;
    else if (!(sh & jrank2))
        sh |= jrank2;
    else
        return HandVal_HANDTYPE_VALUE(JokerRules_HandType_QUINTS)
        + HandVal_TOP_CARD_VALUE(JokerDeck_Rank_ACE);
   
    if (HandVal_HANDTYPE(retval) == JokerRules_HandType_STFLUSH)
        return retval;
   
    ranks |= jrank;
    ranks |= jrank2;
   
    ...
}


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group