├── Interface.md ├── LICENSE ├── Makefile ├── PET_evaluator.h ├── README.md ├── ace_eval ├── ACE_eval.h ├── README.md └── get.sh ├── cactuskev ├── KEV_eval.h ├── README.md ├── get.sh └── source │ ├── README.md │ ├── allfive.c │ └── pokerlib.c ├── getall.sh ├── hammer ├── HAM_eval.h ├── README.md └── get.sh ├── howtoaddevaluators.md ├── howtoruntests.md ├── miniparse.c ├── nul_eval.h ├── perfecthash ├── PHK_eval.c ├── PHK_eval.h ├── README.md └── get.sh ├── pet_utils.h ├── platform.h ├── plot.py ├── pokersource ├── PSE_eval.h ├── README.md └── get.sh ├── runall.sh ├── senzee ├── README.md ├── SNZ_eval.c ├── SNZ_eval.h ├── build_table.c ├── get.sh └── index52c7_patch ├── showdown ├── BHS_eval.h ├── README.md └── get.sh ├── specialk ├── SPK_eval.cpp ├── SPK_eval.h └── get.sh ├── speed_test.c ├── twoplustwo ├── README.md ├── TPT_eval.c ├── TPT_eval.h └── generate_table.c └── wiki └── framework.md /Interface.md: -------------------------------------------------------------------------------- 1 | # PET Interface 2 | 3 | The testbed has the following interface: 4 | (Replace XXX with the 3-letter abbreviation for an evaluator) 5 | 6 | ## Concepts: 7 | 8 | ### Rank: 9 | A numeric representation of the following hand ranks: 10 | Straight Flush, Four of a Kind, Full House, 11 | Flush, Straight, Three of a Kind, Two Pair, 12 | Pair, High Card. 13 | 14 | 15 | Enumerated in `pet_utils.h` as `pet_rank`. An evaluator can use a different representation, as long as it can convert to this enumeration on demand. 16 | 17 | ### FaceValue: 18 | 2 through 10, Jack, Queen, King, Ace. 19 | ### Suit: 20 | Clubs, Diamonds, Hearts, Spades. 21 | 22 | 23 | 24 | Each card has a Suit and FaceValue, enumerated in `pet_utils.h` as `pet_card`; An evaluator should be able to convert to and from this enumeration. 25 | 26 | 27 | 28 | ### Equivalence Class: 29 | Although there are 2,598,960 possible 5-card hands, there are only 7,462 possible results when determining a winning hand. For example, there are 6 possible ways to have a pair of aces with any given kicker *[AcAd AcAh AcAs AdAh AdAs AhAs]*, but they all count the same when finding the winner. All four ways to get a royal flush are equivalent, and have the same equivalency class, as do all 4080 ways of getting the lowest possible hand *(7-5-4-3-2)*. You can list these classes in order: Royal Flush, King High Straight Flush,...Full House Queens over 2s, Full House Jacks over Aces, ... Pair Tens with a K 9 8, ... down to 7 Card High, and assign them each a value in descending order. Then finding winning hands becomes as simple as comparing equivalency values. 30 | 31 | 32 | ## Requirements: 33 | 34 | ### `typedef`s: 35 | - `Card` : A single card representation. Whatever is most effiicent for the evaluator. 36 | - `EquivClass` : Holds the equivalence class of a hand. An evaluator can use any scheme it wishes, as long as a) there are only 7462 possible values, and b) larger values beat smaller ones. 37 | 38 | ### `#define`: 39 | - `XXXHAND` : An integer. The `addcard` and `evaluate` will operate on a "Hand" of size `Card[XXXHAND]`. (This size may be more or less than 7 Cards, it should be the most efficient representation for the evaluator). 40 | 41 | ### Methods (functions or macros) 42 | 43 | - `int XXXX_init(void);` //Any one-time setup, return 0 on success 44 | - `Card XXX_makecard(pet_card c);` //turns an enumeration from 0..51 into internal card representation. 45 | - `void XXX_addcard(Card*, Card);` //Adds a card to a Hand. Called 7 times. 46 | - `EquivClass XXX_evaluate(Card*);` //Evaluates hand, returns a Equivalence Class. 47 | - `int XXX_rank(EquivClass);` //returns the hand rank. An internal representation of Rank, must be between 0 and 9 inclusive. 48 | - `pet_rank XXX_ranktran(int rank);` //converts internal rank to pet_rank (see pet_utils.h) 49 | - `pet_rank XXX_decode(Hand, EquivClass, int* out);` //Fills `out` with the 5 winning cards from the hand, returns the rank. 50 | - `void XXX_cleanup();` //frees any resurces from init. 51 | 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2105 Adam Shelly (github.com/ashelly) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBS = -lrt 2 | FLAGS = -O3 -s 3 | #FLAGS = -g 4 | 5 | all: ace tpt kev bhs pse snz phk ham null_eval 6 | 7 | tables: tpt_tables.dat ham_table1.dat snz_tables.dat 8 | 9 | null_eval: 10 | gcc -DTESTNUL=1 $(LIBS) $(FLAGS) speed_test.c -onull_eval 11 | null_sz: 12 | gcc -DTESTNUL=1 -s $(FLAGS) miniparse.c -onull_sz 13 | 14 | 15 | ace: 16 | gcc -DTESTACE=1 $(LIBS) $(FLAGS) speed_test.c ace_eval/source/ace_eval_best.c -oace 17 | ace_sz: 18 | gcc -DTESTACE=1 -s $(FLAGS) miniparse.c ace_eval/source/ace_eval_best.c -oace_sz 19 | 20 | tpt: tpt_tables.dat 21 | gcc -DTESTTPT=1 $(LIBS) $(FLAGS) speed_test.c twoplustwo/TPT_eval.c -otpt 22 | 23 | tpt_tables.dat: 24 | gcc twoplustwo/generate_table.c --std=c99 perfecthash/source/fast_eval.c cactuskev/source/pokerlib.c -otwoplustwo/generate_table && twoplustwo/generate_table 25 | 26 | kev: 27 | gcc -DTESTKEV=1 $(LIBS) $(FLAGS) speed_test.c cactuskev/source/pokerlib.c -okev 28 | 29 | phk: 30 | gcc -DTESTPHK=1 $(LIBS) $(FLAGS) speed_test.c perfecthash/PHK_eval.c perfecthash/source/fast_eval.c cactuskev/source/pokerlib.c -ophk 31 | 32 | hes: 33 | gcc -DTESTHES=1 $(LIBS) $(FLAGS) speed_test.c showdown/HandEval.c -ohes 34 | 35 | bhs: 36 | gcc -DTESTBHS=1 $(LIBS) $(FLAGS) speed_test.c showdown/source/HandEval.c -obhs 37 | bhs_sz: 38 | gcc -DTESTBHS=1 -s $(FLAGS) miniparse.c showdown/source/HandEval.c -obhs_sz 39 | 40 | pse: 41 | gcc -DTESTPSE=1 $(LIBS) $(FLAGS) speed_test.c -static -Lpokersource/source/lib/.libs -Ipokersource/source/include -lpoker-eval -opse 42 | 43 | snz: snz_tables.dat 44 | gcc -DTESTSNZ=1 $(LIBS) $(FLAGS) speed_test.c senzee/SNZ_eval.c -osnz 45 | 46 | snz_tables.dat: 47 | gcc -DTESTSNZ=1 $(LIBS) $(FLAGS) senzee/build_table.c cactuskev/source/pokerlib.c -osenzee/build_table 48 | 49 | ham: ham_table1.dat 50 | gcc -DTESTHAM=1 $(LIBS) $(FLAGS) speed_test.c hammer/source/handeval/handevaluator.c -oham 51 | 52 | ham_table1.dat: 53 | cp hammer/source/handeval/eqcllist ./ham_table1.dat && cp hammer/source/handeval/carddag ./ham_table2.dat 54 | 55 | 56 | specialk/libspecialk.a: 57 | g++ -c specialk/SPK_eval.cpp -o specialk/SPK_eval.o 58 | g++ -c specialk/source/SevenEval.cpp -o specialk/SevenEval.o 59 | g++ -c specialk/source/FiveEval.cpp -o specialk/FiveEval.o 60 | ar crf specialk/libspecialk.a specialk/SevenEval.o specialk/FiveEval.o specialk/SPK_eval.o 61 | 62 | 63 | spk: specialk/libspecialk.a 64 | g++ -DTESTSPK=1 -lstdc++ $(LIBS) $(FLAGS) -x c speed_test.c -Lspecialk -lspecialk -Ispecialk/source -ospk 65 | 66 | 67 | clean: 68 | rm ??? 69 | -------------------------------------------------------------------------------- /PET_evaluator.h: -------------------------------------------------------------------------------- 1 | /* Evaluator selector for PET testbed 2 | Sets the "Evaluator Under Test"(EUT) and includes the adapter file 3 | 4 | compile with -DTESTXXX to include evaluator xxx. 5 | 6 | (c) 2015 Adam Shelly (github.com/ashelly) 7 | 8 | */ 9 | 10 | 11 | #if TESTNUL 12 | #include "nul_eval.h" 13 | #define EUT NUL 14 | 15 | #elif TESTACE 16 | #include "ace_eval/ACE_eval.h" 17 | #define EUT ACE 18 | 19 | #elif TESTTPT 20 | #include "twoplustwo/TPT_eval.h" 21 | #define EUT TPT 22 | 23 | #elif TESTKEV 24 | #include "cactuskev/KEV_eval.h" 25 | #define EUT KEV 26 | 27 | #elif TESTHES 28 | #include "showdown/HES_eval.h" 29 | #define EUT HES 30 | 31 | #elif TESTBHS 32 | #include "showdown/BHS_eval.h" 33 | #define EUT BHS 34 | 35 | #elif TESTPSE 36 | #include "pokersource/PSE_eval.h" 37 | #define EUT PSE 38 | 39 | #elif TESTSNZ 40 | #include "senzee/SNZ_eval.h" 41 | #define EUT SNZ 42 | 43 | #elif TESTPHK 44 | #include "perfecthash/PHK_eval.h" 45 | #define EUT PHK 46 | 47 | #elif TESTHAM 48 | #include "hammer/HAM_eval.h" 49 | #define EUT HAM 50 | 51 | #elif TESTSPK 52 | #include "specialk/SPK_eval.h" 53 | #define EUT SPK 54 | 55 | /*Copy the following 3 lines, paste above this, and replace 'XXX' and 'newdir' */ 56 | #elif TESTXXX 57 | #include "newdir/XXX_eval.h" 58 | #define EUT XXX 59 | 60 | #endif 61 | 62 | /* These macros transform evaluator calls to PET_method 63 | to calls to XXX_Method, where XXX is the EvaluatorUnderTest. 64 | */ 65 | 66 | 67 | #define STRINGIZE(a) #a 68 | #define PET_STR(a) STRINGIZE(a) 69 | #define PET_CONCAT(a,b) a ## b 70 | #define PET_MAKENAME(a,b) PET_CONCAT(a,b) 71 | #define EUT_NAME(b) PET_MAKENAME(EUT,b) 72 | 73 | #define PETHAND EUT_NAME(HAND) 74 | #define PET_init EUT_NAME(_init) 75 | #define PET_makecard EUT_NAME(_makecard) 76 | #define PET_addcard EUT_NAME(_addcard) 77 | #define PET_evaluate EUT_NAME(_evaluate) 78 | #define PET_rank EUT_NAME(_rank) 79 | #define PET_ranktran EUT_NAME(_ranktran) 80 | #define PET_decode EUT_NAME(_decode) 81 | #define PET_DECODES_CARDS EUT_NAME(_DECODES_CARDS) 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Poker Evaluator Testbed 2 | ================== 3 | 4 | ##PET: A Roundup and Speed Comparison of Seven Card Poker Hand Evaluators 5 | 6 | --------------------------------------------- 7 | 8 | ##What's This? 9 | A open test framework and speed comparison of seven card poker hand evaluators in C and C++. Measuring the state-of-the art in open-source hand evaluators. Inspired by James Devlin's [apparently defunct](http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup) 10 | five card roundup at [CodingTheWheel](https://web.archive.org/web/20140717015339/http://codingthewheel.com/archives/poker-hand-evaluator-roundup/). 11 | 12 | 13 | ##What do I do with it? 14 | 15 | 1. [View existing results](https://github.com/ashelly/pet/wiki/results) 16 | 17 | ![Sample Results](https://cloud.githubusercontent.com/assets/3831873/7707508/28546af2-fe1e-11e4-8f36-2b2eac8deb51.png "Example") 18 | 19 | 2. [Contribute test results from your machine](howtoruntests.md) 20 | 21 | 3. [Add more evaluators](howtoaddevaluators.md) 22 | 23 | 4. Create more test scenarios. 24 | 25 | 26 | ##How does it work? 27 | 28 | The framework provides a C adaptor for each evaluator to give it a uniform interface. Each Evaluator Under Test (EUT) is assigned a unique 3 letter abbreviation, and implements the api defined in `PET_evaluator.h`. A set of macros in that header translates the API names to the evaluator's names so that we can use the same test harness for all of them. See Interface.md for more details 29 | 30 | 31 | It runs each evaluator through two tests, and times the performance for: 32 | 33 | - Enumerating every possible 7 card hand. 34 | - This test includes the cost of adding cards to the hand. 35 | - Enumerating lots (50,000,000 by default) of random hands. 36 | - This one creates the hands and calls `addcard` outside the timing loop. 37 | 38 | There is a script to collect and plot the results. `python plot.py trials` 39 | 40 | 41 | ## Evaluators Tested: 42 | 43 | - [CactusKev](cactuskev/README.md) 44 | - [TwoPlusTwo](twoplustwo/README.md) 45 | - [Paul Senzee's 7-Card](senzee/README.md) 46 | - [Steve Brecher's Holdem Showdown](showdown/README.md) 47 | - [PokerSource](pokersource/README.md) 48 | - [Ace Eval](ace_eval/README.md) 49 | - [Moritz Hammer](hammer/README.md) 50 | - [PerfectHash for CactusKev](perfecthash/README.md) 51 | 52 | ## Coming In The Future 53 | - [SpecialK](specialk/README.md) 54 | - [PokerStove](pokerstove/README.md) 55 | 56 | 57 | ### Licence 58 | The PET code is open source, available under the terms of the MIT License (see LICENSE). Individual evaluators are licenced under their original terms, see README files in each subidrectory. 59 | 60 | -------------------------------------------------------------------------------- /ace_eval/ACE_eval.h: -------------------------------------------------------------------------------- 1 | // PET adaptor for ACE_eval 2 | // (c) 2015 Adam Shelly (github.com/ashelly) 3 | #include 4 | #include "../pet_utils.h" 5 | 6 | typedef uint32_t Card; 7 | typedef uint32_t EquivClass; 8 | 9 | 10 | #define ACEHAND 5 11 | #define ACE_init() (0) 12 | static inline Card ACE_makecard(int i){return 1<<(2*(i%13)+6)|1<<(i/13);} 13 | static inline void ACE_addcard(Card* h, Card c){ h[c&7]+=c; h[3]|=c; } 14 | #define ACE_evaluate(h) E((h)) 15 | #define ACE_rank(v) ((v)>>28) 16 | #define ACE_ranktran(r) ((r)<8?(r)+1:((r)==9?9:0)) 17 | 18 | 19 | //remove a card of `cardrank` from `hand`, return it. 20 | static inline pet_card PullCardFromHand(int cardrank, Card* hand){ 21 | int suit, rank_mask = ACE_makecard(cardrank)&~0x3F; //card less suit bits 22 | for (suit=0;suit<4;suit++) { 23 | int suit_bit = (1<0 && i<5;) { //extract 5 bits downto 0 46 | if (v & (1<>1; //straights only set the high bit, but we need 5. 55 | } 56 | } 57 | } 58 | reps/=4; //next counter 59 | } 60 | } 61 | } 62 | return rank; 63 | } 64 | #define ACE_DECODES_CARDS 1 //True if decode fills `out` with cards 65 | -------------------------------------------------------------------------------- /ace_eval/README.md: -------------------------------------------------------------------------------- 1 | # AShelly's Card Evaluator 2 | ## ACE 3 | 4 | Source: https://github.com/ashelly/ACE_eval.git 5 | 6 | 7 | - Card Representation: 32 bit: `0a0k0q0j0t09080706050403020..SHDC` 8 | one bit for face value, one bit for suit 9 | 10 | 11 | - Hand Representation: 5 Card reps. 12 | One for each suit, plus one with all card OR'd together. 13 | 14 | 15 | - Equivalence Class: `RRRR..AKQJT98765432akqjt98765432`. 16 | 4 bit rank, one bit for each value card, one bit for each kicker. 17 | 18 | Ranks: 19 | - 0:"High Card" 20 | - 1:"One Pair 21 | - 2""Two Pair", 22 | - 3:"Three of a Kind", 23 | - 4:"Straight", 24 | - 5:"Flush", 25 | - 6:"Full House", 26 | - 7:"Four of a Kind", 27 | - 8:"unused", 28 | - 9:"Straight Flush" 29 | 30 | 31 | A tiny table-less evaluator that uses "sideways addition" to generate counts of each face value in parallel. This allows fast detection of everything but straights and flushes. Flushes are found by inspecting the count of each suit. Straights are found by shifting and AND'ing the value bits 4 times, which clears them unless there are 5 in a row. As part of detecting the hand rank, the evaluator extracts the bits which determine the value. From there, it is easy to find the remaining kicker cards, and so it is possible to find the hand description (i.e. "Aces over Tens with a King kicker") directly from the returned Equivalence class. See the README.md for more info. 32 | -------------------------------------------------------------------------------- /ace_eval/get.sh: -------------------------------------------------------------------------------- 1 | git clone https://github.com/ashelly/ACE_eval.git source 2 | -------------------------------------------------------------------------------- /cactuskev/KEV_eval.h: -------------------------------------------------------------------------------- 1 | // PET adaptor for CactusKev's evaluator 2 | // http://www.suffecool.net/poker/evaluator.html 3 | // 4 | // (c) 2015 Adam Shelly (github.com/ashelly) 5 | 6 | #include 7 | #include "../pet_utils.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #define Card uint32_t 14 | #define EquivClass uint16_t 15 | 16 | #define KEVHAND 8 17 | 18 | #define KEV_init() (0) 19 | #define KEV_addcard(hand,card) ((hand)[(hand)[7]++]=(card)) 20 | #define KEV_evaluate(hand) eval_7hand(hand) 21 | #define KEV_rank(v) hand_rank(v) 22 | #define KEV_ranktran(r) ((10-(r))%10) 23 | 24 | #define KEV_decode(h,v,out) KEV_ranktran(KEV_rank(v)) 25 | #define KEV_DECODES_CARDS 0 //True if decode fills `out` with cards 26 | 27 | extern int primes[]; 28 | static inline Card KEV_makecard(pet_card c){ 29 | int rank=c%13, suit = 0x8000>>(c/13); 30 | return primes[rank] | (rank << 8) | suit | (1 << (16+rank)); 31 | } 32 | 33 | 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /cactuskev/README.md: -------------------------------------------------------------------------------- 1 | # CactusKev Evaluator 2 | ## KEV 3 | 4 | Source: http://suffecool.net/poker/evaluator.html 5 | 6 | 7 | - Card Representation: 32 bit: `...AKQJT98765432CDHSrrrrxxpppppp` 8 | r = rank[0..12],p= prime (see below). 9 | - Hand Representation: 7 Cards 10 | - Hand Value: 1...7462 , 1 is best. 11 | 12 | 13 | A five-card evaluator. Introduced the idea of equivalency classes. Kev's concept was to find a fast way to map each set of cards to the equivalent value. He does this by assigning each card rank a prime number p: '2' is 2, '3' is 3, '4' is 5, upto 'A' which is 41. By multiplying each card's prime together you get a unique number. The largest possible product is `41*41*41*41*37 = 104,553,157`. You could implement a 104 million entry array and store the equivalence value directly, but that doesn't do nice things to your cache. It's also fairly sparse, so a lot of the space would be wasted. Instead, he does a binary search on a table of products, and uses the index of the match to lookup the value. 14 | This scheme can not detect flushes, so part of the card representation is a bit for the suit, and a bit for each rank. If all five cards have the same suit bit set, it's a flush, and the 13 rank bits are used as a lookup into a 'flushes' table'. All the rest of the possible hand classes can be found by the product of primes scheme, but Kev makes it faster by using the 13 rank bits as a lookup into a 'unique5' table which returns the hand class if all five cards are different. 15 | Unfortunately, this evaluator does not scale well to a 7 card hand (the largest product becomes 143,133,271,933 and you can now have a flush with more than 1 suit in your hand). So it evaluates 7 card hands by running the 5 card evaluator on the 21 possible permutations of 5 cards. This drops it to near the bottom of the speed test. 16 | -------------------------------------------------------------------------------- /cactuskev/get.sh: -------------------------------------------------------------------------------- 1 | #Source: http://www.suffecool.net/poker/evaluator.html 2 | mkdir -p source 3 | cd source 4 | wget http://www.suffecool.net/poker/code/poker.h 5 | wget http://www.suffecool.net/poker/code/pokerlib.c 6 | wget http://www.suffecool.net/poker/code/arrays.h 7 | wget http://www.suffecool.net/poker/code/allfive.c 8 | wget http://www.suffecool.net/poker/code/Makefile 9 | cd .. 10 | 11 | -------------------------------------------------------------------------------- /cactuskev/source/README.md: -------------------------------------------------------------------------------- 1 | All files in this directory (c) Kevin Suffecool (aka Cactus Kev) 2 | originally hosted at http://www.suffecool.net/poker/evaluator.html 3 | -------------------------------------------------------------------------------- /cactuskev/source/allfive.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "poker.h" 3 | 4 | /*************************************************/ 5 | /* */ 6 | /* This code tests my evaluator, by looping over */ 7 | /* all 2,598,960 possible five card hands, cal- */ 8 | /* culating each hand's distinct value, and dis- */ 9 | /* playing the frequency count of each hand type */ 10 | /* */ 11 | /* Kevin L. Suffecool, 2001 */ 12 | /* suffecool@bigfoot.com */ 13 | /* */ 14 | /*************************************************/ 15 | 16 | main() 17 | { 18 | int deck[52], hand[5], freq[10]; 19 | int a, b, c, d, e, i, j; 20 | 21 | // seed the random number generator 22 | srand48( getpid() ); 23 | 24 | // initialize the deck 25 | init_deck( deck ); 26 | 27 | // zero out the frequency array 28 | for ( i = 0; i < 10; i++ ) 29 | freq[i] = 0; 30 | 31 | // loop over every possible five-card hand 32 | for(a=0;a<48;a++) 33 | { 34 | hand[0] = deck[a]; 35 | for(b=a+1;b<49;b++) 36 | { 37 | hand[1] = deck[b]; 38 | for(c=b+1;c<50;c++) 39 | { 40 | hand[2] = deck[c]; 41 | for(d=c+1;d<51;d++) 42 | { 43 | hand[3] = deck[d]; 44 | for(e=d+1;e<52;e++) 45 | { 46 | hand[4] = deck[e]; 47 | 48 | i = eval_5hand( hand ); 49 | j = hand_rank(i); 50 | freq[j]++; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | for(i=1;i<=9;i++) 58 | printf( "%15s: %8d\n", value_str[i], freq[i] ); 59 | } 60 | -------------------------------------------------------------------------------- /cactuskev/source/pokerlib.c: -------------------------------------------------------------------------------- 1 | #include 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 | } 206 | -------------------------------------------------------------------------------- /getall.sh: -------------------------------------------------------------------------------- 1 | for dir in `find . -type f -name "get.sh" -print | cut -d '/' -f2` ; do 2 | cd $dir 3 | ./get.sh 4 | cd .. 5 | done 6 | 7 | -------------------------------------------------------------------------------- /hammer/HAM_eval.h: -------------------------------------------------------------------------------- 1 | // PET adaptor for Moritz Hammer's evaluator 2 | // http://www.pst.ifi.lmu.de/~hammer/poker/handeval.html 3 | // 4 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 5 | 6 | #include 7 | #include "../pet_utils.h" 8 | #include "source/handeval/handevaluator.h" 9 | 10 | typedef unsigned char Card; 11 | typedef handeval_eq_class* EquivClass; 12 | typedef Card* Hand; 13 | 14 | 15 | #define HAMHAND 8 16 | static int HAM_init() { 17 | //todo: add local version of these loaders with error checking and combined file 18 | load_equivalenceclasses("./ham_table1.dat"); 19 | load_dag("./ham_table2.dat"); 20 | return 0; 21 | } 22 | static inline Card HAM_makecard(int i){return (((i%13)<<2)|(i/13));} 23 | static inline void HAM_addcard(Hand h, Card c){ h[h[7]++]=c;} 24 | #define HAM_evaluate(h) calculate_equivalence_class((h)) 25 | #define HAM_rank(v) ((v)->type) 26 | #define HAM_ranktran(r) ((r)+1) 27 | 28 | 29 | static inline pet_card ham2pet(char c) { 30 | return (c>>2)+(13*(c&3)); 31 | } 32 | 33 | const char* ham_valstr="23456789TJQKA"; 34 | static inline pet_card pull_card_from_hand(char cardstr, Hand hand) 35 | { 36 | short i,cardval; 37 | for (cardval=0;ham_valstr[cardval]!=cardstr;cardval++) { 38 | if (cardval>12) { return (pet_card)-1; } //error 39 | } 40 | for (i=0;i>2)==cardval){ 42 | pet_card retval = ham2pet(hand[i]); 43 | hand[i]=hand[--(hand[7])]; 44 | return retval; 45 | } 46 | } 47 | return (pet_card)-1; 48 | } 49 | 50 | static inline pet_rank HAM_decode(Card* hand,EquivClass v, pet_card* out) 51 | { 52 | int i; 53 | pet_rank rank=HAM_ranktran(HAM_rank(v)); 54 | if (out){ 55 | Card h[HAMHAND]; 56 | memcpy(h,hand,sizeof(h)); 57 | for (i=0;i<5;i++){ 58 | unsigned char cardstr = v->cards[i]; 59 | out[i]=pull_card_from_hand(cardstr,h); 60 | } 61 | } 62 | return rank; 63 | } 64 | #define HAM_DECODES_CARDS 1 //True if decode fills `out` with cards 65 | -------------------------------------------------------------------------------- /hammer/README.md: -------------------------------------------------------------------------------- 1 | # Moritz Hammer's Hand Evaluator 2 | ## HAM 3 | 4 | Source: http://www.pst.ifi.lmu.de/~hammer/poker/handeval.html 5 | 6 | 7 | 8 | - Card Representation: 8 bit: `..ffffss` 9 | 4 bits for face value, 2 bits for suit. 2 spare 10 | - Hand Representation: 7 Cards 11 | - Hand Value: 1...7462 , 1 is best. 12 | 13 | 14 | 15 | Uses a Directed Acyclic Graph (DAG) to walk through a precomputed graph of card sets, arriving at the final hand and equivalence class. Think of it as 7 steps of asking "if I start with this set of cards and add this new card, what set of cards do I have now?" For example, adding a King to a pair of Jacks arrives at the same place as adding a Jack to a King,Jack. This is essentially the same technique the TwoPlusTwo evaluator uses, but it only selects of face value, reducing the table size drastically. It uses a separate count of each suit to do flush detection. -------------------------------------------------------------------------------- /hammer/get.sh: -------------------------------------------------------------------------------- 1 | mkdir -p source 2 | wget http://www.pst.ifi.lmu.de/~hammer/poker/handeval.tar.gz 3 | gunzip handeval.tar.gz 4 | tar -xvf handeval.tar 5 | mv handeval source 6 | rm handeval.tar 7 | -------------------------------------------------------------------------------- /howtoaddevaluators.md: -------------------------------------------------------------------------------- 1 | # How to add evaluators. 2 | 3 | 1. Fork this repository. 4 | 5 | 2. Pick a 3 letter abbreviation for your evaluator. Use that abbreviation everywhere you see 'xxx' below. 6 | 7 | 3. Make a subdirectory for your evaluator. (Use a long name, not the 3 letter name). Change to it. 8 | 4. Create a `get.sh`. Make it executable. 9 | It must contain commands to fetch the evaluator from the web, and place it in a subdirectory named `source`. (Use git clone, wget,...) See existing directories for examples. Test it. Add `get.sh` to a commit. 10 | 11 | 5. Create a `XXX_eval.h` This file will map the specifics of your evaluator's implementation to the generic test interface. 12 | - The file should `#include "..\pet_utils.h` 13 | - The file must define the following 2 types: 14 | Card, EquivClass 15 | - It must #define XXXHAND which contains the count of Card sized objects needed to represent a Hand 16 | - It must contain these 6 functions/macros. 17 | - int XXX_init() 18 | - Card XXX_makecard(in) 19 | - void XXX_addcard(Hand,Card) 20 | - EquivClass XXX_evaluate(Hand) 21 | - int XXX_rank(EquivClass) 22 | - pet_rank XXX_ranktran(int) 23 | See [the Interface document](Interface.md) for more details. 24 | Add this file to the commit. 25 | 26 | 6. Edit `PET_evaluator.h` to add your code. This is the file that redirects calls to PET_* to your evaluator. Just copy and paste the 3 lines starting with `elif TESTXXX`, replacing XXX with your abbreviation. 27 | Add this file to the commit. 28 | 29 | 7. Edit the `Makefile`. Copy an existing entry as an example. It is important to change "-DTESTXXX=1" and "-oxxx" for your evaluator. Also add your abbreviation to the "all" line. 30 | Run `make all` and ensure your code builds cleanly. Run ./xxx to ensure it runs without errors. 31 | Add the makefile to the commit. 32 | 33 | 8. If there is a data file, Also add a Makefile line to create the file if needed, and to copy it to the root with name xxx_tables.dat. Make sure the `XXX_init()` function uses this file. 34 | 35 | 9. See [How To Run Tests](howtoruntests.md) to compare your evaluator to the rest. 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /howtoruntests.md: -------------------------------------------------------------------------------- 1 | # How To Run Tests. 2 | 3 | ### Linux 4 | 5 | Clone this repository. 6 | Change to the cloned directory. 7 | 8 | ~~~~ 9 | ./getall.sh 10 | make tables 11 | make all 12 | ./runall.sh 13 | ./post_results.sh #(TBD - would like to script a pull request of results.txt and plot output) 14 | ~~~~ 15 | 16 | ### Mac *(untested)* 17 | 18 | The same as Linux? 19 | 20 | 21 | ### PC 1 *(untested)* 22 | 23 | 24 | Install cygwin. 25 | Follow Linux instructions. 26 | 27 | 28 | ### PC 2 *(unimplemented)* 29 | Fork this repository, Port it to PC, Make a pull request. 30 | Then something like 31 | 32 | ~~~~ 33 | getall.bat 34 | nmake 35 | runall.bat 36 | post_results.bat 37 | ~~~~ 38 | -------------------------------------------------------------------------------- /miniparse.c: -------------------------------------------------------------------------------- 1 | //minimal evaluator for PET. 2 | // usage: echo "AH KH 2D 3D 4D 4C AD" | ./a.out 3 | // returns hand rank and equivalence class 4 | // 5 | // (c) 2015 Adam Shelly (github.com/ashelly) 6 | 7 | #include 8 | #include 9 | #include "PET_evaluator.h" 10 | char*S="CDHS23456789TJQKA",b[32],*c; 11 | Card hand[PETHAND]={0}; 12 | main(int C){ 13 | PET_init(); 14 | c=gets(&b[0]); 15 | while (*c){ 16 | int rank=strchr(S,*c++)-S-4; 17 | int suit=strchr(S,*c++)-S; 18 | Card card = PET_makecard(rank+suit*13); 19 | c++; 20 | PET_addcard(hand,card); 21 | } 22 | EquivClass v =PET_evaluate(hand); 23 | printf("%s\n",pet_rank_str[PET_ranktran(PET_rank(v))]); 24 | printf("%08x \n",(unsigned)v); 25 | } 26 | -------------------------------------------------------------------------------- /nul_eval.h: -------------------------------------------------------------------------------- 1 | //null evaluator for PET 2 | // non-functional 3 | // used to determine minimum size of evaluation code 4 | // 5 | // (c) 2015 Adam Shelly (github.com/ashelly) 6 | 7 | #include "pet_utils.h" 8 | 9 | #define Card int 10 | #define EquivClass int 11 | 12 | 13 | #define NULHAND 1 14 | #define NUL_init() (0) 15 | #define NUL_makecard(i) (i) 16 | 17 | #define NUL_addcard(h,c) ((h)[0]+=(c)) 18 | #define NUL_evaluate(h) (h[0]) 19 | #define NUL_rank(v) ((v)>>24) 20 | #define NUL_ranktran(r) (((r)<9)?(r):9) 21 | #define NUL_DECODES_CARDS 0 22 | 23 | static inline int NUL_decode(Card* h, EquivClass v, pet_card* out) 24 | { 25 | int rank=NUL_ranktran(NUL_rank(v)); 26 | return rank; 27 | } 28 | -------------------------------------------------------------------------------- /perfecthash/PHK_eval.c: -------------------------------------------------------------------------------- 1 | // PET adaptor for Paul Senzee's Perfect Hash optimization of CactusKev's evaluator 2 | // http://www.suffecool.net/poker/evaluator.html 3 | // http://www.paulsenzee.com/2006/06/some-perfect-hash.html 4 | // 5 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 6 | 7 | #include "PHK_eval.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | 14 | extern int perm7[21][5]; 15 | extern int eval_5hand_fast(int c1, int c2, int c3, int c4, int c5); 16 | 17 | // This is a non-optimized method of determining the 18 | // best five-card hand possible out of seven cards. 19 | // 20 | short 21 | eval_7hand_fast( int *hand ) 22 | { 23 | int i, j, q, best = 9999, subhand[5]; 24 | 25 | for ( i = 0; i < 21; i++ ) 26 | { 27 | q = eval_5hand_fast( hand[perm7[i][0]], 28 | hand[perm7[i][1]], 29 | hand[perm7[i][2]], 30 | hand[perm7[i][3]], 31 | hand[perm7[i][4]]); 32 | if ( q < best ) 33 | best = q; 34 | } 35 | return( best ); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /perfecthash/PHK_eval.h: -------------------------------------------------------------------------------- 1 | //PET adaptor for Paul Senzee's perfect hash optimization of CactusKev's Evaluator 2 | // http://www.suffecool.net/poker/evaluator.html 3 | // http://www.paulsenzee.com/2006/06/some-perfect-hash.html 4 | // 5 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 6 | 7 | #include 8 | #include "../pet_utils.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #define Card uint32_t 15 | #define EquivClass uint16_t 16 | 17 | #define PHKHAND 8 18 | 19 | #define PHK_init() (0) 20 | #define PHK_addcard(hand,card) ((hand)[(hand)[7]++]=(card)) 21 | #define PHK_evaluate(hand) eval_7hand_fast(hand) 22 | #define PHK_rank(v) hand_rank(v) 23 | #define PHK_ranktran(r) ((10-(r))%10) 24 | 25 | #define PHK_decode(h,v,out) PHK_ranktran(PHK_rank(v)) 26 | #define PHK_DECODES_CARDS 0 //True if decode returns cards in `out` 27 | 28 | extern int primes[]; 29 | static inline Card PHK_makecard(pet_card c){ 30 | int rank=c%13, suit = 0x8000>>(c/13); 31 | return primes[rank] | (rank << 8) | suit | (1 << (16+rank)); 32 | } 33 | 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /perfecthash/README.md: -------------------------------------------------------------------------------- 1 | # Paul Senzee's Perfect Hash Optimization of CactusKev's Evaluator 2 | ## PHK 3 | 4 | Source : http://www.paulsenzee.com/2006/06/some-perfect-hash.html 5 | 6 | - Card Representation: 32 bit: `...AKQJT98765432CDHSrrrrxxpppppp` 7 | r = rank[0..12],p= prime (see below). 8 | - Hand Representation: 7 Cards 9 | - Hand Value: 1...6xxx , 1 is best. 10 | (Same as CactusKev). 11 | 12 | 13 | Paul took the CactusKev evaluator and found a way to get rid of the binary search step at the end. He takes the product of the primes, and runs it through a perfect hash, which maps each of the significant values in the 100 million value range to a unique value in a 6K range. Looking up the hashed value gives the equivalancy class directly. He claims a 2.7x speedup over the original. However, this still suffers from its inability to do 7 card hands directly; it also has to iterate over the 21 possible combinations of 5 cards. 14 | 15 | 16 | -------------------------------------------------------------------------------- /perfecthash/get.sh: -------------------------------------------------------------------------------- 1 | mkdir -p source 2 | cd source 3 | wget http://www.psenzee.com/code/fast_eval.c 4 | cd .. 5 | -------------------------------------------------------------------------------- /pet_utils.h: -------------------------------------------------------------------------------- 1 | /* Basic types and Utilities for PET: Poker Evaluator Test 2 | 3 | (c) 2014-2015 Adam Shelly (github.com/ashelly) 4 | 5 | */ 6 | #ifndef PETUTILS_H 7 | #define PETUTILS_H 8 | #include 9 | 10 | //card values 11 | typedef enum card_t { 12 | c2C, c3C, c4C, c5C, c6C, c7C, c8C, c9C, cTC, cJC, cQC, cKC, cAC, 13 | c2D, c3D, c4D, c5D, c6D, c7D, c8D, c9D, cTD, cJD, cQD, cKD, cAD, 14 | c2H, c3H, c4H, c5H, c6H, c7H, c8H, c9H, cTH, cJH, cQH, cKH, cAH, 15 | c2S, c3S, c4S, c5S, c6S, c7S, c8S, c9S, cTS, cJS, cQS, cKS, cAS 16 | } pet_card; 17 | 18 | static const char* pet_card_str[] = { 19 | "Two","Three","Four", 20 | "Five","Six","Seven", 21 | "Eight","Nine","Ten", 22 | "Jack","Queen","King","Ace" 23 | }; 24 | static const char* pet_suit_str[] = { 25 | "Clubs","Diamonds","Hearts","Spades" 26 | }; 27 | 28 | //hand ranks 29 | typedef enum rank_t { 30 | rInvalid, 31 | rHighCard, rPair, rTwoPair, 32 | rThreeOfaKind, rStraight, rFlush, 33 | rFullHouse, rFourOfaKind, rStraightFlush 34 | } pet_rank; 35 | 36 | static const char *pet_rank_str[] = { 37 | "BAD", 38 | "High Card", 39 | "One Pair", 40 | "Two Pair", 41 | "Three of a Kind", 42 | "Straight", 43 | "Flush", 44 | "Full House", 45 | "Four of a Kind", 46 | "Straight Flush" 47 | }; 48 | 49 | 50 | //populates `out` with upto `n` chars describing card `c` 51 | //ex: "Ace of Spades" 52 | //returns !0 if n is not big enough 53 | static inline int pet_card_longname(pet_card c, char* out, int n){ 54 | int r = snprintf(out, n,"%s of %s",pet_card_str[c%13],pet_suit_str[c/13]); 55 | return r<0||r>n; 56 | } 57 | 58 | //populates `out` with min(2,`n)` chars describing card `c` 59 | //ex: "AS" 60 | //returns !0 if n is not big enough 61 | static inline int pet_card_name(pet_card c, char* out, int n){ 62 | int r = snprintf(out, n,"%c%c","23456789TJQKA"[c%13],"CDHS"[c/13]); 63 | return r<0||r>n; 64 | } 65 | 66 | //returns card face value 0=2 .. 13=Ace 67 | static inline short petcard_value(pet_card c) { return c%13;} 68 | //returns card suit: 0..3 = C..S 69 | static inline short petcard_suit(pet_card c) { return c/13;} 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /platform.h: -------------------------------------------------------------------------------- 1 | //File for platform specific functions 2 | // Add others as needed 3 | // 4 | // (c) 2014 Adam Shelly (github.com/ashelly) 5 | #include 6 | typedef struct timespec sysTime_t; 7 | #define MS_PER_SEC 1000.0f 8 | 9 | //** Linux specific high-precision timing**/ 10 | 11 | //returns elapsed time between `now` and `then` as sysType_t 12 | static inline sysTime_t platformTimeElapsed(sysTime_t now, sysTime_t then) 13 | { 14 | sysTime_t e; 15 | e.tv_sec = now.tv_sec-then.tv_sec; 16 | if (now.tv_nsec < then.tv_nsec){ 17 | e.tv_sec-=1; 18 | e.tv_nsec = now.tv_nsec+(1e9-then.tv_nsec); 19 | } 20 | else{ 21 | e.tv_nsec = now.tv_nsec-then.tv_nsec; 22 | } 23 | return e; 24 | } 25 | 26 | //converts `timeIn` to Milliseconds 27 | static inline double platformSysTimeToMs(sysTime_t timeIn) { 28 | return (double)timeIn.tv_sec* MS_PER_SEC + (double)timeIn.tv_nsec / 1e6; 29 | } 30 | 31 | //fills `out` with current time 32 | static inline void platformGetTime(sysTime_t* out){ 33 | clock_gettime(CLOCK_THREAD_CPUTIME_ID, out); 34 | } 35 | -------------------------------------------------------------------------------- /plot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys,os 4 | import glob 5 | import math 6 | import string 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | 10 | #the size of an evaluator which does nothing 11 | try: 12 | NULSZ = os.stat('null_eval').st_size; 13 | except: 14 | NULSZ = 0; 15 | 16 | def getsize(name): 17 | """ gets the size of an evaluator and associated tables""" 18 | sz =0 19 | name = string.lower(name) 20 | try: 21 | sz = os.stat(name).st_size - NULSZ 22 | for datfilename in glob.glob(name+"_table?.dat"): 23 | sz += os.stat(datfilename).st_size 24 | except OSError: 25 | pass #no such file 26 | 27 | return sz 28 | 29 | results = {} 30 | resultfilename = sys.argv[1] 31 | 32 | resultfile = open(resultfilename,"r") 33 | processor,memory = "Unknown","?" 34 | for line in resultfile: 35 | if "model" in line : 36 | processor = line.split(':')[1] 37 | elif "MemTotal" in line : 38 | memory = float(line.split()[1])/1e6 39 | else: 40 | try: 41 | label,enum,rand = line.split() 42 | data = results.get(label,[[],[],[]]) 43 | data[0].append(float(enum)) 44 | data[1].append(float(rand)) 45 | data[2] = getsize(label) 46 | results[label]=data 47 | except ValueError: 48 | pass #skip bad lines 49 | 50 | 51 | N = len(results) 52 | enummeans = [] 53 | enumstd = [] 54 | randmeans = [] 55 | randstd = [] 56 | sizes = [] 57 | sorted_results = sorted(results.items(),key=lambda d: d[1][2]) 58 | 59 | for label,data in sorted_results: 60 | enummeans.append(np.mean(data[0])) 61 | enumstd.append(np.std(data[0])) 62 | randmeans.append(np.mean(data[1])) 63 | randstd.append(np.std(data[1])) 64 | sizes.append(math.log(getsize(label),10)) 65 | 66 | x_locs = np.arange(N) # the x locations for the groups 67 | width = 0.35 # the width of the bars 68 | 69 | fig, ax = plt.subplots() 70 | 71 | #speed axis 72 | rects1 = ax.bar(x_locs, enummeans, width, color='r', yerr=enumstd) 73 | rects2 = ax.bar(x_locs+width, randmeans, width, color='y', yerr=randstd) 74 | ax.set_ylabel('MH/S') 75 | 76 | #size axis 77 | ax2 = ax.twinx() 78 | ax2.plot(x_locs+width, sizes, '-') 79 | ax2.set_ylim([0,10]) 80 | ax2.set_ylabel('size') 81 | ax2.set_yticks([3,6,9]) 82 | ax2.set_yticklabels(['1 Kb','1 Mb','1 Gb']) 83 | 84 | # title text, x axis 85 | plt.suptitle('7 Card Hand Evaluator Speed',fontsize=16) 86 | ax.set_title("{} with {} GB RAM".format(processor,memory),fontsize=8) 87 | ax.set_xticks(x_locs+width) 88 | ax.set_xticklabels( [d[0] for d in sorted_results]) 89 | 90 | ax.legend( (rects1[0], rects2[0]), ('enumerated', 'random') ) 91 | 92 | 93 | def autolabel(rects): 94 | # attach some text labels 95 | for rect in rects: 96 | height = rect.get_height() 97 | ax.text(rect.get_x()+rect.get_width()/2., 1.01*height, '%d'%int(height), 98 | ha='center', va='bottom') 99 | 100 | autolabel(rects1) 101 | autolabel(rects2) 102 | 103 | plt.show() 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /pokersource/PSE_eval.h: -------------------------------------------------------------------------------- 1 | // PET adaptor for the Pokersource poker-eval evaluator 2 | // http://gna.org/projects/pokersource/ 3 | // 4 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 5 | 6 | #include 7 | #include "source/include/poker_defs.h" 8 | #include "source/include/inlines/eval.h" 9 | #include "../pet_utils.h" 10 | 11 | #define Card StdDeck_CardMask 12 | typedef uint32_t EquivClass; 13 | #define Hand Card* 14 | 15 | 16 | #define PSEHAND 1 17 | #define PSE_init() (0) 18 | #define PSE_makecard(i) (StdDeck_MASK(i)) 19 | 20 | #define PSE_addcard(h,c) StdDeck_CardMask_OR(*h,*h,c) 21 | #define PSE_evaluate(h) (StdDeck_StdRules_EVAL_N(*h,7)) 22 | #define PSE_rank(v) (HandVal_HANDTYPE(v)&0xF) 23 | 24 | #define PSE_ranktran(r) (((r)+1)%10) 25 | 26 | 27 | static inline pet_card pull_card_from_hand(short cardval, Hand hand) 28 | { 29 | pet_card candidate; 30 | int suit; 31 | //try all suits until we find a match 32 | for (suit=0;suit<4;suit++) { 33 | Card result; 34 | candidate = StdDeck_MAKE_CARD(cardval,suit); 35 | StdDeck_CardMask_AND(result,*hand,StdDeck_MASK(candidate)); 36 | if (!StdDeck_CardMask_IS_EMPTY(result)){ 37 | StdDeck_CardMask_XOR(*hand,*hand,result); //remove match 38 | break; 39 | } 40 | } 41 | return candidate; 42 | } 43 | 44 | 45 | //5x3bit numbers: how many cards of each value 46 | static const uint16_t rank_reptable[]={ 47 | 0,4681,586,82,75,4681,4681,19,12,4681}; 48 | 49 | 50 | static inline int PSE_decode(Card* hand,EquivClass v, pet_card* out) 51 | { 52 | int rank=PSE_ranktran(PSE_rank(v)); 53 | int card=0; 54 | if (out){ 55 | Card h[PSEHAND]; 56 | uint16_t reps = rank_reptable[rank]; 57 | memcpy(h,hand,sizeof(h)); 58 | 59 | if (rank==rStraight||rank==rStraightFlush){ 60 | int val=HandVal_TOP_CARD(v); 61 | for (reps=5;reps-->0;){ 62 | out[card++]=pull_card_from_hand(val,h); 63 | if (--val<0) val=12; 64 | } 65 | reps=0; 66 | } 67 | 68 | for(;reps&7;reps--) { 69 | out[card++]=pull_card_from_hand(HandVal_TOP_CARD(v),h); 70 | } 71 | for(reps>>=3;reps&7;reps--) 72 | out[card++]=pull_card_from_hand(HandVal_SECOND_CARD(v),h); 73 | for(reps>>=3;reps&7;reps--) 74 | out[card++]=pull_card_from_hand(HandVal_THIRD_CARD(v),h); 75 | for(reps>>=3;reps&7;reps--) 76 | out[card++]=pull_card_from_hand(HandVal_FOURTH_CARD(v),h); 77 | for(reps>>=3;reps&7;reps--) 78 | out[card++]=pull_card_from_hand(HandVal_FIFTH_CARD(v),h); 79 | } 80 | return rank; 81 | } 82 | #define PSE_DECODES_CARDS 1 //True if decode fills `out` with cards 83 | 84 | -------------------------------------------------------------------------------- /pokersource/README.md: -------------------------------------------------------------------------------- 1 | # Poker Source Evaluator 2 | ## PSE 3 | 4 | Source: http://gna.org/projects/pokersource/ 5 | 6 | - Card Representation: 64 bits : `...AKQJT98765432` repeated 4 times, once per suit. 7 | - Hand Representation: 52 bits : 7 cards OR'd together. 8 | - Hand Value: 32 bits : `0x0R0CCCCC`. R=HandRank, C=significant card(12=A,0=2) 9 | 10 | This is a big fast open source library. It uses the same card and hand representation as the Senzee7 evaluator. It starts similarly to [Brecher's Holdem Showdown](..\showdown\README.md), and uses many of the same techniques: It OR's the suits and counts the number of unique ranks (bits set) using a table lookup, and the number of duplicates (num cards - num ranks). If there are more than 5 different ranks, checks each suit in turn: are there more then 5 cards, and are those 5 a straight. (Straights are also detected with a 13 bit table lookup). As long as there are not more than 2 duplicate cards, then there is no quad or FH and the result is set. 11 | Otherwise, it does a switch on num duplicates: 12 | 0 = no pair: the top 5 cards are extracted with a 13 bit lookup. 13 | 1 = 1 pair, the pair card is found by XORing the suits with the all-ranks. 14 | 2 = 2 pair or trips. Use the same XOR trick to find the pairs: if none it's trips, and more bitwise magic finds the right bit. 15 | else: AND the suits to detect a quad; else if trips still exist it's a fullhouse. else if flush or straight were not detected above, it must be 2 pair. 16 | For all these hands the top cards are extracted by looking up the top card(s) in a 13 bit table, then XORing with all cards to find the kickers and looking up those too. 17 | 18 | -------------------------------------------------------------------------------- /pokersource/get.sh: -------------------------------------------------------------------------------- 1 | svn co http://svn.gna.org/svn/pokersource/trunk/poker-eval source 2 | sed 's/AM_CONFIG_HEADER/AC_CONFIG_HEADERS/' tmp.ac 3 | cp tmp.ac source/configure.ac 4 | rm tmp.ac 5 | cd source 6 | libtoolize 7 | autoreconf --install 8 | ./configure 9 | make 10 | cd .. 11 | 12 | -------------------------------------------------------------------------------- /runall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | touch trials 4 | grep model trials 5 | if [ $? -ne 0 ]; then 6 | grep "model name" < /proc/cpuinfo | head -1 >> trials 7 | grep "MemTotal" < /proc/meminfo >> trials 8 | fi 9 | for args in `ls -1 ???` 10 | do 11 | echo ${args} 12 | ./${args} | tail -1 >>trials 13 | ./${args} | tail -1 >>trials 14 | ./${args} | tail -1 >>trials 15 | done 16 | 17 | python plot.py trials 18 | -------------------------------------------------------------------------------- /senzee/README.md: -------------------------------------------------------------------------------- 1 | # Paul Senzee's 7 card evaluator: 2 | ## SNZ 3 | 4 | Source: http://www.paulsenzee.com/2007/01/7.html 5 | 6 | - Card Representation: 52 bits : `AKQJT98765432` repeated 4 times, once per suit. 7 | - Hand Representation: 52 bits : 7 cards OR'd together. 8 | - Hand Value: ---same as [CactusKev](..\cactuskev\README.md) 9 | 10 | This is another evaluator that tries to do a direct loopup into a big table. It represents the hand as a 52 bit number with exactly 7 bits set. It provides a hash function `index52c7` which turns that into a unique value between 1 and ~133 million. The hash function is hard to reverse-engineer: It splits the input into 16 bit words, then does a series of table lookups, additions and multiplications to get the result. The tables used include a 2^16 entry `_bitcount`, a 2^16 entry partial result, and a set of smaller offset and multiplier tables. 11 | 12 | The final result is now used as an index into a 133 million (265GB) table which contains the Cactus Kev equivalency class. This is actually faster than the two-plus-two evaluator, (likely because it only does 1 lookup in the giant table instead of 7), but it is still slower than most of the less massive evaluators. 13 | 14 | *TODO: figure out why 133 million and not 183 million. 15 | -------------------------------------------------------------------------------- /senzee/SNZ_eval.c: -------------------------------------------------------------------------------- 1 | // PET adaptor for the Paul Senzee's 7 hand evaluator 2 | // http://www.paulsenzee.com/2006/06/some-perfect-hash.html 3 | // 4 | // table builder: (c) James Devlin 5 | // http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup 6 | // 7 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 8 | 9 | 10 | #include 11 | #define SNZ_NOTABLES 12 | #include "SNZ_eval.h" 13 | 14 | #define LOOKUP_TABLE_SIZE 133784560 15 | 16 | EquivClass* snzTable; 17 | 18 | EquivClass* load_table(const char* fname){ 19 | printf("Allocating memory for 133,784,560 entries...\n"); 20 | EquivClass* snzTable = malloc(sizeof(EquivClass)*LOOKUP_TABLE_SIZE); 21 | if (snzTable){ 22 | // Load the DAT file and map it into the HR array 23 | printf("Attempting to load %s lookup table...\n", fname); 24 | FILE * fin = fopen(fname, "rb"); 25 | if (fin) { 26 | size_t bytesread = fread(snzTable, sizeof(EquivClass), LOOKUP_TABLE_SIZE, fin); 27 | fclose(fin); 28 | printf("Successfully loaded %ld byes!\n", bytesread); 29 | } 30 | else { 31 | printf("Could not load table\n"); 32 | free(snzTable); 33 | snzTable = NULL; 34 | } 35 | } 36 | return snzTable; 37 | } 38 | -------------------------------------------------------------------------------- /senzee/SNZ_eval.h: -------------------------------------------------------------------------------- 1 | // PET adaptor for the Paul Senzee's 7 hand evaluator 2 | // http://www.paulsenzee.com/2006/06/some-perfect-hash.html 3 | // 4 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 5 | 6 | #include 7 | #include "../pet_utils.h" 8 | 9 | #ifndef SNZ_NOTABLES 10 | #include "source/index52c7.h" 11 | #endif 12 | 13 | #define Card uint64_t 14 | typedef uint16_t EquivClass; 15 | 16 | 17 | extern EquivClass* snzTable; 18 | EquivClass* load_table(const char*); 19 | 20 | #define SNZHAND 1 21 | static inline int SNZ_init(){ 22 | snzTable=load_table("snz_tables.dat"); 23 | return (snzTable==NULL?-1:0); 24 | } 25 | 26 | #define SNZ_makecard(i) (1LL<<(i)) 27 | 28 | #define SNZ_addcard(h,c) (*(h)|=(c)) 29 | #define SNZ_evaluate(h) snzTable[index52c7(*(h))] 30 | 31 | static inline pet_rank SNZ_rank(EquivClass v){ 32 | if (v > 6185) return(rHighCard); // 1277 high card 33 | if (v > 3325) return(rPair); // 2860 one pair 34 | if (v > 2467) return(rTwoPair); // 858 two pair 35 | if (v > 1609) return(rThreeOfaKind); // 858 three-kind 36 | if (v > 1599) return(rStraight); // 10 straights 37 | if (v > 322) return(rFlush); // 1277 flushes 38 | if (v > 166) return(rFullHouse); // 156 full house 39 | if (v > 10) return(rFourOfaKind); // 156 four-kind 40 | return(rStraightFlush); // 10 straight-flushes 41 | } 42 | 43 | #define SNZ_ranktran(r) (r) 44 | 45 | static inline int SNZ_decode(Card* h,EquivClass v, pet_card* out) 46 | { 47 | int rank=SNZ_ranktran(SNZ_rank(v)); 48 | return rank; 49 | } 50 | #define SNZ_DECODES_CARDS 0 //True if decode fills `out` with cards 51 | -------------------------------------------------------------------------------- /senzee/build_table.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Table Builder for Paul Senzee's 7 card poker evaluator. 6 | 7 | Authors: 8 | Paul Senzee (index52c7 function and lookup tables) 9 | http://www.senzee5.com/2007/01/7.html 10 | 11 | James Devlin (code to build 266MB lookup table from index52c7) 12 | http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup 13 | 14 | Adam Shelly (minor modifications for PET framework) 15 | http://wwww.github.com/ashelly/PET_framework 16 | 17 | */ 18 | 19 | #include "source/index52c7.h" 20 | #include "../cactuskev/KEV_eval.h" 21 | 22 | extern short eval_7hand( int *hand ); 23 | 24 | #define LOOKUP_TABLE_SIZE 133784560 25 | 26 | EquivClass* snzTable; 27 | 28 | 29 | uint16_t* create_table(const char* saveAs) 30 | { 31 | printf("Allocating memory for 133,784,560 entries...\n"); 32 | uint16_t* hand_lookup = (uint16_t*) malloc(sizeof(uint16_t)*LOOKUP_TABLE_SIZE); 33 | if (!hand_lookup) 34 | return NULL; 35 | 36 | printf("Creating %s...\n", saveAs); 37 | 38 | printf("Initializing decks...\n"); 39 | // Initialize the Pokersource deck... 40 | int64_t virginDeck[52]; 41 | int c,cactusKevDeck[52]; 42 | for (c = 0; c < 52; c++){ 43 | virginDeck[c] = (0x1LL << c); 44 | cactusKevDeck[c] = KEV_makecard((int)c); 45 | } 46 | 47 | int c0, c1, c2, c3, c4, c5, c6; 48 | int64_t h0, h1, h2, h3, h4, h5, h6; 49 | int cactuskev_hand[7]; 50 | int count = 0; 51 | 52 | printf("Building table: enumerating 7-card hands...\n"); 53 | for (c0 = 0; c0 < 46; c0++) { 54 | h0 = virginDeck[c0]; // first card 55 | cactuskev_hand[0] = cactusKevDeck[c0]; 56 | for (c1 = c0+1; c1 < 47; c1++) { 57 | h1 = h0 | virginDeck[c1]; // 2nd card 58 | cactuskev_hand[1] = cactusKevDeck[c1]; 59 | for (c2 = c1+1; c2 < 48; c2++) { 60 | h2 = h1 | virginDeck[c2]; // 3rd card 61 | cactuskev_hand[2] = cactusKevDeck[c2]; 62 | for (c3 = c2+1; c3 < 49; c3++) { 63 | h3 = h2 | virginDeck[c3]; // 4th card 64 | cactuskev_hand[3] = cactusKevDeck[c3]; 65 | for (c4 = c3+1; c4 < 50; c4++) { 66 | h4 = h3 | virginDeck[c4]; // 5th card 67 | cactuskev_hand[4] = cactusKevDeck[c4]; 68 | for (c5 = c4+1; c5 < 51; c5++) { 69 | h5 = h4 | virginDeck[c5]; // 6th card 70 | cactuskev_hand[5] = cactusKevDeck[c5]; 71 | for (c6 = c5+1; c6 < 52; c6++) { 72 | h6 = h5 | virginDeck[c6]; // 7th card 73 | cactuskev_hand[6] = cactusKevDeck[c6]; 74 | 75 | int hashedIndex = index52c7(h6); 76 | hand_lookup[hashedIndex] = eval_7hand(cactuskev_hand); 77 | 78 | if ((count++ % 1000000) == 0) 79 | printf("%d hands complete...\n", count); 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | printf("Saving the lookup table into %s...\n", saveAs); 89 | 90 | // output the array now that I have it!! 91 | FILE * fout = fopen(saveAs, "wb"); 92 | if (fout) 93 | { 94 | fwrite(hand_lookup, 2, LOOKUP_TABLE_SIZE, fout); // big write, but quick 95 | fclose(fout); 96 | } 97 | else 98 | { 99 | printf("Problem creating the Output File!\n"); 100 | } 101 | 102 | printf("Press any key to continue..."); 103 | 104 | } 105 | 106 | 107 | int main(int c, char* v){ 108 | create_table("snz_tables.dat"); 109 | } 110 | -------------------------------------------------------------------------------- /senzee/get.sh: -------------------------------------------------------------------------------- 1 | mkdir -p source 2 | cd source 3 | wget http://www.psenzee.com/code/index52c7.h 4 | patch index52c7.h ../index52c7_patch 5 | cd .. 6 | 7 | -------------------------------------------------------------------------------- /senzee/index52c7_patch: -------------------------------------------------------------------------------- 1 | --- index52c7.h.orig 2015-05-14 20:15:35.222655700 -0400 2 | +++ index52c7.h 2015-05-14 20:16:53.618324782 -0400 3 | @@ -5,8 +5,6 @@ 4 | // Senzee 5 5 | // http://senzee.blogspot.com 6 | 7 | -namespace 8 | -{ 9 | 10 | const unsigned _choose16x[] = { 1, 16, 120, 560, 1820, 4368, 8008, 11440 }; 11 | const unsigned _choose32x[] = { 1, 32, 496, 4960, 35960, 201376, 906192, 3365856 }; 12 | @@ -4144,9 +4142,8 @@ 13 | 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 14 | }; 15 | 16 | -} 17 | 18 | -inline unsigned index52c7(unsigned __int64 x) 19 | +inline unsigned index52c7(uint64_t x) 20 | { 21 | const unsigned short *a = (const unsigned short *)&x; 22 | unsigned A = a[3], B = a[2], C = a[1], D = a[0], 23 | -------------------------------------------------------------------------------- /showdown/BHS_eval.h: -------------------------------------------------------------------------------- 1 | // PET adaptor for Steve Brecher's Holdem Showdown 2 | // http://www.brecware.com/Software/software.html 3 | // 4 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 5 | 6 | #include 7 | #include "source/HandEval.h" 8 | #include "../pet_utils.h" 9 | 10 | #define Card Hand_T 11 | typedef uint32_t EquivClass; 12 | 13 | #define BHSHAND 1 14 | #define BHS_init() (!Init_Hand_Eval()) 15 | #define BHS_makecard(i) ((Card)IndexToMask(i)) 16 | 17 | #define BHS_addcard(h,c) ((*h).as64Bits|=(c.as64Bits)) 18 | #define BHS_evaluate(h) Hand_7_Eval(*h) 19 | #define BHS_rank(v) ((v)>>24) 20 | 21 | #define BHS_ranktran(r) (((r)+1)%10) 22 | 23 | static inline int BHS_decode(Card* h,EquivClass v, pet_card* out) 24 | { 25 | static const uint8_t reptable[]={0,0,1,5,2,0,0,6,3,0}; 26 | int reps,n=0,rank=BHS_ranktran(BHS_rank(v)); 27 | int card; 28 | //This is do-able but I'm not going to do it tonight. 29 | return rank; 30 | } 31 | #define BHS_DECODES_CARDS 0 //True if decode fills `out` with cards 32 | -------------------------------------------------------------------------------- /showdown/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Steve Brecher's Holdem Showdown: 3 | ## BHS 4 | 5 | source: http://www.stevebrecher.com 6 | 7 | - Card Representation: 64 bits : `...AKQJT98765432` repeated 4 times, once per suit. 8 | - Hand Representation: 64 bits : 7 cards OR'd together. 9 | - Hand Value: 32 bits : `0x0R0CCCCC`. R=HandRank, C=significant card(12=A,0=2) 10 | 11 | 12 | This evaluator uses a different bit to represent each card, two 13-bit lookup tables, and a lot of fast bit tricks to do the evaluation. The first step is to separate the hand into suits, then OR each suit together. This will result in a word with between 2 and 7 bits set. Looking up the word in the `nbrOfRanks` table returns that bit count, representing the number of unique card ranks. Knowing this lets you narrow the remainder of the evaluation. For example: only 2 unique ranks means you have "quads with trips kicker". so you can AND the suits together to find which card you have 4 of. 3 unique ranks means you have either full house or quads. You can refine further XORing the four suits together, which gives you the bits that are set 1 or 3 times - triples and singletons. And so on. 13 | Once you get over 5 unique ranks you also need to check for straights and flushes, which is done with the 'mStraitAndOrFlush' macro. It uses the `nbrOfRanks` table again to check the number of cards in each individual suit, calling it a flush if there are 5 or more. It uses a `straightValue` table on either the flush suit or on all cards to find straights. This table returns the high card for any input with 5 bits in a row, or 0 if no straight exists. 14 | 15 | This evaluator also contains 5,6, and 8 card evaluators, as well as lowball variants like Razz. 16 | -------------------------------------------------------------------------------- /showdown/get.sh: -------------------------------------------------------------------------------- 1 | mkdir -p source 2 | cd source 3 | wget http://www.stevebrecher.com/Software/HES_downloads/HoldEm_Showdown_source.zip 4 | unzip HoldEm_Showdown_source.zip 5 | cp HoldEm*/* . 6 | cd .. 7 | -------------------------------------------------------------------------------- /specialk/SPK_eval.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "source/SevenEval.h" 3 | 4 | static SevenEval const* g_spk_evaluator = new SevenEval(); 5 | 6 | extern "C" { 7 | 8 | #include "SPK_eval.h" 9 | 10 | int SPK_init() { 11 | return 0; 12 | } 13 | 14 | 15 | unsigned short SPK_evaluate(Card* hand) { 16 | return g_spk_evaluator->GetRank(hand[1],hand[2],hand[3],hand[4],hand[5],hand[6],hand[7]); 17 | } 18 | 19 | 20 | int 21 | SPK_rank( EquivClass val ) 22 | { 23 | if (val <= 1277) return(1); ///1); //HIGH_CARD); // 1277 high card 24 | if (val <= 4137) return(2); //ONE_PAIR); // 2860 one pair 25 | if (val <= 4995) return(3); //TWO_PAIR); // 858 two pair 26 | if (val <= 5853) return(4); ///THREE_OF_A_KIND); // 858 three-kind 27 | if (val <= 5863) return(5); ///STRAIGHT); // 10 straights 28 | if (val <= 7140) return(6); ///FLUSH); // 1277 flushes 29 | if (val <= 7296) return(7); ///FULL_HOUSE); // 156 full house 30 | if (val <= 7452) return(8); ///FOUR_OF_A_KIND); // 156 four-kind 31 | return(9); // 10 straight-flushes 32 | } 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /specialk/SPK_eval.h: -------------------------------------------------------------------------------- 1 | // PET adaptor for SpecialK's Poker Evaluator 2 | // https://github.com/kennethshackleton/SpecialKEval.git source 3 | // 4 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 5 | #include 6 | #include "../pet_utils.h" 7 | 8 | typedef int Card; 9 | typedef unsigned short EquivClass; 10 | 11 | 12 | #define Card int 13 | #define EquivClass short 14 | 15 | 16 | #define SPKHAND 8 17 | int SPK_init(); 18 | #define SPK_makecard(i) (((51-(i))/13)+((51-(i))%13)*4) 19 | //TODO: speed this up by modifying SevenEval constructor to use same enumeration order as `pet_card 20 | 21 | #define SPK_addcard(h,c) ((h)[++((h)[0])]=(c)) 22 | //#define SK7_evaluate(h) sk7Evaluator->getRankOfSeven(h[1],h[2],h[3],h[4],h[5],h[6]) 23 | unsigned short SPK_evaluate(Card* hand); 24 | int SPK_rank(EquivClass v); 25 | #define SPK_ranktran(r) (r) 26 | 27 | #define SPK_decode(h,v,o) (SPK_rank(v)) 28 | #define SPK_DECODES_CARDS 0 29 | -------------------------------------------------------------------------------- /specialk/get.sh: -------------------------------------------------------------------------------- 1 | git clone https://github.com/kennethshackleton/SpecialKEval.git source 2 | -------------------------------------------------------------------------------- /speed_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "platform.h" 7 | #include "pet_utils.h" 8 | 9 | // Poker Evaluator Testbed 10 | // 11 | //: Based on code collected in the XPokerEval library by James Devlin at: 12 | // http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup 13 | //: original code from Ray Wouten, Steve Brecher, Kevin Suffecool 14 | //: and others on twoplustwo.com. 15 | //: See READMEs in individual directories for full credits 16 | //: 17 | // This implementation 18 | // (c) 2015 Adam Shelly (github.com/ashelly) 19 | 20 | #define NCARDS 7 //hand size 21 | 22 | #include "PET_evaluator.h" 23 | 24 | 25 | /* **************************************** 26 | //Poker Evaluators under Test must provide: 27 | typedef Card; // The card representation 28 | 29 | int PET_init( ); 30 | // Does any setup needed for the evaluator. 31 | // File reading, table creation, etc. 32 | // Return 0 on success. 33 | 34 | Card PET_makecard(pet_card i); 35 | // Take a number from 0..51, return the appropriate Card representation. 36 | // See enum card_t in 'pet_utils.h" for the order. 37 | 38 | #define PETHAND N 39 | // where N is the number of Card items needed to represent a hand. 40 | // Each 'player' gets a hand represented by `Card h[PETHAND]` 41 | //TODO: maybe replace this with a required Hand_t? Most don't seem to need it. 42 | 43 | void PET_addcard(Card* hand,Card card); 44 | // Adds card to hand. Will be called NCARDS times. 45 | 46 | typedef EquivClass; //The 'equivalence' class of the hand. 47 | //The expectation is that the largest value wins. 48 | 49 | EquivClass PET_evaluate(Card* hand); // *timed 50 | // Evaluate the hand, return the equivalence class 51 | 52 | int PET_rank(EquivClass v); //* timed 53 | // Given an equivalence class, return a integer representing the rank. 54 | // this can be in the internal format, as long as it is between 0 and 9. 55 | 56 | pet_rank PET_ranktran(int rank); //translate internal rank to pet_rank. 57 | 58 | pet_rank PET_decode(Card* hand,EquivClass v, pet_card* cards_out); 59 | // Given the hand and the result, show the rank. 60 | // If cards_out is not null, treat as array, fill with 5 winning cards. 61 | 62 | Note that `makecard`, `ranktran` and `decode` are adapters specifically for the purpose 63 | of converting between the representation used by this testharness and 64 | the implementation's internal representation. 65 | They will not be timed and may take a different form in real-world usage. 66 | 67 | */ 68 | 69 | #define LOTS 50000000L //how many random hands to time 70 | 71 | //Expected frequency for 7 card hands 72 | static int expected_freq_7[] = { 73 | 0, 74 | 23294460, 75 | 58627800, 76 | 31433400, 77 | 6461620, 78 | 6180020, 79 | 4047644, 80 | 3473184, 81 | 224848, 82 | 41584 83 | }; 84 | 85 | 86 | //"Knuth Shuffle" or "Fisher-Yates" 87 | void Shuffle(Card* Deck) 88 | { 89 | int r,i; 90 | Card temp; 91 | for(i=51;i>0;i--) { 92 | r=(int)(((float)rand())/RAND_MAX*i); //TODO: better rand 93 | temp = Deck[i]; 94 | Deck[i]=Deck[r]; 95 | Deck[r]=temp; 96 | } 97 | } 98 | 99 | //Adapters 100 | // This routine initializes the deck. A deck of cards is 101 | // simply an array of Cards of length 52 (no jokers). This 102 | // routine should populate the array. 103 | void pet_init_deck(Card* deck) { 104 | int i; 105 | for (i=0;i<52;i++) 106 | deck[i]=PET_makecard(i); 107 | } 108 | 109 | //Given `hand` of `n` cards, return eqivalence class 110 | EquivClass pet_eval_hand(Card* hand,int n) { 111 | int i; 112 | Card h[PETHAND]={0}; 113 | for (i=0;irInvalid && r<= rStraightFlush) 295 | { 296 | printf("%15s: %8d\n", pet_rank_str[r], handTypeSum[i]); 297 | if (handTypeSum[i]!=expected_freq_7[r]) { 298 | printf("Error, should be %8d\n",expected_freq_7[r]); 299 | } 300 | } 301 | count+=expected_freq_7[r]; //TODO: need expected for N<7 302 | } 303 | 304 | speed[0]=report("Enumerated", timings,endtimings,timer,count); 305 | 306 | memset(handTypeSum,0,sizeof(handTypeSum[0])*10); 307 | 308 | 309 | printf("\nCreating %ld random hands...\n",LOTS); 310 | cardsLeft = 0; 311 | for (i=0;irInvalid && r<= rStraightFlush) { 355 | printf("\n%16s = %d", pet_rank_str[r], handTypeSum[i]); 356 | } 357 | } 358 | 359 | printf("\nTotal Hands = %d\n", count); 360 | 361 | speed[1]=report("Random",timings,endtimings,timer,count); 362 | 363 | printf("%s\t%f\t%f\n",PET_STR(EUT),speed[0],speed[1]); 364 | 365 | } 366 | 367 | 368 | -------------------------------------------------------------------------------- /twoplustwo/README.md: -------------------------------------------------------------------------------- 1 | 2 | # TwoPlusTwo: 3 | ## TPT 4 | 5 | Source: http://archives1.twoplustwo.com/showflat.php?Cat=0&Number=8513906&amp;amp;page=2&fpart=1&vc=1 6 | Author: various, see source. 7 | 8 | - Card Representation: 16 bits : 0=2C,1=2D,..4=3C,...51=AS 9 | - Hand Representation: 7 Cards + a card count *(see Note) 10 | - Hand Value: 16 bits: RRRRvvvvvvvvvvvv R=HandRank,v=Value 11 | 12 | This evaluator, developed in an epic discussion on the two-plus-two poker forum, starts with the same basic idea as [cactusKev](../cactuskev/README.md): Let's take a set of unsorted cards, and turn them directly into the equivalency class. But it does it in a ingenious, yet fairly straightforward way: Assign each card in the deck a value between 1 and 51. Lookup that card in an 52 entry table. The result is the address of another table. Use the next card as an index, and get yet another table address, and so on, until you get to the 7th card and the 7th table. The result of the last lookup is the hand value. The tables are actually all concatenated together so the 'address' is really just an offset further into the array. This is probably the bare minumum of operations you can do. The only drawback: 129MB of tables. The amount of memory swapping can cause a speed penalty for some use cases. 13 | 14 | 15 | * Note: The Hand Representation could theoretically be implemented as a single table pointer that gets updated each time AddCard is called. I haven't tried this yet. -------------------------------------------------------------------------------- /twoplustwo/TPT_eval.c: -------------------------------------------------------------------------------- 1 | // PET adaptor for the TwoPlusTwo 7 card hand evaluator 2 | // http://archives1.twoplustwo.com/showflat.php?Cat=0&Number=8513906&amp;amp;page=2&fpart=1&vc=1 3 | // 4 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 5 | 6 | #include "../pet_utils.h" 7 | #include "TPT_eval.h" 8 | #include 9 | #include 10 | #include 11 | 12 | #define TABLESIZE 32487834 13 | uint32_t HR[TABLESIZE]={0}; 14 | int TPT_init(void) 15 | { 16 | printf("Loading tpt_tables.dat file..."); 17 | FILE * fin = fopen("tpt_tables.dat", "rb"); 18 | if (!fin) 19 | return -1; 20 | size_t bytesread = fread(HR, sizeof(HR), 1, fin); // get the HandRank Array 21 | fclose(fin); 22 | printf("%ld bytes loaded.\n\n",bytesread*sizeof(HR)); 23 | return 0; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /twoplustwo/TPT_eval.h: -------------------------------------------------------------------------------- 1 | // PET adaptor for the TwoPlusTwo 7 card hand evaluator 2 | // http://archives1.twoplustwo.com/showflat.php?Cat=0&Number=8513906&amp;amp;page=2&fpart=1&vc=1 3 | // 4 | // adaptor (c) 2015 Adam Shelly (github.com/ashelly) 5 | 6 | #include 7 | 8 | typedef uint16_t Card; 9 | typedef uint16_t EquivClass; 10 | typedef Card* Hand; 11 | 12 | #define TPTHAND 8 13 | extern uint32_t HR[]; 14 | 15 | int TPT_init(void); 16 | #define TPT_makecard(i) ((Card)(((i)%13)*4)+((i)/13)+1) 17 | 18 | #define TPT_addcard(h,c) ((h)[++((h)[0])]=(c)) 19 | static inline int TPT_evaluate(Card* pCards) 20 | { 21 | ++pCards; 22 | int p = HR[53 + *pCards++]; 23 | p = HR[p + *pCards++]; 24 | p = HR[p + *pCards++]; 25 | p = HR[p + *pCards++]; 26 | p = HR[p + *pCards++]; 27 | p = HR[p + *pCards++]; 28 | return HR[p + *pCards++]; 29 | } 30 | 31 | /* //maybe Hand could just be a pointer into the table. */ 32 | /* static inline void TPT_addcard(Hand h,Card c) { */ 33 | /* if (*h==0) *h=53; */ 34 | /* *h=HR[*h+c]; */ 35 | /* } */ 36 | /* static inline EquivClass TPT_evaluate(Hand h){ */ 37 | /* return *h; */ 38 | /* } */ 39 | 40 | 41 | #define TPT_rank(r) ((r)>>12) 42 | #define TPT_ranktran(r) (r) 43 | static inline int TPT_decode(Card* h,EquivClass v, pet_card* out){ 44 | //TODO: deal with non-NULL out 45 | return TPT_ranktran(TPT_rank(v)); 46 | } 47 | #define TPT_DECODES_CARDS 0 //True if decode fills `out` with cards 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /twoplustwo/generate_table.c: -------------------------------------------------------------------------------- 1 | // HandRankSetup.cpp : Sets up the HandRank File for VERY fast Lookups 2 | // by Ray Wotton and the 2+2 list My code is GPL, use it as you like 3 | // http://archives1.twoplustwo.com/showflat.php?Cat=0&Number=8513906&amp;amp;page=2&fpart=1&vc=1 4 | 5 | /* Modified by 6 | James Devlin ( http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup) 7 | 8 | Adam Shelly (minor modifications for PET framework) 9 | */ 10 | 11 | //#include "windows.h" 12 | //#include 13 | #include "../cactuskev/source/poker.h" 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define __int64 int64_t 21 | #define LARGE_INTEGER int64_t 22 | 23 | int __min(int a, int b){ 24 | return (a<=b?a:b); 25 | } 26 | 27 | const char HandRanks[][16] = {"BAD!!","High Card","Pair","Two Pair","Three of a Kind","Straight","Flush","Full House","Four of a Kind","Straight Flush"}; 28 | 29 | __int64 IDs[612978]; 30 | int HR[32487834]; 31 | 32 | int numIDs = 1; 33 | int numcards = 0; 34 | int maxHR = 0; 35 | __int64 maxID = 0; 36 | 37 | __int64 MakeID(__int64 IDin, int newcard) // adding a new card to this ID 38 | { 39 | __int64 ID = 0; 40 | int suitcount[4 + 1]; 41 | int rankcount[13 + 1]; 42 | int workcards[8]; // intentially keeping one as a 0 end 43 | int cardnum; 44 | int getout = 0; 45 | 46 | memset(workcards, 0, sizeof(workcards)); 47 | memset(rankcount, 0, sizeof(rankcount)); 48 | memset(suitcount, 0, sizeof(suitcount)); 49 | 50 | // can't have more than 6 cards! 51 | for (cardnum = 0; cardnum < 6; cardnum++) 52 | { 53 | workcards[cardnum + 1] = (int) ((IDin >> (8 * cardnum)) & 0xff); // leave the 0 hole for new card 54 | } 55 | 56 | // my cards are 2c = 1, 2d = 2 ... As = 52 57 | newcard--; // make 0 based! 58 | 59 | workcards[0] = (((newcard >> 2) + 1) << 4) + (newcard & 3) + 1; // add next card formats card to rrrr00ss 60 | 61 | for (numcards = 0; workcards[numcards]; numcards++) 62 | { 63 | suitcount[workcards[numcards] & 0xf]++; // need to see if suit is significant 64 | rankcount[(workcards[numcards] >> 4) & 0xf]++; // and rank to be sure we don't have 4! 65 | if (numcards) 66 | { 67 | if (workcards[0] == workcards[numcards]) { // can't have the same card twice 68 | getout = 1; // if so need to get out after counting numcards 69 | } 70 | } 71 | } 72 | 73 | if (getout) { 74 | return 0; // duplicated another card (ignore this one) 75 | } 76 | 77 | 78 | int needsuited = numcards - 2; // for suit to be significant - need to have n-2 of same suit 79 | 80 | if (numcards > 4) { 81 | for (int rank = 1; rank < 14; rank++) { 82 | if (rankcount[rank] > 4) { // if I have more than 4 of a rank then I shouldn't do this one!! 83 | return 0; // can't have more than 4 of a rank so return an ID that can't be! 84 | } 85 | } 86 | } 87 | 88 | // However in the ID process I prefered that 89 | // 2s = 0x21, 3s = 0x31,.... Kc = 0xD4, Ac = 0xE4 90 | // This allows me to sort in Rank then Suit order 91 | 92 | // if we don't have at least 2 cards of the same suit for 4, we make this card suit 0. 93 | 94 | if (needsuited > 1) { 95 | for (cardnum = 0; cardnum < numcards; cardnum++) { // for each card 96 | if (suitcount[workcards[cardnum] & 0xf] < needsuited) { // check suitcount to the number I need to have suits significant 97 | workcards[cardnum] &= 0xf0; // if not enough - 0 out the suit - now this suit would be a 0 vs 1-4 98 | } 99 | } 100 | } 101 | 102 | // Sort Using XOR. Network for N=7, using Bose-Nelson Algorithm: Thanks to the thread! 103 | #define SWAP(I,J) {if (workcards[I] < workcards[J]) {workcards[I]^=workcards[J]; workcards[J]^=workcards[I]; workcards[I]^=workcards[J];}} 104 | 105 | SWAP(0, 4); 106 | SWAP(1, 5); 107 | SWAP(2, 6); 108 | SWAP(0, 2); 109 | SWAP(1, 3); 110 | SWAP(4, 6); 111 | SWAP(2, 4); 112 | SWAP(3, 5); 113 | SWAP(0, 1); 114 | SWAP(2, 3); 115 | SWAP(4, 5); 116 | SWAP(1, 4); 117 | SWAP(3, 6); 118 | SWAP(1, 2); 119 | SWAP(3, 4); 120 | SWAP(5, 6); 121 | 122 | // long winded way to put the pieces into a __int64 123 | // cards in bytes --66554433221100 124 | // the resulting ID is a 64 bit value with each card represented by 8 bits. 125 | ID = (__int64) workcards[0] + 126 | ((__int64) workcards[1] << 8) + 127 | ((__int64) workcards[2] << 16) + 128 | ((__int64) workcards[3] << 24) + 129 | ((__int64) workcards[4] << 32) + 130 | ((__int64) workcards[5] << 40) + 131 | ((__int64) workcards[6] << 48); 132 | 133 | return ID; 134 | } 135 | 136 | int SaveID(__int64 ID) 137 | { 138 | if (ID == 0) return 0; // don't use up a record for a 0! 139 | 140 | if (ID >= maxID) { // take care of the most likely first goes on the end... 141 | if (ID > maxID) { // greater than create new else it was the last one! 142 | IDs[numIDs++] = ID; // add the new ID 143 | maxID = ID; 144 | } 145 | return numIDs - 1; 146 | } 147 | 148 | // find the slot I will find it (by a pseudo bsearch algorithm) 149 | int low = 0; 150 | int high = numIDs - 1; 151 | __int64 testval; 152 | int holdtest; 153 | 154 | while (high - low > 1) { 155 | holdtest = (high + low + 1) / 2; 156 | testval = IDs[holdtest] - ID; 157 | if (testval > 0) high = holdtest; 158 | else if (testval < 0) low = holdtest; 159 | else return holdtest; // got it!! 160 | } 161 | // I guess it couldn't be found so must be added to the current location (high) 162 | // make space... // don't expect this much! 163 | memmove(&IDs[high + 1], &IDs[high], (numIDs - high) * sizeof(IDs[0])); 164 | 165 | IDs[high] = ID; // do the insert into the hole created 166 | numIDs++; 167 | return high; 168 | } 169 | 170 | int DoEval(__int64 IDin) 171 | { 172 | // I guess I have some explaining to do here... I used the Cactus Kevs Eval ref http://www.suffecool.net/poker/evaluator.html 173 | // I Love the pokersource for speed, but I needed to do some tweaking to get it my way 174 | // and Cactus Kevs stuff was easy to tweak ;-) 175 | int handrank = 0; 176 | int cardnum; 177 | int workcard; 178 | int rank; 179 | int suit; 180 | int mainsuit = 20; // just something that will never hit... need to eliminate the main suit from the iterator 181 | //int suititerator = 0; 182 | int suititerator = 1; // changed as per Ray Wotton's comment at http://archives1.twoplustwo.com/showflat.php?Cat=0&Number=8513906&page=0&fpart=18&vc=1 183 | int holdrank; 184 | int workcards[8]; // intentially keeping one as a 0 end 185 | int holdcards[8]; 186 | int numevalcards = 0; 187 | 188 | // See Cactus Kevs page for explainations for this type of stuff... 189 | const int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41 }; 190 | 191 | memset(workcards, 0, sizeof(workcards)); 192 | memset(holdcards, 0, sizeof(holdcards)); 193 | 194 | if (IDin) 195 | { // if I have a good ID then do it... 196 | for (cardnum = 0; cardnum < 7; cardnum++) 197 | { // convert all 7 cards (0s are ok) 198 | holdcards[cardnum] = (int) ((IDin >> (8 * cardnum)) & 0xff); 199 | if (holdcards[cardnum] == 0) break; // once I hit a 0 I know I am done 200 | numevalcards++; // if not 0 then count the card 201 | if (suit = holdcards[cardnum] & 0xf) { // find out what suit (if any) was significant 202 | mainsuit = suit; // and remember it 203 | } 204 | } 205 | 206 | 207 | for (cardnum = 0; cardnum < numevalcards; cardnum++) 208 | { 209 | // just have numcards... 210 | workcard = holdcards[cardnum]; 211 | 212 | // convert to cactus kevs way!! ref http://www.suffecool.net/poker/evaluator.html 213 | // +--------+--------+--------+--------+ 214 | // |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp| 215 | // +--------+--------+--------+--------+ 216 | // p = prime number of rank (deuce=2,trey=3,four=5,five=7,...,ace=41) 217 | // r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12) 218 | // cdhs = suit of card 219 | // b = bit turned on depending on rank of card 220 | 221 | rank = (workcard >> 4) - 1; // my rank is top 4 bits 1-13 so convert 222 | suit = workcard & 0xf; // my suit is bottom 4 bits 1-4, order is different, but who cares? 223 | if (suit == 0) { // if suit wasn't significant though... 224 | suit = suititerator++; // Cactus Kev needs a suit! 225 | if (suititerator == 5) // loop through available suits 226 | suititerator = 1; 227 | if (suit == mainsuit) { // if it was the sigificant suit... Don't want extras!! 228 | suit = suititerator++; // skip it 229 | if (suititerator == 5) // roll 1-4 230 | suititerator = 1; 231 | } 232 | } 233 | // now make Cactus Kev's Card 234 | workcards[cardnum] = primes[rank] | (rank << 8) | (1 << (suit + 11)) | (1 << (16 + rank)); 235 | } 236 | 237 | // James Devlin: replaced all calls to Cactus Kev's eval_5cards with calls to 238 | // Senzee's improved eval_5hand_fast 239 | 240 | switch (numevalcards) { // run Cactus Keys routines 241 | 242 | case 5 : holdrank = eval_5hand_fast(workcards[0],workcards[1],workcards[2],workcards[3],workcards[4]); 243 | break; 244 | // if 6 cards I would like to find HandRank for them 245 | // Cactus Key is 1 = highest - 7362 lowest I need to get the min for the permutations 246 | case 6 : holdrank = eval_5hand_fast(workcards[0],workcards[1],workcards[2],workcards[3],workcards[4]); 247 | holdrank = __min(holdrank, eval_5hand_fast(workcards[0],workcards[1],workcards[2],workcards[3],workcards[5])); 248 | holdrank = __min(holdrank, eval_5hand_fast(workcards[0],workcards[1],workcards[2],workcards[4],workcards[5])); 249 | holdrank = __min(holdrank, eval_5hand_fast(workcards[0],workcards[1],workcards[3],workcards[4],workcards[5])); 250 | holdrank = __min(holdrank, eval_5hand_fast(workcards[0],workcards[2],workcards[3],workcards[4],workcards[5])); 251 | holdrank = __min(holdrank, eval_5hand_fast(workcards[1],workcards[2],workcards[3],workcards[4],workcards[5])); 252 | break; 253 | case 7 : holdrank = eval_7hand(workcards); 254 | break; 255 | default : // problem!! shouldn't hit this... 256 | printf(" Problem with numcards = %d!!\n", numcards); 257 | break; 258 | } 259 | 260 | // I would like to change the format of Catus Kev's ret value to: 261 | // hhhhrrrrrrrrrrrr hhhh = 1 high card -> 9 straight flush 262 | // r..r = rank within the above 1 to max of 2861 263 | handrank = 7463 - holdrank; // now the worst hand = 1 264 | 265 | if (handrank < 1278) handrank = handrank - 0 + 4096 * 1; // 1277 high card 266 | else if (handrank < 4138) handrank = handrank - 1277 + 4096 * 2; // 2860 one pair 267 | else if (handrank < 4996) handrank = handrank - 4137 + 4096 * 3; // 858 two pair 268 | else if (handrank < 5854) handrank = handrank - 4995 + 4096 * 4; // 858 three-kind 269 | else if (handrank < 5864) handrank = handrank - 5853 + 4096 * 5; // 10 straights 270 | else if (handrank < 7141) handrank = handrank - 5863 + 4096 * 6; // 1277 flushes 271 | else if (handrank < 7297) handrank = handrank - 7140 + 4096 * 7; // 156 full house 272 | else if (handrank < 7453) handrank = handrank - 7296 + 4096 * 8; // 156 four-kind 273 | else handrank = handrank - 7452 + 4096 * 9; // 10 straight-flushes 274 | 275 | } 276 | return handrank; // now a handrank that I like 277 | } 278 | 279 | 280 | int main(int argc, char* argv[]) 281 | { 282 | int IDslot, card = 0, count = 0; 283 | __int64 ID; 284 | 285 | clock_t timer = clock(); // remember when I started 286 | 287 | // Store the count of each type of hand (One Pair, Flush, etc) 288 | int handTypeSum[10]; 289 | 290 | // Clear our arrays 291 | memset(handTypeSum, 0, sizeof(handTypeSum)); 292 | memset(IDs, 0, sizeof(IDs)); 293 | memset(HR, 0, sizeof(HR)); 294 | 295 | 296 | // step through the ID array - always shifting the current ID and adding 52 cards to the end of the array. 297 | // when I am at 7 cards put the Hand Rank in!! 298 | // stepping through the ID array is perfect!! 299 | 300 | int IDnum; 301 | int holdid; 302 | 303 | printf("\nGetting Card IDs!\n"); 304 | 305 | // Jmd: Okay, this loop is going to fill up the IDs[] array which has 612,967 slots. 306 | // as this loops through and find new combinations it adds them to the end 307 | // I need this list to be stable when I set the handranks (next set) (I do the insertion sort on new IDs these) 308 | // so I had to get the IDs first and then set the handranks 309 | for (IDnum = 0; IDs[IDnum] || IDnum == 0; IDnum++) 310 | { 311 | // start at 1 so I have a zero catching entry (just in case) 312 | for (card = 1; card < 53; card++) 313 | { 314 | // the ids above contain cards upto the current card. Now add a new card 315 | ID = MakeID(IDs[IDnum], card); // get the new ID for it 316 | if (numcards < 7) holdid = SaveID(ID); // and save it in the list if I am not on the 7th card 317 | } 318 | printf("\rID - %d", IDnum); // just to show the progress -- this will count up to 612976 319 | } 320 | 321 | printf("\nSetting HandRanks!\n"); 322 | 323 | // this is as above, but will not be adding anything to the ID list, so it is stable 324 | for (IDnum = 0; IDs[IDnum] || IDnum == 0; IDnum++) 325 | { 326 | // start at 1 so I have a zero catching entry (just in case) 327 | for (card = 1; card < 53; card++) 328 | { 329 | ID = MakeID(IDs[IDnum], card); 330 | 331 | if (numcards < 7) 332 | { 333 | IDslot = SaveID(ID) * 53 + 53; // when in the index mode (< 7 cards) get the id to save 334 | } 335 | else 336 | { 337 | IDslot = DoEval(ID); // if I am at the 7th card, get the equivalence class ("hand rank") to save 338 | } 339 | 340 | maxHR = IDnum * 53 + card + 53; // find where to put it 341 | HR[maxHR] = IDslot; // and save the pointer to the next card or the handrank 342 | } 343 | 344 | if (numcards == 6 || numcards == 7) 345 | { 346 | // an extra, If you want to know what the handrank when there is 5 or 6 cards 347 | // you can just do HR[u3] or HR[u4] from below code for Handrank of the 5 or 6 card hand 348 | HR[IDnum * 53 + 53] = DoEval(IDs[IDnum]); // this puts the above handrank into the array 349 | } 350 | 351 | printf("\rID - %d", IDnum); // just to show the progress -- this will count up to 612976 same as above! 352 | } 353 | 354 | printf("\nNumber IDs = %d\nmaxHR = %d\n", numIDs, maxHR); // for warm fuzzys 355 | 356 | timer = clock() - timer; // end the timer 357 | 358 | printf("Training seconds = %.2f\n", (float)timer/CLOCKS_PER_SEC); // display training time 359 | 360 | LARGE_INTEGER timings, endtimings; // for high precision timing 361 | 362 | timer = clock(); // now get current time for Testing! 363 | 364 | // another algorithm right off the thread 365 | 366 | int c0, c1, c2, c3, c4, c5, c6; 367 | int u0, u1, u2, u3, u4, u5; 368 | 369 | // QueryPerformanceCounter(&timings); // start High Precision clock 370 | 371 | for (c0 = 1; c0 < 53; c0++) { 372 | u0 = HR[53+c0]; 373 | for (c1 = c0+1; c1 < 53; c1++) { 374 | u1 = HR[u0+c1]; 375 | for (c2 = c1+1; c2 < 53; c2++) { 376 | u2 = HR[u1+c2]; 377 | for (c3 = c2+1; c3 < 53; c3++) { 378 | u3 = HR[u2+c3]; 379 | for (c4 = c3+1; c4 < 53; c4++) { 380 | u4 = HR[u3+c4]; 381 | for (c5 = c4+1; c5 < 53; c5++) { 382 | u5 = HR[u4+c5]; 383 | for (c6 = c5+1; c6 < 53; c6++) { 384 | handTypeSum[HR[u5+c6] >> 12]++; 385 | count++; 386 | } 387 | } 388 | } 389 | } 390 | } 391 | } 392 | } 393 | 394 | // QueryPerformanceCounter(&endtimings); // end the high precision clock 395 | 396 | timer = clock() - timer; // get the time in this 397 | 398 | for (int i = 0; i <= 9; i++) // display the results 399 | printf("\n%16s = %d", HandRanks[i], handTypeSum[i]); 400 | 401 | printf("\nTotal Hands = %d\n", count); 402 | 403 | // __int64 clocksused = (__int64)endtimings.QuadPart - (__int64) timings.QuadPart; // calc clocks used from the High Precision clock 404 | 405 | // and display the clock results 406 | // printf("\nValidation seconds = %.4lf\nTotal HighPrecision Clocks = %I64d\nHighPrecision clocks per lookup = %lf\n", (double)timer/CLOCKS_PER_SEC, clocksused, (double) clocksused / 133784560.0) ; 407 | 408 | // output the array now that I have it!! 409 | FILE * fout = fopen("tpt_tables.dat", "wb"); 410 | if (!fout) { 411 | printf("Problem creating the Output File!\n"); 412 | return 1; 413 | } 414 | fwrite(HR, sizeof(HR), 1, fout); // big write, but quick 415 | 416 | fclose(fout); 417 | printf("Created tpt_tables.dat\n"); 418 | 419 | return 0; 420 | } 421 | 422 | ///////////////////////////////// end code!! 423 | 424 | 425 | -------------------------------------------------------------------------------- /wiki/framework.md: -------------------------------------------------------------------------------- 1 | # The Test Framework 2 | 3 | ## Framework Types 4 | 5 | The framework defines two types: 6 | 7 | - `pet_card`: an enum representing a card. The order is `0 = 2C, 1 = 3C, ..., 13 = 2D, ... , 51 = AS`. 8 | - `pet_rank`: an enum representing the hand rank: `1 = high card, 2 = pair, ..., 9 = StraightFlush`. 9 | 10 | ## Evaluator Methods and Types 11 | 12 | The Evaluators under test must implement the following methods, which the framework will call in the listed order. It must also define the **`Bolded`** types. 13 | 14 | 1. Do any one-time setup (loading files, creating tables, etc) with: 15 | 16 | void PET_init(void); 17 | 18 | 2. Create 52 Cards to fill the deck with: 19 | 20 | Card PET_makecard(pet_card); 21 | 22 | **`Card`** is defined by the evaluator as it's most efficient card representation. 23 | 24 | 3. Add Cards to the Hand with: 25 | 26 | void XXX_addcard(Hand, Card); 27 | 28 | **`Hand`** is defined as an array of `Card`s of size `PETHAND`. The evaluator defines PETHAND. The test harness allocates the memory. The evaluator is free to cast the pointer into a more efficient representation. 29 | 30 | 4. Run the evaluator to find the [equivalence value](equivalence.md): 31 | 32 | EquivClass XXX_evaluate(Hand); 33 | 34 | The **`EquivClass`** can use any numeric encoding scheme, as long as better hands always get higher numbers. 35 | 36 | 5. Extract the hand rank with: 37 | 38 | pet_rank XXX_rank(EquivClass); 39 | 40 | 41 | 6. Or fetch the winning cards into the 5 card `out` array with: 42 | 43 | pet_rank XXX_decode(Hand,EquivClass,pet_card* out); 44 | 45 | 46 | 7. Cleanup any initialized resources with: 47 | 48 | void XXX_cleanup(); //frees any resurces from init. 49 | 50 | 51 | 52 | The framework 53 | -HandRank has 10 possible values: StraightFlush through HighCard, plus "Invalid" (Evaluator can, but is not required to detect hands that are impossible with 7 cards from a 51 card deck.) 54 | 55 | 56 | 57 | #### *A note about "equivalency classes".* 58 | 59 | Although there are X possible 5-card hands, there are only Y possible results when determining a winning hand. For example, there are 6 possible ways to have a pair of aces `(AcAd AcAh AcAs AdAh AdAs AhAs)`, but they all count the same when determining the winner. All four ways to get a royal flush are count the same and have the same equivalency class, as do all N ways of getting a 7 card High. You can list these classes in order from low to high: 60 | 61 | 7 High, with 5,4,3,2 62 | 7 High, with 6,4,3,2 63 | ... 64 | Pair Aces with a K Q J, 65 | Two Pair 2s and 3s with a 5, 66 | ... 67 | Full House Jacks over Aces, 68 | Full House Queens over 2s, 69 | ... 70 | King high Straight Flush, 71 | Royal Flush. 72 | 73 | Assigning each of these a unique number in an increasing sequence gives an equivalency class for that hand. 74 | 75 | ### Terminology and Abbreviations 76 | 77 | - Rank (card): The number or face on the card : `2..9TJQKA` 78 | - Suit : The card suit: `CDHS` 79 | - Hand : 7 cards 80 | - Hand Value : A number representing the equivalency class. Can be any 1-to-1 mapping to the 6xxx classes, as long as the higher value is always a better hand. 81 | - Hand Rank : The 'type' of hand: One Pair, Full House, etc. 82 | 83 | 84 | --------------------------------------------------------------------------------