Terms & Conditions

Internet Magazine

Non–US new players
Get five 2+2 books


Order Books
Book Translations
Forum Login
 
 
Expand All   Collapse All

 Two Plus Two 
2+2 Magazine Forum
Special Sklansky Forum
2+2 Pokercast
About the Forums

 General Poker Discussion 
Beginners Questions
Books and Publications
Televised Poker
News, Views, and Gossip
Brick and Mortar
Home Poker
Beats, Brags, and Variance
Poker Theory
Poker Legislation

 Coaching/Training 
StoxPoker
DeucesCracked

 German Forums 
Poker Allgemein
Strategie: Holdem NL cash
Strategie: Sonstige
Internet/Online
BBV
Small Talk
German Poker News

 French Forums 
Forum francophone
Strategie
BBV (French)

 Limit Texas Hold'em 
High Stakes Limit
Medium Stakes Limit
Small Stakes Limit
Micro Stakes Limit
Mid-High Short-handed
Small Stakes Shorthanded
Limit––>NL

 PL/NL Texas Hold'em 
High Stakes
Medium Stakes
Small Stakes
Micro Stakes
Small-High Full Ring
Micro Full Ring

 Tournament Poker 
Small Stakes MTT
High Stakes MTT
MTT Community
STT Strategy
Tournament Circuit

 Other Poker 
Omaha/8
Omaha High
Stud
Heads Up Poker
Other Poker Games

 General Gambling 
Probability
Psychology
Sports Betting
Other Gambling Games
Entertainment Betting

 Internet Gambling 
Internet Gambling
Internet Bonuses
Affiliates/RakeBack
Software

 2+2 Communities 
Other Other Topics
The Lounge: Discussion+Review
El Diablo's General Discussion
BBV4Life

 Other Topics 
Golf
Sporting Events
Politics
Business, Finance, and Investing
Travel
Science, Math, and Philosophy
Health and Fitness
Student Life
Puzzles and Other Games
Video Games
Laughs or Links!
Computer Technical Help
Sponsored Support Forums
RakebackNetwork
RakeReduction.com
Other Links
Books
Authors
Abbreviations
Calendar
Order Books
Books by Others
Favorite Links
Feedback
Advertising Information
Home
Posting Hints
Privacy Notice
Forum Archives

The 2+2 Forums

Before using this Forum, please refer to the Terms and Conditions (Last modified: 2/26/2006)

Be sure to read the   Two Plus Two Internet Magazine

This is an archive. The main forums are here

These forums are read only.


 
UBB.threads™ Groupee, Inc.

Internet Gambling >> Software

Pages: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | >> (show all)
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
7 Card Hand Evaluators
      #8513906 - 12/21/06 02:27 PM

Hi all, new user but was told this was a good poker forum!

Not sure if this is the right place to post, please move if it is not.

Anyway, I've finished writing a 7 card hand evaluator for poker, and I'm managing to get around 14-15 million hands evaluated per second (14,000 to 15,000 per millisecond). I've had a look around, and is anyone aware of any software that can beat this?

Tests where done on a P4 2.4ghz PC with 1GB Ram.

Thanks for your time!

Tom


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8515375 - 12/21/06 04:43 PM

When u say it evaluates hands, what exactly does it do? Solve for the equity value vs a random distribution, a specified distribution, tell you your hands rank, or does some sort of opponent/bidding modelling?

As far as whether I'm aware of faster software? Again it depends what it does. If you just have it look up equity values poker stove caps off at about 100,000 million games a second - but it "cheats" - because it looks them up in tables. If you specify part of the flop, then it slows to about 5-10 million games a second. And if you specify distribution vs. distribution analysis with multiple players it slows a to a crawl, it slows to a few thousand a second.

Because of that I've wrote a poker evaluator as well that is 10,000 faster than poker stove for distribution vs. distribution analysis and partial flop analysis, which i'm using as an engine to try write a bot that approximates Nash EQ play. It lets you specify distribtuions more precisely than pokerstove currently does - which honestly isn't much use to the average poker player. E.g. you can assign any decimal between [0,1] to the probability that when your opponent is dealt XY, he plays XY (or is on XY at that point in time). This allows you to capture the full range of possible distributions. The purpose of my program is to be interacted with by another program, not realy for some human to plug in by hand a bunch of values between [0,1] for each of the 1326 XY combinations.

Anyways, need to know what exactly your program does, if u want to know if something else does it faster. "Evaluation" is an ambiguous term


Post Extras: Print Post   Remind Me!   Notify Moderator  
justkevin
enthusiast


Reged: 10/05/06
Posts: 203
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8516032 - 12/21/06 05:40 PM

I think he means ranks hands-- takes a combination of 7 cards and returns an integer. A better hand has a higher integer value than a worse hand.

A faster evaluator means a faster bot/analysis program etc.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: justkevin]
      #8518276 - 12/21/06 09:49 PM

oh ok. In terms of merely assigning a integer value to a hand ranking i'm getting slightly over 60 million per second. I have a AMD Dual FX-60, 2 Gigs of ram. For purposes of conductnig the evaluation I assigned a static flop and hands to 10 players and fed that repeatedly to my "determine winner" function which ranks all 10 hands.

The determine winner function contains no memory of the previous hand fed to it, so its like receiving a new hand, but it isolates the processing time, from the time it takes to generate new hands, etc....

I could write a function that is about 1000x faster but the memory requirements would increase 10 fold. Right now it uses about 200 Mbs of memory.

Well, I would still say yours might be faster than any available commercial software, (I think mine are). If anyone has serious intentions on making commercial or proprietary software using these types of algorithms they can contact me. Right now i'm using mine to design bots.


Post Extras: Print Post   Remind Me!   Notify Moderator  
DonkBluffer
veteran


Reged: 08/26/05
Posts: 1597
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8522519 - 12/22/06 08:02 AM

I'd be very interested in a program where you could put a player on a preflop range, choose a flop, and let the program calculate how often the player would have flopped every possible combination. (Top pair, a gutshot, top pair AND a gutshot should be different combinations, so the total adds up to 100%, if you know what i mean)

I don't know if such a thing exists, but you guys seem to know a lot about programming.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: DonkBluffer]
      #8534618 - 12/23/06 01:34 PM

D&L:

I find 60 million per second extremely impressive. Because you are on dual core, we can cut that in half and say 30 million per second, which is still a darn sight faster than mine.

Just to clarrify:

The problem is, to take in 7 UNORDERED integers (0-52) representing a hand, and convert them into a ranking which can be compared with other hands ranking to quickly determine the winner. I can't cap 13.5million per second! Is this what your evaluator does?


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8534912 - 12/23/06 02:15 PM

Update: Managed to get it to nearly 15 million per second.

Post Extras: Print Post   Remind Me!   Notify Moderator  
bachfan
member


Reged: 11/22/05
Posts: 196
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8535175 - 12/23/06 02:48 PM

Quote:

Update: Managed to get it to nearly 15 million per second.




This is similar to my numbers on propokertools , although ranking a 7-card hand is not what my code spends most of its time doing these days.

I might try and whip something up faster for fun in the next few hours.

Cheers,
bachfan


Post Extras: Print Post   Remind Me!   Notify Moderator  
bachfan
member


Reged: 11/22/05
Posts: 196
Re: 7 Card Hand Evaluators [Re: bachfan]
      #8535196 - 12/23/06 02:52 PM

Quote:

Quote:

Update: Managed to get it to nearly 15 million per second.




This is similar to my numbers on propokertools , although ranking a 7-card hand is not what my code spends most of its time doing these days.

I might try and whip something up faster for fun in the next few hours.

Cheers,
bachfan




Oops, off by a factor of two. I'm closer to 30million on one core of a core duo 1.6ghz. This is JUST the compute ranking function.

- bachfan


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: bachfan]
      #8537353 - 12/23/06 07:08 PM

So, to clear things up, your program can work out the rank of a hand given to it in any order, IE given random unordered set of 7 integers it can work out 30 million of them per second? Or as a pre condition must they be ordered?

Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8538571 - 12/23/06 09:42 PM

actually dual core isn't coming into play yet...it simiply allows me to be surfing the web without degrading the speed of other programs. Its still 60 million...but i am on a faster machine overall i think.

Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8538748 - 12/23/06 10:00 PM

Mine is 60million unordered. Keep in mind my function takes about 200MB of memory. I was trying to make the fastest algorithm my machine could support. The only faster algorithm I could think of would have taken multiple gigabytes of memory. Memory and speed are almost always tradeoffs.

Remember my dual core processor doesn't effect speed. Although I am on a faster comp i think.

Anyways, I'm not writing to say that mines faster. I'm really impressed that others are coming up with similar results. I've worked for a while on various altorithms, as part of trying to apply game theory to poker. If any of u are working on commercial software, tell me about it, and if its interesting and u sound serious, I may be willing to share my techniques.

P.s. I'm not a programmer. _D&L_ = Dirty and Litigious - i'm a lawyer, with a background in gametheory and economics. It's quite possible that if a professional programmer looked at my code they could find further optimizations. I programmed in VisualBasic 5.0, which might be slower than if it were converted to something like C++.

-_D&L_


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8539655 - 12/24/06 12:02 AM

So you are getting 60 million evaluations per second (that's 40 cpu cycles per hand) using VB?

Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8540024 - 12/24/06 12:56 AM

Yes Gull, thats what I said...

Look if memory was no object you could just do this:

Dim HandValue(52, 52, 52, 52, 52, 52, 52) as Integer

And write hand values for every combination of cards, for any order they are entered. It would take, i think, 2000 gigabytes of memory. I don't think there are computers that support that much memory. But the result would be that you would have hand evaluations that could be achieved in maybe 1 cpu cycle - that is the array would just be a pointer to a result.

Obviously, not feasible given memory constraints. I can't reveal my methods, though i sense disbelief, but hopefully this extreme example shows what is possible. Indeed, this extreme example shows that if memory were not an object we could have a function that solved billions or maybe trillions of games a second.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: DonkBluffer]
      #8540272 - 12/24/06 01:32 AM

Yeah, i don't think anything exists for that right now. It would take me about 3-7 days to transform my program into something that could do that...as well as adding a decent user interface to edit distributions (right now I use access).

If someone with (proven) experience in publishing share-ware like programs wanted to co-publish a program like this with me, I could probably be talked into doing it. As I said elsewhere in this thread, I'm not a programmer by trade. I have difficulty getting my program to run on other systems, don't know how to create an installation package, and i would want to protect my databases (part of my algorithm) from being hacked and used for other un-liscensed purposes, etc.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Phil153
Notable Twit


Reged: 10/18/05
Posts: 4905
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8540556 - 12/24/06 02:15 AM

Quote:

Because of that I've wrote a poker evaluator as well that is 10,000 faster than poker stove for distribution vs. distribution analysis and partial flop analysis



I call BS. It may be faster than pokerstove in very specific situations, but for the majority of analyses I doubt there's much difference.

And anything that requires a 200MB database on disk is not commercially viable except as a web application, a market which propokertools has pretty much owned. Sorry.

Anyone can generate the lookup tables required for a blazing fast algorithm, so I don't know how you claim that PokerStove "cheats" by using them, when that's exactly what you use. You can only do about a billion bitwise (and, or, xor), comparison, or iteration operations a second, and without lookup tables you'll never get a reasonable speed using any algorithm.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Phil153]
      #8540954 - 12/24/06 03:16 AM

Phil why are you hostile? Did I ever say the world needed a poker stove that was 10,000 times faster? In fact, you gloss over the fact I said the exact opposite, because you want to pick a fight.

I didn't design my program to be the next poker stove, i designed it for speed - to be used in high end bots that i work on, or game theory software, which is pretty much the same thing.

BTW i'm not a programmer, and haven't worked on these algorithms in over a year, so i mistook how much memory they used. I said 200mb, that was an earlier version, I double checked, they use about 15MBs. My original version was 8 gigs of hd space...so i've made lots improvements over time.

Anyways, do i fault poker stove for using "lookup tables" - of course not. I use them too. My point - which u missed, because u want a fight - was that if a result is not on the lookup table, the program slows considerably. From 100,000 million games a second, to 5,000. The difference being is that for my algorithm no result is "off table."

-Dirty & Litigious


Post Extras: Print Post   Remind Me!   Notify Moderator  
Andrew Prock
enthusiast


Reged: 09/02/02
Posts: 346
Loc: oakland
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8542250 - 12/24/06 10:03 AM

5,000 per second? On what platform? The slowest it ever goes for me about 500,000 evals per second.

- Andrew


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Andrew Prock]
      #8542389 - 12/24/06 10:37 AM

I've written an algorithm that uses NO lookup tables and can do 1.5 to 2 million evaluations per second.

My look up table version runs at 14-15 million per second. I'm sorry but without seeing your code I find it extremely difficult to believe that you can do 60 million per second with VB with a 15MB look up table. Would you be willing to expand on your methods?


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8543252 - 12/24/06 01:55 PM

Quote:

oh ok. In terms of merely assigning a integer value to a hand ranking i'm getting slightly over 60 million per second. I have a AMD Dual FX-60, 2 Gigs of ram. For purposes of conductnig the evaluation I assigned a static flop and hands to 10 players and fed that repeatedly to my "determine winner" function which ranks all 10 hands.

The determine winner function contains no memory of the previous hand fed to it, so its like receiving a new hand, but it isolates the processing time, from the time it takes to generate new hands, etc....





Quote:

So, to clear things up, your program can work out the rank of a hand given to it in any order, IE given random unordered set of 7 integers it can work out 30 million of them per second? Or as a pre condition must they be ordered?




Quote:

Mine is 60million unordered. Keep in mind my function takes about 200MB of memory. I was trying to make the fastest algorithm my machine could support. The only faster algorithm I could think of would have taken multiple gigabytes of memory. Memory and speed are almost always tradeoffs.




Quote:

Oops, off by a factor of two. I'm closer to 30million on one core of a core duo 1.6ghz. This is JUST the compute ranking function.




There's a HUGE difference between assigning static cards for the flop and the players, and the general purpose solution. Are you both actually comparing your algorithms using the same metric?

IE: Your function is passed an unordered vector of 7 integers in the range {0..51} and returns the hand rank for the input combination.

From what I just read it sounds like one of you is generating your timings with essentially 5 of the 7 cards statically pre-assigned, and the other is generating the timings with no static cards and possibly some are using a 52 element bitstring rather than 7 unordered integers (bachfan?).


Quote:

I could write a function that is about 1000x faster but the memory requirements would increase 10 fold. Right now it uses about 200 Mbs of memory.




Quote:

So you are getting 60 million evaluations per second (that's 40 cpu cycles per hand) using VB?




Quote:

Yes Gull, thats what I said...



A 1000 fold increase of the existing 40 cycles per hand would be 0.04 cycles per hand???

Juk

PS: I'm supposed to be having a rest from poker/coding atm (until new year at least), but this thread got me interested now...


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8543333 - 12/24/06 02:07 PM

Quote:

Phil why are you hostile? Did I ever say the world needed a poker stove that was 10,000 times faster? In fact, you gloss over the fact I said the exact opposite, because you want to pick a fight.



Phil153 + Poker AI = Flames

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
Andrew Prock
enthusiast


Reged: 09/02/02
Posts: 346
Loc: oakland
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8543348 - 12/24/06 02:11 PM

If you really did get 60 million general evals per second on a normal machine (3 MHz or so), I'm truly impressed. But you have to keep in mind that when it come to doing poker calculations there is more to it than just plain evaluation speed. The guy with the Poker Bolide software demonstrated that recently by making a fast monte carlo evaluator, but not much else.

It's unclear what you're striving for here, or what you've achieved. There is no world record for evals/second, but even at 60 million per second, interesting poker calculations can be done much faster. PokerStove can do about 2 billion evaluations per second in the optimized Enumeration code. In the unoptimized code, it does about 10 million evals per second when enumerating. That may seem slow, but many of the calculations finish with the exact solution nearly instantly. The same can't be said for monte carlo evaluation. While PokerStove does have various lookup tables, those consist mostly of things like bit counting tables. Admittedly, the monte carlo code is not it's strong point, but there's a reason for that.

Under monte carlo, once the code is fast enough, it doesn't really matter too much if you make it twice as fast. You'll get close to the exact value quickly, but you'll never get the exact value. Said another way, doubling the number of evals won't double the convergence of the result. There are also issues of what sort of RNG you use, and equally important how you use it. Getting the RNG problem wrong can mean that your monte carlo simulations converge to the wrong result.

If you can calculate the exact value, you're usually much better off, assuming you can actually calculate it quickly enough. The nice thing is that many of the interesting questions the people ask about poker are computable using exact enumeration, and can be done in a reasonable amount of time. In a lot of ways that was the whole point of PokerStove. If you know what you are trying to calculate, you can do it significantly faster by taking the whole calculation into consideration instead of just relying on fast 7 card hand evaluators. Unfortunatly, not all things can be calculated exactly in a short period of time, and for those you often have to resort to monte carlo, precomputation, or other methods.

If you look at the work of eastbay and bachfan, you can see that they've done a great job in actually answering questions that people want to ask. As for your software, until you make it public in some fashion, it'll mean the world to you, but not much to the world. That's certainly not a bad thing. I have a lot of software which isn't public either. And it does mean quite a bit to me.

On the other hand, all this thread has piqued my curiosity. I'v never really tried to make a fast general purpose evaluator. I wonder how fast I could make it?

- Andrew


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #8543938 - 12/24/06 04:13 PM

Well right now I use two lookup tables; and some on-the-fly processing. With 2 gigs of memory, you could just have one big lookup table, and reduce processing time to just retreiving a value from an array. Is that 1000x faster, maybe not, but it would be exponentially faster. Can modern CPU's retreive more than one integer value per CPU cycle? How about from a multi-dimensional Array? Well at any rate, that is the limiting factor, if memory wasn't an issue.

Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Andrew Prock]
      #8544157 - 12/24/06 04:38 PM

Hey Andrew,

First, for anybody else reading Andrew is the author of poker stove. Second, Andrew you may remember me. I wrote you about a year or so ago, inquiring about access to poker stove source code, offering to make some "improvements," sign a confidentiality agreement, etc. You declined, as I probably would have too in your situation, but you can't really fault me for trying.

At any rate, i wrote this code to be used with bots and junk that I was working on. I didn't have your code as a starting point, so maybe i needed to re-invent the wheel - to some extent. Certainly poker stove is great at what it's designed to do, and my intent was never to write a program to displace yours.

One thing i noticed is that with poker stove is that if you set all the players to distributions, the program slows considerably. Its not a big deal for Poker_stove, because rarely are there 9 other players on the flop with you, but Pstove does slow from about 100,000 million games a sec to about 1 game every 4 seconds (9 players on broadway, final player on random all). The slow down isn't as severe with 3 or 4 players on the flop, but it still slows by a magnitude of about 200.

Again, not a big deal for pokerstove, because few Pokerstove users need a question like this answered. But when you set a bot to search for the right strategy, u don't want it hit bottlenecks like that. So thats why my program tries to process everything at a more uniform speed. Besides, even if poker stove does do a better job than my program in answering these questions, i still didn't have access to the source to use in other programs. So at any rate, thats why I wrote my own...

Dirty & Litigious

-Dirty & Litigious


Post Extras: Print Post   Remind Me!   Notify Moderator  
bachfan
member


Reged: 11/22/05
Posts: 196
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #8544466 - 12/24/06 05:16 PM

Quote:


From what I just read it sounds like one of you is generating your timings with essentially 5 of the 7 cards statically pre-assigned, and the other is generating the timings with no static cards and possibly some are using a 52 element bitstring rather than 7 unordered integers (bachfan?).





Here's what I did (and it looks like I was right with my FIRST quote of approx 15mil/sec, not 30. Oh well.

I just spewed out 15 million random 7-card poker hands and computed the ranking for each. I looked up the cpu utilization for the ranking function and extrapolated the rate from that (i thought 30 mil for a minute but i forget to include a part which in all fairness really is part of the evaluation function - long story...)

I've got some more comments coming.

- bachfan


Post Extras: Print Post   Remind Me!   Notify Moderator  
bachfan
member


Reged: 11/22/05
Posts: 196
Re: 7 Card Hand Evaluators [Re: Andrew Prock]
      #8544578 - 12/24/06 05:30 PM

Quote:


Under monte carlo, once the code is fast enough, it doesn't really matter too much if you make it twice as fast. You'll get close to the exact value quickly, but you'll never get the exact value. Said another way, doubling the number of evals won't double the convergence of the result. There are also issues of what sort of RNG you use, and equally important how you use it. Getting the RNG problem wrong can mean that your monte carlo simulations converge to the wrong result.




I'd just like to echo that an elaborate a bit. While computing exact results is very satisfying (and has a certain "gee whiz" appeal), in my case generating a fast exhaustive algorithm was a lot easier than generating a fast monte-carlo algorithm. Fortunately, it doesn't matter a lot in practice, at least not for me, because in almost all cases it is pretty easy to converge on a useful result.

There's another point I'd like to raise here - perhaps some other programmer types have some thoughts.

In developing propokertools, one of the biggest challenges has been generating random hands for monte carlo simulations such that the results correctly approach the same results from an exhaustive simulation. Now, I am fairly confident my algorithms work, and I have automated tests to "prove" it, but there are some situations (most of them academic and not useful in practice) where the random hand generation gets really bogged down. I don't want to get into too many technical details as that isn't my point (get to it, bachfan!). My point is, there may be a tradeoff to be made in certain situations between "correctness" and speed. In fact, you can think of monte-carlo as just such a tradeoff. There may well be some psycho-fast algorithm out there that is almost-but-not-quite-entirely-correct that, for purposes of mass generation (ie, for creating a bot using neural networks or whatever), might be the right way to go.

Just a thought, (babbling now)
bachfan


Post Extras: Print Post   Remind Me!   Notify Moderator  
Andrew Prock
enthusiast


Reged: 09/02/02
Posts: 346
Loc: oakland
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8544627 - 12/24/06 05:36 PM

Pstove does slow from about 100,000 million games a sec to about 1 game every 4 seconds (9 players on broadway, final player on random all).

Interesting behaviour. I looked at the code, and it seems that my method for selecting monte carlo hands is fairly inefficient. For those who are interested, in the monte carlo code I draw cards from an infinite deck for all the hands and then discard scenarios where two hands hold duplicate cards. Of course, when all the hands are drawn from a narrow distribution, this happens far too often.

Like I said, it's not how fast you evaluate...



Thanks for the info. I'll try and get a fix for that by the end of the year.

- Andrew


Post Extras: Print Post   Remind Me!   Notify Moderator  
bachfan
member


Reged: 11/22/05
Posts: 196
Re: 7 Card Hand Evaluators [Re: Andrew Prock]
      #8544630 - 12/24/06 05:36 PM

Quote:

I'v never really tried to make a fast general purpose evaluator. I wonder how fast I could make it?





I must admit, the same thing occurred to me in the shower this morning. I even started coding up a new approach, but then I realized it was almost isomorphic to my existing one. Ooops.

I did have a fun idea for a new way of evaluating omaha hands, which i coded up, but it turned out to be just about as fast as my existing code in most cases.
Ooops again. I may come back to that at some point.

- bachfan


Post Extras: Print Post   Remind Me!   Notify Moderator  
bachfan
member


Reged: 11/22/05
Posts: 196
Re: 7 Card Hand Evaluators [Re: Andrew Prock]
      #8544687 - 12/24/06 05:41 PM

Quote:


Interesting behaviour. I looked at the code, and it seems that my method for selecting monte carlo hands is fairly inefficient. For those who are interested, in the monte carlo code I draw cards from an infinite deck for all the hands and then discard scenarios where two hands hold duplicate cards. Of course, when all the hands are drawn from a narrow distribution, this happens far too often.





This is exactly the problem I was referring to a few posts back (sounds like we are taking the same approach here). I found some tricks for making things a bit faster in various cases, but I have yet to find a fast algorithm that generates the hands with the correct probability in these kinds of cases. I described the problem to my brother (a math professor at a major university), and the best he could come up with was "it sounds hard."

Cheers,
bachfan


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: bachfan]
      #8545105 - 12/24/06 06:34 PM

Pretend we have two players that play only two hands:

AK or AA.

First player: AK - 16 combos, AA 6 combos, total 22
First Players chance of playing AK is 16/22
First Players chance of playing AA is 6/22

Now assign the second player:

If player 1 was assigned AK (p = 16/22), then
Player 2: (Baysian updating)
Plays AK with 9/12 probability
Plays AA with 3/12 probability
If player 1 was assigned AA (p = 6/22), then
Player 2: (Baysian updating)
Plays AK with 8/9 probability
Plays AA with 1/9 probability

Now what is Player 2's chance of playing AA
(16/22)*(3/12) + (6/22)*(1/9) = 7/33

Now the problem: Player 1 and Player 2 had the same distributions. But 7/33 != 6/22. The problem is that assigning PLAYER 1 his distribution frist, gives player 2 an unequal chance of getting AA. The player most likely to be dealt pocket pairs, is the one where all the aces are in deck when he gets a card on his distribution. Notice that Player 2's chance of being distributed AA is less than Player 1, regardless of whether Player 1 was dealt AK or AA. (6/22 >> 3/12 or 1/9)

A correct algorithm, would at the very least, have to give both players (with equal distributions) an equal chance of getting AA. Thus, dealing everyone random hands, and discarding until everyone gets a hand on their distribution is an unbiased, but albeit extremely slow method of getting the right answer.


Post Extras: Print Post   Remind Me!   Notify Moderator  
bachfan
member


Reged: 11/22/05
Posts: 196
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8545192 - 12/24/06 06:47 PM

Quote:

Pretend we have two players that play only two hands:

AK or AA.

First player: AK - 16 combos, AA 6 combos, total 22
First Players chance of playing AK is 16/22
First Players chance of playing AA is 6/22

Now assign the second player:

If player 1 was assigned AK (p = 16/22), then
Player 2: (Baysian updating)
Plays AK with 9/12 probability
Plays AA with 3/12 probability
If player 1 was assigned AA (p = 6/22), then
Player 2: (Baysian updating)
Plays AK with 8/9 probability
Plays AA with 1/9 probability

Now what is Player 2's chance of playing AA
(16/22)*(3/12) + (6/22)*(1/9) = 7/33

Now the problem: Player 1 and Player 2 had the same distributions. But 7/33 != 6/22. The problem is that assigning PLAYER 1 his distribution frist, gives player 2 an unequal chance of getting AA. The player most likely to be dealt pocket pairs, is the one where all the aces are in deck when he gets a card on his distribution.

A correct algorithm, would at the very least, have to give both players (with equal distributions) an equal chance of getting AA. Thus, dealing everyone random hands, and discarding until everyone gets a hand on their distribution is an unbiased, but albeit extremely slow method of getting the right answer.




You are correct. If only you had written this earlier it would have saved me a few days of thought/work two years ago.

I came up with a (relatively) fast way to generate random razz hands, but could not prove it was correct. So I in fact implemented the extremely boneheaded (but unambiguosly correct) algorithm you outlined above for comparison purposes. Took billions of hands to converge within 0.01% in some cases, but converge they did.

- bachfan


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: bachfan]
      #8545205 - 12/24/06 06:49 PM

yeah, took me longer than 2 days to figure out why player 1 kept beating player 2 through 10, when i assigned them all the same distributions

Post Extras: Print Post   Remind Me!   Notify Moderator  
Phil153
Notable Twit


Reged: 10/18/05
Posts: 4905
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #8545926 - 12/24/06 08:50 PM

Quote:

Quote:

Phil why are you hostile? Did I ever say the world needed a poker stove that was 10,000 times faster? In fact, you gloss over the fact I said the exact opposite, because you want to pick a fight.



Phil153 + Idiots = Flames

Juk



FYP.

BTW, hand evaulation algorithms are not poker AI.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Phil153]
      #8553530 - 12/25/06 09:38 PM

Please could you all run this test on your hand evaluators and let me know the results:

int c1, c2, c3, c4, c5, c6, c7;
int handSetCounters[8] = {0,0,0,0,0,0,0,0};

for(c1 = 0; c1 < 52; c1++) {
for(c2 = c1 + 1; c2 < 52; c2++) {
for(c3 = c2 + 1; c3 < 52; c3++) {
for(c4 = c3 + 1; c4 < 52; c4++) {
for(c5 = c4 + 1; c5 < 52; c5++) {
for(c6 = c5 + 1; c6 < 52; c6++) {
for(c7 = c6 + 1; c7 < 52; c7++) {

CALCULATE RANK
CALCULATE SET ID (HIGH CARD TO STRAIGHT FLUSH)
INCREMENT HANDSETCOUNTERS[SET ID]

}}}}}}}

PRINT HANDSETCOUNTERS


If you could time the speed of your code in that scenario it would be most interesting! Also with a copy of your results (number of flushes etc incase your code is not designed to be 100% accurate).

Thanks for anyone that participates!

Tom


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8554678 - 12/25/06 11:51 PM

I've done the tests. It took some rewriting of my code to make your tests work, and i haven't yet checked them for accuracy.

Without the checksums i was getting 117,354,877 games per second. It took 1.14 seconds to process all 133,784,560 games in the loops.

With the check sums, which i didn't spend time optimizing it took 1.71 seconds, for a speed of 78,236,584 games per second.

The result of the check sums are as follows:
In order from lowest hand to highest (hi-card, 1 pair, 2 pair, 3 kind, straight, flush, full house, four kind, to straight flush).

23294460
58627800
31433400
6461620
6180020
4047644
3473184
224848
41584

Let me know if you see errors in the checksum. I actually had to look at code that is no longer in my program to interpret my lookup tables, cause all my program knows now is that higher integer > better cards. I wrote a compression algorithm to make all the rankings fit on one integer, and had to decompress the results for your test, so its possible i could have made a mistake. Its also why the checksums almost double the time it takes for my program to resolve values.

P.s. i know 117 million is faster than the 60 million i said earlier. My program is designed to do a lot more than just resolve hand values. I took out some of the overhead, for this test.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Phil153
Notable Twit


Reged: 10/18/05
Posts: 4905
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8555081 - 12/26/06 12:47 AM

With these speeds you obviously have all your precalculated results in a single (very large) table, and nothing is calculated. The time for your algorithm is basically the time to increment the 7 counters(c1..c7), do one bitwise operation, look up the value in a a humongous table, and add the results.

This is incredibly simple to code if you don't mind a 200MB file on your hard file. It's actually easier to code than a 15 million hand/second algorithm, and far easier to code than an advanced and complex algorithm like PokerStove. If you used the same massive table in PokerStove, it'd probably run 5x faster than your code.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Phil153]
      #8555117 - 12/26/06 12:53 AM

Phil - do i have lookup tables yes. The same way you have a calculator to look up multiplication, while you perform calculus. But you'd be kidding yourself if you think you could put all the results in one look up table. As I've already said such a table would be several Gigs in size - and extremely slow if you used a hardrive. Mine is 15 MB.

If you think i'm just pushing 7 counters, i'd be resolving about 6 to 9 billion games a second. Why is it you assume I'm only capable of coding a simple algorithms. My programming skills may be sub-par, but i've spent years studying game theory - so why is it u assume my algorithms "must" be simple? More specifically, why do you respond to everything with "BS" "idiot" an the like, is the fact the someone else can code something like this a "threat" to you?


Post Extras: Print Post   Remind Me!   Notify Moderator  
A.Nironen
member


Reged: 10/29/06
Posts: 118
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8556504 - 12/26/06 07:13 AM

Quote:


If you could time the speed of your code in that scenario it would be most interesting! Also with a copy of your results (number of flushes etc incase your code is not designed to be 100% accurate).





Gullanian,

Here is my demo (210 Kb).

http://www.pokerbolide.com/files/handeval7cards.exe

Andrzej Nironen


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: A.Nironen]
      #8557919 - 12/26/06 12:58 PM

D&L, congratulations, your numbers are correct and I am very impressed.

PokerBolide.com, wow. I am amazed. I thought I had the optimum solution, but you have out done me and months of work, im truly impressed.

Congratulations both of you, I don't know how you do it but they seem to work superbly.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8557964 - 12/26/06 01:05 PM

PokerBolide.com, wow, just running again and it's superb. My solution used look up tables, some faily large in size, and yours does not seem to at all. Superb.

Post Extras: Print Post   Remind Me!   Notify Moderator  
A.Nironen
member


Reged: 10/29/06
Posts: 118
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8558007 - 12/26/06 01:14 PM

Quote:

PokerBolide.com, wow, just running again and it's superb. My solution used look up tables, some faily large in size, and yours does not seem to at all. Superb.



My algorithm does use lookup tables, but they are not big (no more than a couple of megabytes) and they are calculated in a moment at program start.

Andrzej Nironen


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: A.Nironen]
      #8558391 - 12/26/06 02:07 PM

Dear Andrzej,

Yeah I had kind of guessed that, a no LUT solution would be impossible.

Anyway, I've thought of a new solution that could potentially be incredibly fast without the need of (large) LUT's, keep an eye on this post and I will let you know how it gets on.

Tom


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8559084 - 12/26/06 03:34 PM

Quote:

Pretend we have two players that play only two hands:

AK or AA.

First player: AK - 16 combos, AA 6 combos, total 22
First Players chance of playing AK is 16/22
First Players chance of playing AA is 6/22

Now assign the second player:

If player 1 was assigned AK (p = 16/22), then
Player 2: (Baysian updating)
Plays AK with 9/12 probability
Plays AA with 3/12 probability
If player 1 was assigned AA (p = 6/22), then
Player 2: (Baysian updating)
Plays AK with 8/9 probability
Plays AA with 1/9 probability

Now what is Player 2's chance of playing AA
(16/22)*(3/12) + (6/22)*(1/9) = 7/33

Now the problem: Player 1 and Player 2 had the same distributions. But 7/33 != 6/22. The problem is that assigning PLAYER 1 his distribution frist, gives player 2 an unequal chance of getting AA. The player most likely to be dealt pocket pairs, is the one where all the aces are in deck when he gets a card on his distribution. Notice that Player 2's chance of being distributed AA is less than Player 1, regardless of whether Player 1 was dealt AK or AA. (6/22 >> 3/12 or 1/9)

A correct algorithm, would at the very least, have to give both players (with equal distributions) an equal chance of getting AA. Thus, dealing everyone random hands, and discarding until everyone gets a hand on their distribution is an unbiased, but albeit extremely slow method of getting the right answer.




Your Baysian process ignores the times where one/both of the players have an unplayable hand.

Of the times both players have playable cards:

Player 1 will have AA, and Player B will have AA with probability 1/41.
Player 1 will have AA, and Player B will have AK with probability 8/41.
Player 1 will have AK, and Player B will have AA with probability 8/41.
Player 1 will have AK, and Player B will have AK with probability 24/41.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8559337 - 12/26/06 04:05 PM

how fast is pokerboldies?

NM - 35Mil a second.

Anyways I see you are all really smart people (exception Phil). If someone could figure out a way to assign a unique index value between 1 and 133784560 for 7 unordered cards, you could dump everything into a 133 MB lookup table.

I wouldn't mind doing that, since my code isn't really for commercial use, and I think the throughput would go up from my 117 million a second, to maybe a few billion.

But, i haven't figured out a way to assign a unique ID to 7 unordered, (up to 52 - noduplicating) numbers. At least not a method that doesn't require putting them in order first, which might negate the speed advantages.


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8560006 - 12/26/06 05:26 PM

Quote:

how fast is pokerboldies?

NM - 35Mil a second.

Anyways I see you are all really smart people (exception Phil). If someone could figure out a way to assign a unique index value between 1 and 133784560 for 7 unordered cards, you could dump everything into a 133 MB lookup table.

I wouldn't mind doing that, since my code isn't really for commercial use, and I think the throughput would go up from my 117 million a second, to maybe a few billion.

But, i haven't figured out a way to assign a unique ID to 7 unordered, (up to 52 - noduplicating) numbers. At least not a method that doesn't require putting them in order first, which might negate the speed advantages.




I tried something similar to this as a method to speed existing hand evaluators: Using the idea of Combinadics it's possible to assign a unique lexicographical index for each ordered combination.

If the hand is represented by a 52 bit number with 7 bits set true to indicate which cards you have in your hand (ie: like pokersource's "packed" format), then you can calculate the lexicographical index as though the cards are ordered (ie: the bitstring representation orders them implicitly) by traversing the bitstring from the least(/most) significant bit.

The naive approach would require testing and summation of each of the 52 bits individually, but using a lookup table it is possible to create partial sums of C() and do whole blocks of 16 bits at a time and thus just do 4 summations rather than all 52 (actually it's possible to do just 3 if you use 18 bits, but this requires 4x more memory to hold the lookups).

The idea is then that we can use the lexicographic index we created to then index into another lookup of 133784560 precomputed 16bit hand evaluations (this means you can use any old [unoptimized] hand evaluator and just concentrate on optimizing the lookup accessing code...).

I tested the idea out very quickly just using the pokersource hand evaluator to create the lookup, and managed to get aprroximately a 2x speedup (21 million hands/sec -> 42 million hands/sec on a 3GHz P4), at the expense of using about 500MB of lookup tables.

I haven't yet tried optimizing the generated assembler (which looked fairly suboptimal) nor have I yet tried using the x86's special BSF/BSR instructions to scan the bitstring (using BSF/BSR we can do just 7 summations and without the need for 2d/3d index dereferencing used by the partial sum method outlined above).

Code:
// FastHandEval.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "random.h"
#include <Crtdbg.h>
#include <time.h>

#include <iostream>

#ifdef __cplusplus
extern "C" {
#include "poker_defs.h"
#include "deck_std.h"
#include "inlines/eval.h"
}
#else
#include "poker_defs.h"
#include "inlines/eval.h"
#endif

using namespace std;

// How mnay iterations do we want to run the etsts on.
#define ITERS 1000000000

// How many tests shall we generate.
#define NUM_TESTS 10000

// Precomputed combination lookups (huge factorials like 52! are a pain to generate here...).
int C[52][8]={
{0,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0},
{0,2,1,0,0,0,0,0},
{0,3,3,1,0,0,0,0},
{0,4,6,4,1,0,0,0},
{0,5,10,10,5,1,0,0},
{0,6,15,20,15,6,1,0},
{0,7,21,35,35,21,7,1},
{0,8,28,56,70,56,28,8},
{0,9,36,84,126,126,84,36},
{0,10,45,120,210,252,210,120},
{0,11,55,165,330,462,462,330},
{0,12,66,220,495,792,924,792},
{0,13,78,286,715,1287,1716,1716},
{0,14,91,364,1001,2002,3003,3432},
{0,15,105,455,1365,3003,5005,6435},
{0,16,120,560,1820,4368,8008,11440},
{0,17,136,680,2380,6188,12376,19448},
{0,18,153,816,3060,8568,18564,31824},
{0,19,171,969,3876,11628,27132,50388},
{0,20,190,1140,4845,15504,38760,77520},
{0,21,210,1330,5985,20349,54264,116280},
{0,22,231,1540,7315,26334,74613,170544},
{0,23,253,1771,8855,33649,100947,245157},
{0,24,276,2024,10626,42504,134596,346104},
{0,25,300,2300,12650,53130,177100,480700},
{0,26,325,2600,14950,65780,230230,657800},
{0,27,351,2925,17550,80730,296010,888030},
{0,28,378,3276,20475,98280,376740,1184040},
{0,29,406,3654,23751,118755,475020,1560780},
{0,30,435,4060,27405,142506,593775,2035800},
{0,31,465,4495,31465,169911,736281,2629575},
{0,32,496,4960,35960,201376,906192,3365856},
{0,33,528,5456,40920,237336,1107568,4272048},
{0,34,561,5984,46376,278256,1344904,5379616},
{0,35,595,6545,52360,324632,1623160,6724520},
{0,36,630,7140,58905,376992,1947792,8347680},
{0,37,666,7770,66045,435897,2324784,10295472},
{0,38,703,8436,73815,501942,2760681,12620256},
{0,39,741,9139,82251,575757,3262623,15380937},
{0,40,780,9880,91390,658008,3838380,18643560},
{0,41,820,10660,101270,749398,4496388,22481940},
{0,42,861,11480,111930,850668,5245786,26978328},
{0,43,903,12341,123410,962598,6096454,32224114},
{0,44,946,13244,135751,1086008,7059052,38320568},
{0,45,990,14190,148995,1221759,8145060,45379620},
{0,46,1035,15180,163185,1370754,9366819,53524680},
{0,47,1081,16215,178365,1533939,10737573,62891499},
{0,48,1128,17296,194580,1712304,12271512,73629072},
{0,49,1176,18424,211876,1906884,13983816,85900584},
{0,50,1225,19600,230300,2118760,15890700,99884400},
{0,51,1275,20825,249900,2349060,18009460,115775100}
};

// Used to store the precomuted 16bit partial sums of C(a,b) along with the current set-bit count.
typedef struct LookupType {
unsigned int Index16;
unsigned char Count16;
} LookupType;

// The main lookup for the combinadic partial sums.
LookupType LookupTable[4][0x10000][8];

// Used to pregenerate the tests.
unsigned long long TestL[NUM_TESTS];
unsigned int Tests[NUM_TESTS][2];

// The eval lookup indexed using the lexicographical index.
unsigned int Evals[133784560];

// -------------------------------------------------------------------------------------

// Had to put this here to avoid problems with nested macros in the pokersource lib...
void GenLookup(CardMask &PossibleExtraCards) {

unsigned int InputA,InputB;
int Index;
int J;

// This holds all of the cards we have used so far.
CardMask AllUsedCards;
CardMask_RESET(AllUsedCards);

uint64 T=(uint64)PossibleExtraCards.cards.spades
|((uint64)PossibleExtraCards.cards.clubs<<13)
|((uint64)PossibleExtraCards.cards.diamonds<<26)
|((uint64)PossibleExtraCards.cards.hearts<<39);

InputA=T;
InputB=(T>>32)&(0x100000-1);

Index=LookupTable[0][InputA&0xffff][0].Index16;
J=LookupTable[0][InputA&0xffff][0].Count16;
Index+=LookupTable[1][InputA>>16][J].Index16;
J=LookupTable[1][InputA>>16][J].Count16;
Index+=LookupTable[2][InputB&0xffff][J].Index16;
J=LookupTable[2][InputB&0xffff][J].Count16;
Index+=LookupTable[3][InputB>>16][J].Index16;

// Use the pokersource lib to create the evaluation value for the lookup.
Evals[Index]=Hand_EVAL_N(PossibleExtraCards,7);
}

// -------------------------------------------------------------------------------------

void InitLookups(void) {

// This hold the emumerated turn and river card combos.
CardMask PossibleExtraCards;

// Clear the lookups.
for (int InputX=0;InputX<4;InputX++) {
for (int InputY=0;InputY<0x10000;InputY++) {
for (int Count=0;Count<8;Count++) {
LookupTable[InputX][InputY][Count].Index16=0;
LookupTable[InputX][InputY][Count].Count16=0;
}
}
}

// Create the lookups.
for (int InputX=0;InputX<4;InputX++) {
for (int InputY=0;InputY<0x10000;InputY++) {
int CurrentInput=InputY;
for (int StartCount=0;StartCount<7;StartCount++) {
int CurrentCount=StartCount;
for (int I=0,J=1;I<16 && CurrentCount<7;I++) {
if ((CurrentInput>>I)&1LL) {
LookupTable[InputX][InputY][StartCount].Index16+=C[(InputX*16)+I][(CurrentCount++)+1];
}
}
LookupTable[InputX][InputY][StartCount].Count16=CurrentCount;
}
}
}

// Clear to be sure (no need really)...
for (int I=0;I<133784560;I++) {
Evals[I]=0;
}

// Make the ranking lookups.
// ### NOTE: This will mess up if Hand_EVAL_N is nested in it...
DECK_ENUMERATE_N_CARDS(StdDeck,PossibleExtraCards,7,
{
GenLookup(PossibleExtraCards);
}); // End Enumerate all combos.

}

// -------------------------------------------------------------------------------------

void InitTests(void) {

// lets make the tests.
for (int I=0;I<NUM_TESTS;I++) {
TestL[I]=0;
for (int T=0;T<7;T++) {
int R;
do {
R=RandInt(51);
} while ((TestL[I]>>R)&1LL);
TestL[I]|=(1LL<<R);
}
Tests[I][0]=TestL[I]&0xffffffff;
Tests[I][1]=(TestL[I]>>32)&0xffffffff;
}

}

// -------------------------------------------------------------------------------------

int _tmain(int argc, _TCHAR* argv[])
{
int J;

unsigned int InputA,InputB;
int Index;

unsigned int MyEval;

// Make the looups and the test data.
cout << "Creating Lookups & Tests... "; cout.flush();
InitLookups();
InitTests();
cout << "Done." << endl;

// Store the start time.
clock_t StartTime=clock();

for (int K=0;K<ITERS;K++) {

// Get the lexicographical index.
InputA=Tests[K%NUM_TESTS][0];
InputB=Tests[K%NUM_TESTS][1];

Index=LookupTable[0][InputA&0xffff][0].Index16;
J=LookupTable[0][InputA&0xffff][0].Count16;
Index+=LookupTable[1][InputA>>16][J].Index16;
J=LookupTable[1][InputA>>16][J].Count16;
Index+=LookupTable[2][InputB&0xffff][J].Index16;
J=LookupTable[2][InputB&0xffff][J].Count16;
Index+=LookupTable[3][InputB>>16][J].Index16;

// Use the evaluation index by the lexicographical index.
MyEval=Evals[Index];

/********* UNOPTIMIZED COMPARISON (HACKY CRAP...) **********
CardMask TestCards;
TestCards.cards.spades=TestL[K%NUM_TESTS];
TestCards.cards.clubs=TestL[K%NUM_TESTS]>>13;
TestCards.cards.diamonds=TestL[K%NUM_TESTS]>>26;
TestCards.cards.hearts=TestL[K%NUM_TESTS]>>39;

// Use the original pokersource eval function fior camparison.
MyEval=Hand_EVAL_N(TestCards,7);
***********************************************************/

}

// Print this heare to force the optimizing compiler to not ignore the above loop...
cout << MyEval << endl;

// Print the timings.
cout << (double)(clock()-StartTime)/(double)CLOCKS_PER_SEC << " seocnds" << endl;
cout << (int)((double)ITERS/((double)(clock()-StartTime)/(double)CLOCKS_PER_SEC)) << " hands per second" << endl;

return 0;
}



NOTE: To use the above code you need both the pokersource hand evaluation library (or you can use ANY hand eval library so long as you set the Evals[133784560] correctly) and a function "RandInt(X)" which generates a random integer in the range {0..X}.

Juk

PS: Sorry the code is very hacky, but I just wrote this to test it out quickly today and didn't spend much time on it. Hopefully it should help explain the method I outlined above.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #8560189 - 12/26/06 05:48 PM

Great post Juk. I gotta book mark this one, so i can come back and study it. I don't know C++ (or combinadics theory) so its like learnign two things at once .

Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8560395 - 12/26/06 06:07 PM

Just a note D&L, you don't need to return a number between 1 and 130 million for the strength of a hand, you can collapse the hands into unique equivalence classes, which there turn out to be about 7,500 unqiue hands.

Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8560406 - 12/26/06 06:07 PM

Also, because my evaluator has been beaten hands down, I will publish my technique very soon. I'll post a link in this thread when it is done.

Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8560432 - 12/26/06 06:10 PM

Jukofyork has basically done what I have done in my technique, looking at the lexographical index of all precomputing hand ranks. The ordering part of my code is the best, classical sorting algorithms are too slow for this job.

Post Extras: Print Post   Remind Me!   Notify Moderator  
A.Nironen
member


Reged: 10/29/06
Posts: 118
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8564493 - 12/27/06 01:15 AM

Quote:

If someone could figure out a way to assign a unique index value between 1 and 133784560 for 7 unordered cards, you could dump everything into a 133 MB lookup table.




Single byte for hand's strength is not enough, so the table must be 266 MB at least.

Andrzej Nironen


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: A.Nironen]
      #8565117 - 12/27/06 02:40 AM

Quote:

Quote:

If someone could figure out a way to assign a unique index value between 1 and 133784560 for 7 unordered cards, you could dump everything into a 133 MB lookup table.




Single byte for hand's strength is not enough, so the table must be 266 MB at least.

Andrzej Nironen




Actually it can be done in under 133 MB...


I've got an array of approx 30 million integers.

One way of accessing a hand ranking is:


Rank := Eval[Eval[Eval[Eval[Eval[Eval[Eval[c1]+c2]+c3]+c4]+c5]+c6]+c7];

The general idea is there are:

1 card hands. 52x52
2 card hands. 1326x52
3 card hands. 22100x52
4 card hands. 84448x52
5 card hands. 152607x52
6 card hands. 352443x52

For each N card hand, there are 52 vectors to the appropriate N+1 card hand.

For 6 card hands, instead of vectors to the 7 card hand, the array holds the Hand Ranking of the 7 card hand.


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8568358 - 12/27/06 01:32 PM

Quote:

Please could you all run this test on your hand evaluators and let me know the results:

int c1, c2, c3, c4, c5, c6, c7;
int handSetCounters[8] = {0,0,0,0,0,0,0,0};

for(c1 = 0; c1 < 52; c1++) {
for(c2 = c1 + 1; c2 < 52; c2++) {
for(c3 = c2 + 1; c3 < 52; c3++) {
for(c4 = c3 + 1; c4 < 52; c4++) {
for(c5 = c4 + 1; c5 < 52; c5++) {
for(c6 = c5 + 1; c6 < 52; c6++) {
for(c7 = c6 + 1; c7 < 52; c7++) {

CALCULATE RANK
CALCULATE SET ID (HIGH CARD TO STRAIGHT FLUSH)
INCREMENT HANDSETCOUNTERS[SET ID]

}}}}}}}

PRINT HANDSETCOUNTERS


If you could time the speed of your code in that scenario it would be most interesting! Also with a copy of your results (number of flushes etc incase your code is not designed to be 100% accurate).

Thanks for anyone that participates!

Tom




Code:
133784560 hands
2.640 secs
50675964 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584

using

procedure TForm1.Button2Click(Sender: TObject);
var
Totals : array[0..9] of integer;
c1, c2, c3, c4, c5, c6, c7 : integer;
Count : integer;
Start, Stop : TDateTime;
secs : double;
Score : integer;
begin
Start := Now;
Fillchar(Totals,sizeof(Totals),0);
Count := 0;
for c1 := 0 to 45 do
for c2 := c1+1 to 46 do
for c3 := c2+1 to 47 do
for c4 := c3+1 to 48 do
for c5 := c4+1 to 49 do
for c6 := c5+1 to 50 do
for c7 := c6+1 to 51 do
begin
inc(Totals[Table[Table[Table[Table[Table[Table[Table[c1+52]+c2]+c3]+c4]+c5]+c6]+c7] shr 20]);
inc(Count);
end;
Stop := Now;
secs := (Stop-Start)*86400.0;
memo1.lines.add(format('%d hands',[Count]));
memo1.lines.add(format('%5.3f secs',[secs]));
memo1.lines.add(format('%d hands/sec',[trunc(Count/secs)]));
memo1.lines.add(format('hc %d',[Totals[1]]));
memo1.lines.add(format('pr %d',[Totals[2]]));
memo1.lines.add(format('tp %d',[Totals[3]]));
memo1.lines.add(format('th %d',[Totals[4]]));
memo1.lines.add(format('st %d',[Totals[5]]));
memo1.lines.add(format('fl %d',[Totals[6]]));
memo1.lines.add(format('fh %d',[Totals[7]]));
memo1.lines.add(format('fr %d',[Totals[8]]));
memo1.lines.add(format('sf %d',[Totals[9]]));
end;


and

133784560 hands
1.188 secs
112613302 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584

using

procedure TForm1.Button1Click(Sender: TObject);
var
Totals : array[0..9] of integer;
c1, c2, c3, c4, c5, c6, c7 : integer;
u1, u2, u3, u4, u5, u6, u7 : integer;
Count : integer;
Start, Stop : TDateTime;
secs : double;
Score : integer;
begin
Start := Now;
Fillchar(Totals,sizeof(Totals),0);
Count := 0;
for c1 := 0 to 45 do
begin
U1 := Table[52+c1];
for c2 := c1+1 to 46 do
begin
U2 := Table[U1+c2];
for c3 := c2+1 to 47 do
begin
U3 := Table[U2+c3];
for c4 := c3+1 to 48 do
begin
U4 := Table[U3+c4];
for c5 := c4+1 to 49 do
begin
U5 := Table[U4+c5];
for c6 := c5+1 to 50 do
begin
U6 := Table[U5+c6];
for c7 := c6+1 to 51 do
begin
inc(Totals[Table[U6+c7] shr 20]);
inc(Count);
end;
end;
end;
end;
end;
end;
end;
Stop := Now;
secs := (Stop-Start)*86400.0;
memo1.lines.add(format('%d hands',[Count]));
memo1.lines.add(format('%5.3f secs',[secs]));
memo1.lines.add(format('%d hands/sec',[trunc(Count/secs)]));
memo1.lines.add(format('hc %d',[Totals[1]]));
memo1.lines.add(format('pr %d',[Totals[2]]));
memo1.lines.add(format('tp %d',[Totals[3]]));
memo1.lines.add(format('th %d',[Totals[4]]));
memo1.lines.add(format('st %d',[Totals[5]]));
memo1.lines.add(format('fl %d',[Totals[6]]));
memo1.lines.add(format('fh %d',[Totals[7]]));
memo1.lines.add(format('fr %d',[Totals[8]]));
memo1.lines.add(format('sf %d',[Totals[9]]));
end;





This was on an AMD 3000+ with 1 gig memory running Win2k

This computer produced

133784560 hands
5.156 secs
25947355 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584

using Andrzej Nironen's "Hand Evaluator Speed Demo"


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8568544 - 12/27/06 01:52 PM

Quote:

Quote:

Please could you all run this test on your hand evaluators and let me know the results:

int c1, c2, c3, c4, c5, c6, c7;
int handSetCounters[8] = {0,0,0,0,0,0,0,0};

for(c1 = 0; c1 < 52; c1++) {
for(c2 = c1 + 1; c2 < 52; c2++) {
for(c3 = c2 + 1; c3 < 52; c3++) {
for(c4 = c3 + 1; c4 < 52; c4++) {
for(c5 = c4 + 1; c5 < 52; c5++) {
for(c6 = c5 + 1; c6 < 52; c6++) {
for(c7 = c6 + 1; c7 < 52; c7++) {

CALCULATE RANK
CALCULATE SET ID (HIGH CARD TO STRAIGHT FLUSH)
INCREMENT HANDSETCOUNTERS[SET ID]

}}}}}}}

PRINT HANDSETCOUNTERS


If you could time the speed of your code in that scenario it would be most interesting! Also with a copy of your results (number of flushes etc incase your code is not designed to be 100% accurate).

Thanks for anyone that participates!

Tom




Code:
133784560 hands
2.640 secs
50675964 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584

using

procedure TForm1.Button2Click(Sender: TObject);
var
Totals : array[0..9] of integer;
c1, c2, c3, c4, c5, c6, c7 : integer;
Count : integer;
Start, Stop : TDateTime;
secs : double;
Score : integer;
begin
Start := Now;
Fillchar(Totals,sizeof(Totals),0);
Count := 0;
for c1 := 0 to 45 do
for c2 := c1+1 to 46 do
for c3 := c2+1 to 47 do
for c4 := c3+1 to 48 do
for c5 := c4+1 to 49 do
for c6 := c5+1 to 50 do
for c7 := c6+1 to 51 do
begin
inc(Totals[Table[Table[Table[Table[Table[Table[Table[c1+52]+c2]+c3]+c4]+c5]+c6]+c7] shr 20]);
inc(Count);
end;
Stop := Now;
secs := (Stop-Start)*86400.0;
memo1.lines.add(format('%d hands',[Count]));
memo1.lines.add(format('%5.3f secs',[secs]));
memo1.lines.add(format('%d hands/sec',[trunc(Count/secs)]));
memo1.lines.add(format('hc %d',[Totals[1]]));
memo1.lines.add(format('pr %d',[Totals[2]]));
memo1.lines.add(format('tp %d',[Totals[3]]));
memo1.lines.add(format('th %d',[Totals[4]]));
memo1.lines.add(format('st %d',[Totals[5]]));
memo1.lines.add(format('fl %d',[Totals[6]]));
memo1.lines.add(format('fh %d',[Totals[7]]));
memo1.lines.add(format('fr %d',[Totals[8]]));
memo1.lines.add(format('sf %d',[Totals[9]]));
end;


and

133784560 hands
1.188 secs
112613302 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584

using

procedure TForm1.Button1Click(Sender: TObject);
var
Totals : array[0..9] of integer;
c1, c2, c3, c4, c5, c6, c7 : integer;
u1, u2, u3, u4, u5, u6, u7 : integer;
Count : integer;
Start, Stop : TDateTime;
secs : double;
Score : integer;
begin
Start := Now;
Fillchar(Totals,sizeof(Totals),0);
Count := 0;
for c1 := 0 to 45 do
begin
U1 := Table[52+c1];
for c2 := c1+1 to 46 do
begin
U2 := Table[U1+c2];
for c3 := c2+1 to 47 do
begin
U3 := Table[U2+c3];
for c4 := c3+1 to 48 do
begin
U4 := Table[U3+c4];
for c5 := c4+1 to 49 do
begin
U5 := Table[U4+c5];
for c6 := c5+1 to 50 do
begin
U6 := Table[U5+c6];
for c7 := c6+1 to 51 do
begin
inc(Totals[Table[U6+c7] shr 20]);
inc(Count);
end;
end;
end;
end;
end;
end;
end;
Stop := Now;
secs := (Stop-Start)*86400.0;
memo1.lines.add(format('%d hands',[Count]));
memo1.lines.add(format('%5.3f secs',[secs]));
memo1.lines.add(format('%d hands/sec',[trunc(Count/secs)]));
memo1.lines.add(format('hc %d',[Totals[1]]));
memo1.lines.add(format('pr %d',[Totals[2]]));
memo1.lines.add(format('tp %d',[Totals[3]]));
memo1.lines.add(format('th %d',[Totals[4]]));
memo1.lines.add(format('st %d',[Totals[5]]));
memo1.lines.add(format('fl %d',[Totals[6]]));
memo1.lines.add(format('fh %d',[Totals[7]]));
memo1.lines.add(format('fr %d',[Totals[8]]));
memo1.lines.add(format('sf %d',[Totals[9]]));
end;





This was on an AMD 3000+ with 1 gig memory running Win2k

This computer produced

133784560 hands
5.156 secs
25947355 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584

using Andrzej Nironen's "Hand Evaluator Speed Demo"



Very impressive! (I think you have the results mixed up though - 112.5mil when doing extra work of incrementing the hand type counts, 50mil when not... )

Have you considered posting this method on the pokersource yahoo group?

Juk

EDIT: Sorry, I just noticed you print the hand type counts in both procedures... BTW: How fast does it run without doing this (ie: just assigning an integer the hand evaluation rather than incrementing totals[]?)

Edited by jukofyork (12/27/06 01:58 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #8569429 - 12/27/06 03:18 PM

Quote:

Quote:


Code:

133784560 hands
1.188 secs
112613302 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584





This was on an AMD 3000+ with 1 gig memory running Win2k

This computer produced

133784560 hands
5.156 secs
25947355 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584

using Andrzej Nironen's "Hand Evaluator Speed Demo"



Very impressive! (I think you have the results mixed up though - 112.5mil when doing extra work of incrementing the hand type counts, 50mil when not... )

Have you considered posting this method on the pokersource yahoo group?

Juk

EDIT: Sorry, I just noticed you print the hand type counts in both procedures... BTW: How fast does it run without doing this (ie: just assigning an integer the hand evaluation rather than incrementing totals[]?)




0.953 secs
140382574 hands/sec

However I'm doubting the granularity of the timing process used.

I'll switch to the RDTSC method and repost relative values.

Using RDTSC as a timer

133784560 hands
2.640 secs
50675964 hands/sec

becomes

133784560 hands
5752663748 cycles
42.999 cycles/hand

133784560 hands
1.188 secs
112613302 hands/sec

becomes

133784560 hands
2645152642 cycles
19.772 cycles/hand

and

133784560 hands
0.953 secs
140382574 hands/sec

becomes

133784560 hands
2077269818 cycles
15.527 cycles/hand


Edited by mykey1961 (12/27/06 03:39 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
A.Nironen
member


Reged: 10/29/06
Posts: 118
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #8571274 - 12/27/06 06:19 PM

I've made some revision of my code. Enumeration wasn't optimal. Now it runs 30% faster with the same hand evaluation function.
http://www.pokerbolide.com/files/handeval7cards.exe

Andrzej Nironen


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8571510 - 12/27/06 06:48 PM

how big are the LU tables combined under this method?

Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8572063 - 12/27/06 07:46 PM

Follow up. Trying to figure out how u map from table 5 to 6, or table 6 to a final hand value without large memory requirements. There about 2.5 Million combinations 5 cards, thust your table would seem to require 2.5 million combinations, yielding a table 6 that is lil over 100 MB.

The one way i thought around this is that your tables may already be condensed using equivalence classes, so the numbers could be smaller. That is, maybe if I wrote the big tables, and backwards eliminated duplicates, it would condense.

Did you do something like that, or totally different?

P.s. your tables lookups for c1 through c3 would probably be a pretty small part of your table(s).

Why not just write them as an array (52,52,52) and put duplicate values in the array to that (1,2,3) = (3,2,1). Looking up a single value from a multi dimensional array is usually much faster than looking up values from 3 different arrays, and performing addition between each lookup.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
279Mill a second..... [Re: mykey1961]
      #8572720 - 12/27/06 08:52 PM

Made some adjustments to my code, am currently getting 279 Million games per second. Although I still think your algorithm may have greater potential (possibly if I can combine it with mine).

Still not ready to share my algorithm...but i'll provide the following sketch of it...i ommitted some parts, changed descriptive names, etc...so that it can't just be copied (at least not easily).

As you can see its speed is primarily due to being able to solve for many hands in one lookup, skipping to more complex analysis as needed. Whether more complex analysis is needed is also determined by a lookup table.

For c1 = 1 To 46
nFTR(1) = CN(c1)
For c2 = c1 + 1 To 47
nFTR(2) = CN(c2)
For c3 = c2 + 1 To 48
nFTR(3) = CN(c3)
For c4 = c3 + 1 To 49
nFTR(4) = CN(c4)
For c5 = c4 + 1 To 50
nFTR(5) = CN(c5)

rec_number = vRec_Num(non-lexographic indexing algorithm...nftr 1 through 5 as input)

With vType(nftr(1 through 5) as input)

If .Type = 0 Then


For c6 = c5 + 1 To 51
For c7 = c6 + 1 To 52

rank = DW_Array(rec_number + sub_index(c6,c7))

' Select Case rank
' Case 0 To 3000
' h1 = h1 + 1
' Case 3001 To 7000
' h2 = h2 + 1
' Case 7001 To 9000
' h3 = h3 + 1
' Case 9001 To 11000
' h4 = h4 + 1
' Case 11001 To 12000
' h5 = h5 + 1
' Case 12001 To 15000
' h6 = h6 + 1
' Case 15001 To 16000
' h7 = h7 + 1
' Case 16001 To 17000
' h8 = h8 + 1
' Case Else
' h9 = h9 + 1
' End Select

Next c7
Next c6

Else

...omitted...

For c6 = c5 + 1 To 51
For c7 = c6 + 1 To 52

vType = .Type
vnumber = .tnumber

If SN(c6) = vType Then vnumber = vnumber + 1: adjust(vnumber) = CN(c6)
If SN(c7) = vType Then vnumber = vnumber + 1: adjust(vnumber) = CN(c7)

Select Case vnumber
Case Is condition1
rank = DW_Array(rec_number + cindex(c6, c7))
Case condition2
rank = SW_Array(Rec_number2 (another non-lexographic indexing algorithm) * XX)
Case condition3
rank = SW_Array(Rec_number2 * XX + fn1(adjust(6)))
Case Else
rank = SW_Array(Rec_number2 * XX + cindex(c6, c7))
End Select

' Select Case rank
' Case 0 To 3000
' h1 = h1 + 1
' Case 3001 To 7000
' h2 = h2 + 1
' Case 7001 To 9000
' h3 = h3 + 1
' Case 9001 To 11000
' h4 = h4 + 1
' Case 11001 To 12000
' h5 = h5 + 1
' Case 12001 To 15000
' h6 = h6 + 1
' Case 15001 To 16000
' h7 = h7 + 1
' Case 16001 To 17000
' h8 = h8 + 1
' Case Else
' h9 = h9 + 1
' End Select

Next c7
Next c6

End If
End With

Next c5
Next c4
Next c3
Next c2
Next c1

'dump counters

'Output.Text = h1 & vbNewLine & h2 & vbNewLine & h3 & vbNewLine & h4 & vbNewLine & h5 & vbNewLine & h6 & vbNewLine & h7 & vbNewLine & h8 & vbNewLine & h9 &
vbNewLine


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8574056 - 12/27/06 11:52 PM

Quote:

Follow up. Trying to figure out how u map from table 5 to 6, or table 6 to a final hand value without large memory requirements. There about 2.5 Million combinations 5 cards, thust your table would seem to require 2.5 million combinations, yielding a table 6 that is lil over 100 MB.

The one way i thought around this is that your tables may already be condensed using equivalence classes, so the numbers could be smaller. That is, maybe if I wrote the big tables, and backwards eliminated duplicates, it would condense.

Did you do something like that, or totally different?

P.s. your tables lookups for c1 through c3 would probably be a pretty small part of your table(s).

Why not just write them as an array (52,52,52) and put duplicate values in the array to that (1,2,3) = (3,2,1). Looking up a single value from a multi dimensional array is usually much faster than looking up values from 3 different arrays, and performing addition between each lookup.




The table I use is for best 5 of 7 cards, not best 5 of 6, or just the only 5 given. Therefore I take advantage of that information when identifing the states.

The "compression" occurs starting at the 4th card.

If the first 4 cards of a 7 card hand are all of different suits, there is no way we could make a flush.

Therefore AsKcQdJh is given the State ID of AxKxQxJx.

There are a total of 24 combinations that would map to the same State ID.

A hand like 9c3sTh5c is given the State ID of Tx9c5c3x

The State ID needs to indicate that 2 of the cards are clubs, in case the next 3 cards are also clubs.

5hJdKdAh would get the StateID AhKdJd5h

Since we could end up with either a heart flush or a diamond flush.


If we are at State ID AhKdJd5h and the next card is a 6s, the new State ID would be AxKxJx6x5x since a flush is no longer possible.

The 270725 4-card combos map down to 84448

With 5 cards you need 3 of the same suit, otherwise all suits can be ignored.

Using an array of (52,52,52) probably wouldn't be faster.

Multi-dimension arrays tend to involve a multiply or two internally.


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 279Mill a second..... [Re: _D&L_]
      #8574107 - 12/27/06 11:57 PM

Quote:

Made some adjustments to my code, am currently getting 279 Million games per second. Although I still think your algorithm may have greater potential (possibly if I can combine it with mine).





This is what happens when you let lawyers near compilers

I'm not 100% sure about VB, but it's possible that since you commented out where you use 'rank', the optimizer eliminated the code that compute/lookup the value for rank.

You might just be timing "empty" for - next loops.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8574125 - 12/28/06 12:00 AM

gotchya, my ".type" uses the similar type of suit compression that you are doing in your lookup tables. Basically signalling when suits can be ignored.

Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 279Mill a second..... [Re: mykey1961]
      #8574138 - 12/28/06 12:02 AM

nah, i ran it with the checksum counters to make sure i got the same checksum results. Promise my timings are legit. I did comment out the checksums though to get the faster time. I did test empty loops too, i get about 4.2-8.5 biilion a second. Hard to get an exact read, because seems timer increments in .015XXXX intervals

Only commented out the "select case" portion to determine what type of hand was made. but still have an assignment to the variable "rank".


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8574237 - 12/28/06 12:15 AM

Quote:

how big are the LU tables combined under this method?




Just one large table... Exactly 127,499,424 bytes used as 31,874,856 32-bit integers.



The 1st 206 bytes were for error catching. (entering the same card twice) but since they don't catch all of the possibilities, they are pretty much wasted space.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Andrew Prock
enthusiast


Reged: 09/02/02
Posts: 346
Loc: oakland
Re: 279Mill a second..... [Re: _D&L_]
      #8575074 - 12/28/06 02:07 AM

As you can see its speed is primarily due to being able to solve for many hands in one lookup

Yeah,

There's a distinct difference between lumping evals together and a fast generic evaluator. You can certainly do evals rather quickly if you are able to "share results" across cases. Under specific circumstances this is a fairly straightforard process. In general, it's rather difficult. How fast can you get the generic evaluator to go?

- Andrew


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 279Mill a second..... [Re: _D&L_]
      #8575383 - 12/28/06 02:51 AM

Quote:

nah, i ran it with the checksum counters to make sure i got the same checksum results. Promise my timings are legit. I did comment out the checksums though to get the faster time. I did test empty loops too, i get about 4.2-8.5 biilion a second. Hard to get an exact read, because seems timer increments in .015XXXX intervals

Only commented out the "select case" portion to determine what type of hand was made. but still have an assignment to the variable "rank".




This is exactly what I was talking about.

Just becuase you have code that assigns a value to rank, doesn't mean that code will be executed. Optimizers can detect that you aren't using the value assigned to rank, and therefore eliminate the code that does the assigning.

"I did test empty loops too, i get about 4.2-8.5 biilion a second."

Ok I see what's happening... since you "looped" 133 millions times in 1 time unit (approx 0.01573936) you divide 133 million by 0.01573936 giving the 8.5 billion.

That makes the code being eliminated by the optimizer even more likely.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 279Mill a second..... [Re: mykey1961]
      #8575911 - 12/28/06 04:34 AM

I get 4.2 to 8.5 billion with empty loops on an AMD Athlon 64 fx-60 dual 2gigs.

Anyways, when i add back in the commented out checksums i drop from 279 million a second, to 102 million a second. Now is that because rank was being optimized out...hard to say, because right now most calculations are simply a array lookups. Comparisons such as If X > Y only occur when a problem gets flagged as difficult. Adding 9 range comparisons (if x between 0 and 3000) should definately slow it down a bit...so i'm thinking that the 239 million is correct as is. But at any rate, 102 million with time to process checksums included.

I hate select case, and if..then operations in vbasic b/c they are slow....which is why i avoid them whenever i can.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 279Mill a second..... [Re: Andrew Prock]
      #8576328 - 12/28/06 06:31 AM

Quote:



There's a distinct difference between lumping evals together and a fast generic evaluator. You can certainly do evals rather quickly if you are able to "share results" across cases. Under specific circumstances this is a fairly straightforard process. In general, it's rather difficult. How fast can you get the generic evaluator to go?




This is true, my program "cheats." But for pretty much all my purposes I need a cheating algorithm cause its faster. Game theory is all working backwards from specific flops-turns-rivers (FTRs) and unknown strategies or distributions. Thus, rarely will i just want to know the value of one two-card holding for any given flop, but how one distribution compares to another. Even normal applications, such as calculating rankings for multiple players on the same flop can benefit from a cheating algorithm. I mean do we really need to look up hand values if all your opponents have already folded? So cheating algorithms are faster for me.... But your point is understood.

Anyways, if i put everything inside all 7 for loops i get about 65 million games a second. It could probably be optimized a bit more for these conditions. For instance, if five cards on the board, my code will look up a corresponding array for those 5 cards, and then compute values using only the final two cards. No need to do this if that 5 card array is only going to be used once.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Andrew Prock
enthusiast


Reged: 09/02/02
Posts: 346
Loc: oakland
Re: 279Mill a second..... [Re: _D&L_]
      #8577088 - 12/28/06 10:02 AM

This is true, my program "cheats."

I wouldn't call that cheating. But it's an important distinction. If people want to make comparisons, it's useful to agree on what you're comparing. PokerStove can do over 1.5 billion evals/sec by grouping evals together. But that's different from a generic evaluation.

- Andrew


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Lexographical Algorithm [Re: Gullanian]
      #8577166 - 12/28/06 10:15 AM

Tried my luck at a Lexographical version:
This one i'll share all ...b/c largely inspired by posts here. I don't yet have a sorting algorithm for it yet. Gull you said you had a good one? Pre-sorted, its 209 million a second (140 million with checksums), but i suspect most the work will be done sorting it once someone adds that feature. It so happens that the for loops gull wanted us to use for the test...are pre-sorted in ascending order, which is what this lexographic index requires.

Once you include sort time, i would think Mykey's would have to be faster. His puts the cards in order, and reduce by equivalence classes with each lookup, as well as assigning a unique index. If i get time i'll try to add that feature, but sounds tough.

Mykey, if i make those improvements, do u want me to post em or no? Probably wouldn't have thought of em, if not for u, so let me know if you'd prefer me not posting those methods.


Main Function:

Dim index As Long
Dim rank As Integer
Dim c1 As Integer
Dim c2 As Integer
Dim c3 As Integer
Dim c4 As Integer
Dim c5 As Integer
Dim c6 As Integer
Dim c7 As Integer

date1 = Timer
For c1 = 1 To 46
For c2 = c1 + 1 To 47
For c3 = c2 + 1 To 48
For c4 = c3 + 1 To 49
For c5 = c4 + 1 To 50
For c6 = c5 + 1 To 51
For c7 = c6 + 1 To 52

'insert a sort algorithm here c1....through c7

index = A_Sum1(c1) + A_Sum2(c1, c2) + A_Sum3(c2, c3) + A_Sum4(c3, c4) + A_Sum5(c4, c5) + A_Sum6(c5, c6) + (c7 - c6)
rank = Rank_Array(index)

Next c7
Next c6
Next c5
Next c4
Next c3
Next c2
Next c1
date2 = Timer


This is code necessary to generate the arrays: Run it once, store the arrays in a file, and load them.

Open "C:\Program Files\DevStudio\VB\Poker5\Rank_Array.bin" For Binary As #5
Get #5, , Rank_Array

vindex = 51
For i = 1 To 46
P_Sum1(i) = Combin(vindex, 6)
vindex = vindex - 1
Next i

vindex = 50
For i = 2 To 47
P_Sum2(i) = Combin(vindex, 5)
vindex = vindex - 1
Next i

vindex = 49
For i = 3 To 48
P_Sum3(i) = Combin(vindex, 4)
vindex = vindex - 1
Next i

vindex = 48
For i = 4 To 49
P_Sum4(i) = Combin(vindex, 3)
vindex = vindex - 1
Next i

vindex = 47
For i = 5 To 50
P_Sum5(i) = Combin(vindex, 2)
vindex = vindex - 1
Next i

vindex = 46
For i = 6 To 51
P_Sum6(i) = Combin(vindex, 1)
vindex = vindex - 1
Next i


For i = 1 To 46
For j = 1 To i - 1
A_Sum1(i) = A_Sum1(i) + P_Sum1(j)
Next j
Next i

For i = 1 To 46
For j = i + 2 To 47
For k = i + 1 To j - 1
A_Sum2(i, j) = A_Sum2(i, j) + P_Sum2(k)
Next k
Next j
Next i

For i = 2 To 47
For j = i + 2 To 48
For k = i + 1 To j - 1
A_Sum3(i, j) = A_Sum3(i, j) + P_Sum3(k)
Next k
Next j
Next i

For i = 3 To 48
For j = i + 2 To 49
For k = i + 1 To j - 1
A_Sum4(i, j) = A_Sum4(i, j) + P_Sum4(k)
Next k
Next j
Next i

For i = 4 To 49
For j = i + 2 To 50
For k = i + 1 To j - 1
A_Sum5(i, j) = A_Sum5(i, j) + P_Sum5(k)
Next k
Next j
Next i

For i = 5 To 50
For j = i + 2 To 51
For k = i + 1 To j - 1
A_Sum6(i, j) = A_Sum6(i, j) + P_Sum6(k)
Next k
Next j
Next i

//////Need this function to calculate permutations////

Private Function Combin(n As Byte, r As Byte) As Long

Dim t As Byte
Dim u As Double
Dim v As Double

If r > n Then
Combin = 0
Else
If r = n Then
Combin = 1
Else
u = 1
For t = (n - r) + 1 To n
u = u * t
Next t
Rem Permutation(n,r) is calculated as above
v = 1
For t = 1 To r
v = v * t
Next t
Rem r! is calculated as above
Rem r!= 1*...*(r-1)*r
Combin = (u / v)
Rem Combination = n! / ((n-r)! * r!)
Rem Combination(n,r) = Permutation(n,r) / r!
Rem (n!)/(n-r!) = ((n-r)+1)*((n-r)+2)*....*n
End If
End If

End Function


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 279Mill a second..... [Re: Andrew Prock]
      #8577255 - 12/28/06 10:28 AM

I know poker stove pre-flop is all tabled out...extremely fast. But once u specify part of the flop, it starts working off tables (or at least not as fast)...from about 1.7 billion(on my comp) to between 3 to 16 million (with wide player distributions so not as to run into the narrow distribution problem). So i do think these algorithm's do have a lot of merit for partial flop analysis.

Again not knocking p-stove, but i think these algorithms shine in the right conditions.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Andrew Prock
enthusiast


Reged: 09/02/02
Posts: 346
Loc: oakland
Re: 279Mill a second..... [Re: _D&L_]
      #8577424 - 12/28/06 10:53 AM

I know poker stove pre-flop is all tabled out...

I'm not sure what you mean by this, but I'm pretty sure that you don't understand what PokerStove is doing. It doesn't use extensive lookup tables. In fact, it does quite the opposite. If you look at the memory footprint of the program, you'll see that it's right around 6 MB.

But once u specify part of the flop, it starts working off tables (or at least not as fast)...

I dunno, I get it running at over 200 evals/sec with a flop.

So i do think these algorithm's do have a lot of merit for partial flop analysis.

I certainly agree with you on that point. PokerStove certainly isn't a complete solution to the evaluation problem. There is lots of improvement available all over the place.

- Andrew


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 279Mill a second..... [Re: Andrew Prock]
      #8579296 - 12/28/06 02:02 PM

Quote:

This is true, my program "cheats."

I wouldn't call that cheating. But it's an important distinction. If people want to make comparisons, it's useful to agree on what you're comparing. PokerStove can do over 1.5 billion evals/sec by grouping evals together. But that's different from a generic evaluation.

- Andrew




Andrew,

I'm so disapointed.

I set up 10 players with "Any Broadway",
and the Board to "Ac Kc Qc Jc Tc"

What I expected from pokerstove:

Text results appended to pokerstove.txt

2,375,880,867,360,000 games 0.005 secs 475,176,173,472,000,000 games/sec

Board: Ac Kc Qc Jc Tc
Dead:

equity (%) win (%) tie (%)
Hand 1: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 2: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 3: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 4: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 5: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 6: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 7: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 8: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 9: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}
Hand 10: 10.0000 % 00.00% 10.00% { TT+, ATs+, KTs+, QTs+, JTs, ATo+, KTo+, QTo+, JTo}


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 279Mill a second..... [Re: mykey1961]
      #8579441 - 12/28/06 02:15 PM

I'll be honest guys, I'm incredibly dissapointed! I thought I had something near optimum, but I must be missing something obvious! My meak 15 million per second is not much compared to your dozens of millions!

Anyway, incase any of you are interested, this is the best way to sort 7 cards. Please note, this sort algorithm I wrote beats all the classic sorting algorithms (quick sort, merge, insertion, std::Sort). It's basically a precomputed LUT for a pigeonhole sort.

// Represent the hand
sevenCardHand = ((__int64)1 << c1) | ((__int64)1 << c2) | ((__int64)1 << c3) | ((__int64)1 << c4) | ((__int64)1 << c5) | ((__int64)1 << c6) | ((__int64)1 << c7);

// Split 64 bit int into 4 chunks of 16 bits
firstChunk = (sevenCardHand & 0xFFFF);
secondChunk = (sevenCardHand & 0xFFFF0000) >> 16;
thirdChunk = (sevenCardHand & 0xFFFF00000000) >> 32;
fourthChunk = (sevenCardHand & 0xFFFF000000000000) >> 48;

// Reset pointer to sorted hand array
sortedPointer = 0;

// Retrieve records
numToLoop = intToIndexArr_L1[firstChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[firstChunk][anotherCounter];

numToLoop = intToIndexArr_L1[secondChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[secondChunk][anotherCounter] + 16;

numToLoop = intToIndexArr_L1[thirdChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[thirdChunk][anotherCounter] + 32;

numToLoop = intToIndexArr_L1[fourthChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[fourthChunk][anotherCounter] + 48;

Edited by Gullanian (12/28/06 02:18 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 279Mill a second..... [Re: Gullanian]
      #8579453 - 12/28/06 02:17 PM

Where intToIndexArr_L1 holds 8 integers. The first holding the number of indexes to return, and the next 7 the indexes of on bits.

Then the way mine works is by applying an algorithm on the sorted cards into a combination ID to look up in O(1) access time. Algorithm works by summating precomputed choose summations to work out the index.

Also please note for the test I gave, I apply the sorting algorithm even though I don't need to, I could just keep a combination counter incrementing on each iteration and use that for an index lookup, this speeds it up hugely. However, in real world cases the hand needs to be sorted, which is why I have left it in.

Edited by Gullanian (12/28/06 02:21 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 279Mill a second..... [Re: Gullanian]
      #8579605 - 12/28/06 02:30 PM

Just checked out PokerStove as well, incredible. I would love to know how that works, it's baffling for me. 4 cycles per hand!

Edited by Gullanian (12/28/06 02:33 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 279Mill a second..... [Re: Gullanian]
      #8580285 - 12/28/06 03:30 PM

Without sorting and just using combination index it gives me around 150 million per second. The sort is slowing it down a lot.

Post Extras: Print Post   Remind Me!   Notify Moderator  
Andrew Prock
enthusiast


Reged: 09/02/02
Posts: 346
Loc: oakland
Re: 279Mill a second..... [Re: Gullanian]
      #8580561 - 12/28/06 03:54 PM

Yeah, that's a tough one. With only 20 broadway cards, we're five cards short of a feasible scenario.

- Andrew


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 279Mill a second..... [Re: Gullanian]
      #8580564 - 12/28/06 03:54 PM

Quote:

I'll be honest guys, I'm incredibly dissapointed! I thought I had something near optimum, but I must be missing something obvious! My meak 15 million per second is not much compared to your dozens of millions!

Anyway, incase any of you are interested, this is the best way to sort 7 cards. Please note, this sort algorithm I wrote beats all the classic sorting algorithms (quick sort, merge, insertion, std::Sort). It's basically a precomputed LUT for a pigeonhole sort.

// Represent the hand
sevenCardHand = ((__int64)1 << c1) | ((__int64)1 << c2) | ((__int64)1 << c3) | ((__int64)1 << c4) | ((__int64)1 << c5) | ((__int64)1 << c6) | ((__int64)1 << c7);

// Split 64 bit int into 4 chunks of 16 bits
firstChunk = (sevenCardHand & 0xFFFF);
secondChunk = (sevenCardHand & 0xFFFF0000) >> 16;
thirdChunk = (sevenCardHand & 0xFFFF00000000) >> 32;
fourthChunk = (sevenCardHand & 0xFFFF000000000000) >> 48;

// Reset pointer to sorted hand array
sortedPointer = 0;

// Retrieve records
numToLoop = intToIndexArr_L1[firstChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[firstChunk][anotherCounter];

numToLoop = intToIndexArr_L1[secondChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[secondChunk][anotherCounter] + 16;

numToLoop = intToIndexArr_L1[thirdChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[thirdChunk][anotherCounter] + 32;

numToLoop = intToIndexArr_L1[fourthChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[fourthChunk][anotherCounter] + 48;





I'm not fluent in the different flavors of C but here goes.

sevenCardHand = ((__int64)1 << c1) | ((__int64)1 << c2) | ((__int64)1 << c3) | ((__int64)1 << c4) | ((__int64)1 << c5) | ((__int64)1 << c6) | ((__int64)1 << c7);
int n = 1;
int64 bit = 1;
for (int x = 0; x < 52; x++, bit <<= 1) {
if (sevenCardHand && bit) {
switch (n) {
case 1 : c1 = x;
case 2 : c2 = x;
case 3 : c3 = x;
case 4 : c4 = x;
case 5 : c5 = x;
case 6 : c6 = x;
case 7 : c7 = x;
}
n++;
}
}
wouldn't this sort c1..c7 as fast?

This could probably also be switch/cased to death and make a large, but fast solution.


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 279Mill a second..... [Re: Andrew Prock]
      #8580883 - 12/28/06 04:16 PM

Quote:

Yeah, that's a tough one. With only 20 broadway cards, we're five cards short of a feasible scenario.

- Andrew





What a brain fart.. when I selected any broadway it lit up a group 5x5.. My brain observed that 5x5 = 25 and then subtracted the 5 board cards giving 20 cards left over.

I hesitated a moment realizing that Royal holdem only exists for 6 people max for a reason, but I let that line of thought fade.

Ok Redo.

7 people with Any Broadway, and a board of Ac Kc Qc Jc Tc.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 279Mill a second..... [Re: mykey1961]
      #8581548 - 12/28/06 05:04 PM

mykey1961: I've tried looping through each bit individually and dumping it in the array, and it's significantly slower than the solution I proposed. This is a type of pigeon hole sort, and because of the low cards to empty slot ratio looping each bit is too inefficient. The precomputed tables are significantly faster.

Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 279Mill a second..... [Re: Gullanian]
      #8581852 - 12/28/06 05:25 PM

Quote:

I'll be honest guys, I'm incredibly dissapointed! I thought I had something near optimum, but I must be missing something obvious! My meak 15 million per second is not much compared to your dozens of millions!

Anyway, incase any of you are interested, this is the best way to sort 7 cards. Please note, this sort algorithm I wrote beats all the classic sorting algorithms (quick sort, merge, insertion, std::Sort). It's basically a precomputed LUT for a pigeonhole sort.

// Represent the hand
sevenCardHand = ((__int64)1 << c1) | ((__int64)1 << c2) | ((__int64)1 << c3) | ((__int64)1 << c4) | ((__int64)1 << c5) | ((__int64)1 << c6) | ((__int64)1 << c7);

// Split 64 bit int into 4 chunks of 16 bits
firstChunk = (sevenCardHand & 0xFFFF);
secondChunk = (sevenCardHand & 0xFFFF0000) >> 16;
thirdChunk = (sevenCardHand & 0xFFFF00000000) >> 32;
fourthChunk = (sevenCardHand & 0xFFFF000000000000) >> 48;

// Reset pointer to sorted hand array
sortedPointer = 0;

// Retrieve records
numToLoop = intToIndexArr_L1[firstChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[firstChunk][anotherCounter];

numToLoop = intToIndexArr_L1[secondChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[secondChunk][anotherCounter] + 16;

numToLoop = intToIndexArr_L1[thirdChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[thirdChunk][anotherCounter] + 32;

numToLoop = intToIndexArr_L1[fourthChunk][0];
for(anotherCounter = 1; anotherCounter < numToLoop; anotherCounter++)
sortedHand[sortedPointer++] = intToIndexArr_L1[fourthChunk][anotherCounter] + 48;



There seems to be alot of dereferences and increments in the above method (ie: even though pigeonhole sort is O(n) there are alot of constant factors to be added for such a small n...).

Have you compared it to a hard compiled Sorting Network, eg:

Quote:

Network for N=7, using Bose-Nelson Algorithm:
SWAP(1, 2);
SWAP(0, 2);
SWAP(0, 1);
SWAP(3, 4);
SWAP(5, 6);
SWAP(3, 5);
SWAP(4, 6);
SWAP(4, 5);
SWAP(0, 4);
SWAP(0, 3);
SWAP(1, 5);
SWAP(2, 6);
SWAP(2, 5);
SWAP(1, 3);
SWAP(2, 4);
SWAP(2, 3);





Even though it takes 16 compare and swap operations, it can actually be done in 6 parallel steps (ie: it's depth) and a modern compiler should be able to get close to this by utilizing CPU pipelines. Overall, for very small n I've always found them to be the most efficient sort in terms of CPU cycles.

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: A.Nironen]
      #8582014 - 12/28/06 05:39 PM

Quote:


Here is my demo (210 Kb).

http://www.pokerbolide.com/files/handeval7cards.exe

Andrzej Nironen



2.53 secs on my 3.4GHz P4.

My Hand_7_Eval, the source code for which is at
http://www.stevebrecher.com/Software/software.html
takes 3.69 secs when exercised as shown below. It uses some lookup tables whose total size is 254KB.

Code:

//deck is a 52-element array of __int64
//with one bit set per element
int main(void)
{
clock_t timer;

int ix1, ix2, ix3, ix4, ix5, ix6, ix7;
Hand_T board1, board2, board3, board4, board5, board6;
int handTypeSum[9];

Init_Hand_Eval();
InitDeck();
for (ix1 = 0; ix1 < 9; ix1++)
handTypeSum[ix1] = 0;


timer = clock();

for (ix1 = 0; ix1 < 46; ix1++) {
board1 = deck[ix1];
for (ix2 = ix1+1; ix2 < 47; ix2++) {
board2 = board1 | deck[ix2];
for (ix3 = ix2+1; ix3 < 48; ix3++) {
board3 = board2 | deck[ix3];
for (ix4 = ix3+1; ix4 < 49; ix4++) {
board4 = board3 | deck[ix4];
for (ix5 = ix4+1; ix5 < 50; ix5++) {
board5 = board4 | deck[ix5];
for (ix6 = ix5+1; ix6 < 51; ix6++) {
board6 = board5 | deck[ix6];
for (ix7 = ix6+1; ix7 < 52; ix7++) {
handTypeSum[Hand_7_Eval(board6 | deck[ix7]) >> 24]++;
}
}
}
}
}
}
}

timer = clock() - timer;

for (ix1 = 0; ix1 < 9; ix1++)
printf("%d\n", handTypeSum[ix1]);
printf("seconds = %.2f\n", (float)timer/CLOCKS_PER_SEC);
}



Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 279Mill a second..... [Re: jukofyork]
      #8582906 - 12/28/06 06:51 PM

Code:
// Test7SortingNet.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "random.h"
#include <iostream>

using namespace std;

inline unsigned long long RDTSC(void)
{
_asm rdtsc;
}


#define NUM_TESTS 100000
#define NUM_ITERS 100000000


int _tmain(int argc, _TCHAR* argv[])
{

// Create the tests.
char Tests[NUM_TESTS][7];
for (int I=0;I<NUM_TESTS;I++) {
for (int J=0;J<7;J++) {
Tests[I][J]=RandInt(51);
}
}
cout << "Tests created." << endl;

// Read start cycle.
unsigned long long StartTime=RDTSC();

// Run the tests.
for (int I=0;I<NUM_ITERS;I++) {

char* V=Tests[I%NUM_TESTS];

// Using XOR.
//#define SWAP(I,J) {if (V[I]>V[J]) {V[I]^=V[J]; V[J]^=V[I]; V[I]^=V[J];}}

// Using swaps.
int T;
#define SWAP(I,J) {if (V[I]>V[J]) {T=V[I]; V[I]=V[J]; V[J]=T;}}

SWAP(0, 4);
SWAP(1, 5);
SWAP(2, 6);
SWAP(0, 2);
SWAP(1, 3);
SWAP(4, 6);
SWAP(2, 4);
SWAP(3, 5);
SWAP(0, 1);
SWAP(2, 3);
SWAP(4, 5);
SWAP(1, 4);
SWAP(3, 6);
SWAP(1, 2);
SWAP(3, 4);
SWAP(5, 6);

}

//
cout << (double)(RDTSC()-StartTime)/(double)NUM_ITERS << " cycles per sort." << endl;

return 0;
}



Runs at about 70 cycles per sort atm, but the MS VC compiler isn't generating what I want:

Code:
;SWAP(1, 5):
mov cl, BYTE PTR [eax+1]
mov dl, BYTE PTR [eax+5]
cmp cl, dl
jle SHORT $LN15@wmain
movsx ecx, cl
mov BYTE PTR [eax+1], dl
mov BYTE PTR [eax+5], cl


The idea is that you can fit the 7 elements in 2 (or more) registers and then you don't need the "mov xx, BYTE PTR [eax+1]" instructions, but the compiler can't seem to see this and insists on storing as a vector (even if you create a 64 bit structure of 8x8 bytes and use the "register" definition prefix it does this too...).

This messing about using just cl and dl is also causing pipelines to stall which wouldn't happen as much if it were using registers to hold each of the 7 elements.

If I get more time I'll try and get it working as I'm pretty sure it should be possible to do in 30-40 cycles when optimized.

One other thing to try is to see if the XOR swap is any faster for an AMD, as for a P4 it is exactly the same even though it compiled down to an extra instruction per SWAP().

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 279Mill a second..... [Re: Gullanian]
      #8583421 - 12/28/06 07:49 PM

that won't run in VB will it darn.

Post Extras: Print Post   Remind Me!   Notify Moderator  
A.Nironen
member


Reged: 10/29/06
Posts: 118
Re: 279Mill a second..... [Re: _D&L_]
      #8586155 - 12/29/06 01:07 AM

Guys you discuss different "weight categories". It's obvious if not to care about RAM used a huge lookup table works best. And it's much harder to create a fast algorithm using any reasonable amount of RAM.

Andrzej Nironen


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Which is the best packed/unpacked so far? [Re: mykey1961]
      #8586847 - 12/29/06 03:23 AM

Quote:

Quote:

Quote:


Code:

133784560 hands
1.188 secs
112613302 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584





This was on an AMD 3000+ with 1 gig memory running Win2k

This computer produced

133784560 hands
5.156 secs
25947355 hands/sec
hc 23294460
pr 58627800
tp 31433400
th 6461620
st 6180020
fl 4047644
fh 3473184
fr 224848
sf 41584

using Andrzej Nironen's "Hand Evaluator Speed Demo"



Very impressive! (I think you have the results mixed up though - 112.5mil when doing extra work of incrementing the hand type counts, 50mil when not... )

Have you considered posting this method on the pokersource yahoo group?

Juk

EDIT: Sorry, I just noticed you print the hand type counts in both procedures... BTW: How fast does it run without doing this (ie: just assigning an integer the hand evaluation rather than incrementing totals[]?)




0.953 secs
140382574 hands/sec

However I'm doubting the granularity of the timing process used.

I'll switch to the RDTSC method and repost relative values.

Using RDTSC as a timer

133784560 hands
2.640 secs
50675964 hands/sec

becomes

133784560 hands
5752663748 cycles
42.999 cycles/hand

133784560 hands
1.188 secs
112613302 hands/sec

becomes

133784560 hands
2645152642 cycles
19.772 cycles/hand

and

133784560 hands
0.953 secs
140382574 hands/sec

becomes

133784560 hands
2077269818 cycles
15.527 cycles/hand




I've kinda lost track of this thread now, but so far (out of all the algorithms listed here), which are the current best algorithms (in cycles per second & assuming no "cheating" - ie: algorithm speed measured against a 7-card hand selected uniformly randomly from all possible 7-card hands) for:

a) A "packed" 64bit integer representation.
b) A unordered 7-integer representation.

Most of my work has used a packed representation in the past, but after seeing mykey1961's nested lookup idea running at 15 cycles per hand, I am seriously considering moving everything over to an unpacked representation to take advantage of this (~20mil hands/sec -> ~200mil hands/sec can't be ignored!).

Overall this thread has opened my eyes; I'd always just taken foregranted that the pokersource lib was highly optimized and assumed only marginal speed improvements would be possible in the future...

Great thread - Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: Which is the best packed/unpacked so far? [Re: jukofyork]
      #8588732 - 12/29/06 10:12 AM

Jukofyork: Thank you for the sorting network idea! Never seen them before, very interesting! It sped my code up from 15million per second to 35 million per second, I think I can squeeze a little more out of it as well!

Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: Which is the best packed/unpacked so far? [Re: Gullanian]
      #8588738 - 12/29/06 10:13 AM

D&L, you can use the sorting network idea as well, just copy the defines into the SWAP statment.

Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8588772 - 12/29/06 10:17 AM

Hey mykey question on your evaluator

Quote:


Totals[Table[Table[Table[Table[Table[Table[Table[c1+52]+c2]+c3]+c4]+c5]+c6]+c7] shr 20]);




not good with C++, but what are you representing hand strength as? a digit between 1 and 9? I notice your counters are highly efficient, because you can just use the hand rank as an index to your array. But to do that, is your hand rank just a number 1 through 9? as opposed to say, defining wich "2 pair" beat another "2 pair." Perhaps i'm just misunderstand, cause I don't know c++

P.s. how fast was your mapped array when incrementing counters, and when not? i think you flipped your times around..reporting a higher time with counters?

At anyrate, i'm 2/3 of the way done making your mapped table array for comparison purposes. Mapping one 4MB array to another takes like trillions of calculations (maybe you had some shortcut)...my computers grinding away.... Only one more array to map.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8588862 - 12/29/06 10:28 AM

Just realised however, in the test given the cards are all sorted, therfore it would run faster. I'm doing a real world test now with unsorted hands to test performance.

Edit: Tests sucesful! The precomputed sorting network works 2-3x faster. Thanks!

Edited by Gullanian (12/29/06 10:31 AM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8588933 - 12/29/06 10:39 AM

Heres a better test for our evaluators:

holeCard1 = 0;
holeCard2 = 1;

for(opCard1 = 0; opCard1 < 52; opCard1++)
{
if(opCard1 == holeCard1 || opCard1 == holeCard2)
continue;

cout<<".";
cout.flush();

for(opCard2 = opCard1 + 1; opCard2 < 52; opCard2++)
{

if(opCard2 == holeCard1 || opCard2 == holeCard2)
continue;

// Loop through all the deck cards
for(c1 = 0; c1 < 52; c1++)
{
if(c1 == holeCard1 || c1 == holeCard2 || c1 == opCard1 || c1 == opCard2)
continue;

for(c2 = c1 + 1; c2 < 52; c2++)
{
if(c2 == holeCard1 || c2 == holeCard2 || c2 == opCard1 || c2 == opCard2)
continue;

for(c3 = c2 + 1; c3 < 52; c3++)
{
if(c3 == holeCard1 || c3 == holeCard2 || c3 == opCard1 || c3 == opCard2)
continue;

for(c4 = c3 + 1; c4 < 52; c4++)
{
if(c4 == holeCard1 || c4 == holeCard2 || c4 == opCard1 || c4 == opCard2)
continue;

for(c5 = c4 + 1; c5 < 52; c5++)
{
if(c5 == holeCard1 || c5 == holeCard2 || c5 == opCard1 || c5 == opCard2)
continue;

WORK OUT OUR HANDS STRENGTH (holeCard1, holeCard2, C1...C5)
WORK OUT OPPONENTS HANDS STRENGTH (opCard1, opCard2, C1...C5)

// Increment totals counters
if(ourHandStrength < oppHandStrength)
totalWins++;
else if (ourHandStrength > oppHandStrength)
totalLosses++;
else
totalDraws++;
}}}}}}}

cout<<endl<<endl<<"Wins : "<<totalWins<<" ("<<((float)totalWins/2097572400)*100<<"%)"<<endl;
cout<<"Draws : "<<totalDraws<<" ("<<((float)totalDraws/2097572400)*100<<"%)"<<endl;
cout<<"Losses : "<<totalLosses<<" ("<<((float)totalLosses/2097572400)*100<<"%)"<<endl<<endl;
cout<<"Total Combinations : "<<totalWins + totalDraws + totalLosses<<endl;
cout<<"Time MS : "<<(endTime-startTime)<<endl;
cout<<"Time Secs : "<<(endTime-startTime)/1000<<endl;
cout<<"Time Mins : "<<((endTime-startTime)/1000)/60<<endl;
cout<<"Iters per second : "<<(2097572400/(endTime-startTime))*1000<<endl<<endl;

My evaluator can run through the 2.1 billion games in 2 minutes. Pretty fast in my opinion, but from the numbers other people are getting im sure it can be beaten flat on it's face!

Edited by Gullanian (12/29/06 10:40 AM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8589399 - 12/29/06 11:50 AM

Quote:

Heres a better test for our evaluators:



(For a constant pair of hole cards, evaluate all combos of one opponent and board). Why is this better? Also, while it won't make a significant difference, one might as well avoid unnecessary loop executions and conditions (untested modification):
Code:

holeCard1 = 0;
holeCard2 = 1;

for(opCard1 = holeCard2 + 1; opCard1 < 51; opCard1++)
{

cout<<".";
cout.flush();

for(opCard2 = opCard1 + 1; opCard2 < 52; opCard2++)
{

// Loop through all the deck cards > holecard2
for(c1 = holeCard2 + 1; c1 < 48; c1++)
{
if(c1 == opCard1 || c1 == opCard2)
continue;

for(c2 = c1 + 1; c2 < 49; c2++)
{
if(c2 == opCard1 || c2 == opCard2)
continue;

for(c3 = c2 + 1; c3 < 50; c3++)
{
if(c3 == opCard1 || c3 == opCard2)
continue;

for(c4 = c3 + 1; c4 < 51; c4++)
{
if(c4 == opCard1 || c4 == opCard2)
continue;

for(c5 = c4 + 1; c5 < 52; c5++)
{
if(c5 == opCard1 || c5 == opCard2)
continue;

// evaluate and tally here
}}}}}}}



Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #8589640 - 12/29/06 12:19 PM

I believe it to be a better test bed because with the other test, the cards are presented ordered already, which means that some sort algorithms by nature will out perform others. In this scenario, the two sets of 7 cards are not always ordered (but partially ordered). I think it's a fairer test.

Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8589767 - 12/29/06 12:35 PM

Quote:

Hey mykey question on your evaluator

Quote:


Totals[Table[Table[Table[Table[Table[Table[Table[c1+52]+c2]+c3]+c4]+c5]+c6]+c7] shr 20]);




not good with C++, but what are you representing hand strength as? a digit between 1 and 9? I notice your counters are highly efficient, because you can just use the hand rank as an index to your array. But to do that, is your hand rank just a number 1 through 9? as opposed to say, defining wich "2 pair" beat another "2 pair." Perhaps i'm just misunderstand, cause I don't know c++

P.s. how fast was your mapped array when incrementing counters, and when not? i think you flipped your times around..reporting a higher time with counters?

At anyrate, i'm 2/3 of the way done making your mapped table array for comparison purposes. Mapping one 4MB array to another takes like trillions of calculations (maybe you had some shortcut)...my computers grinding away.... Only one more array to map.





I'm not using the rank as an index.

Think of my 1 big table as broken down into 7 smaller tables.

I start at state 0, where I have no knowledge about the cards to be ranked.

When I get my first card to be ranked, I use my current state, and the 1st card as a vector to my new state.

State_1 = Table0[State_0,Card_1]
State_2 = Table1[State_1,Card_2]
State_3 = Table2[State_2,Card_3]
State_4 = Table3[State_3,Card_4]
State_5 = Table4[State_4,Card_5]
State_6 = Table5[State_5,Card_6]

This last step is a little different

Rank = Table6[State_6,Card_7]


The values in Table6 are not states like in Table0 thru Table5, they are actual rankings.

My ranking is a 24 bit value. it's best viewed in Hex format.

If my 7 cards are

AcQh3c8hTcTd5d = 0x2AEC80

A = Ten
B = Jack
C = Queen
D = King
E = Ace

6s5s4h3sJs2dTc = 0x465432


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #8589814 - 12/29/06 12:41 PM

Quote:

Quote:

Heres a better test for our evaluators:



(For a constant pair of hole cards, evaluate all combos of one opponent and board). Why is this better? Also, while it won't make a significant difference, one might as well avoid unnecessary loop executions and conditions (untested modification):
Code:

holeCard1 = 0;
holeCard2 = 1;

for(opCard1 = holeCard2 + 1; opCard1 < 51; opCard1++)
{

cout<<".";
cout.flush();

for(opCard2 = opCard1 + 1; opCard2 < 52; opCard2++)
{

// Loop through all the deck cards > holecard2
for(c1 = holeCard2 + 1; c1 < 48; c1++)
{
if(c1 == opCard1 || c1 == opCard2)
continue;

for(c2 = c1 + 1; c2 < 49; c2++)
{
if(c2 == opCard1 || c2 == opCard2)
continue;

for(c3 = c2 + 1; c3 < 50; c3++)
{
if(c3 == opCard1 || c3 == opCard2)
continue;

for(c4 = c3 + 1; c4 < 51; c4++)
{
if(c4 == opCard1 || c4 == opCard2)
continue;

for(c5 = c4 + 1; c5 < 52; c5++)
{
if(c5 == opCard1 || c5 == opCard2)
continue;

// evaluate and tally here
}}}}}}}






I think the choice of

holeCard1 = 0;
holeCard2 = 1;

was unfortunate.

If it had been

holeCard1 = x;
holeCard2 = y;

where x and y are defines elsewhere then the loop collision checking is required.

even

holeCard1 = 1;
holeCard2 = 10;

would require loop checking.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8590262 - 12/29/06 01:23 PM

I just gave the primitive loop as a base to work on, I've just managed to speed mine up a lot by creating arrays holding values to loop through, eliminating the need of all the ORs.

Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8590677 - 12/29/06 01:59 PM

Quote:

I just gave the primitive loop as a base to work on, I've just managed to speed mine up a lot by creating arrays holding values to loop through, eliminating the need of all the ORs.




I think an updating double linked list of unused cards might allow for faster looping.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8591270 - 12/29/06 02:52 PM

I've optimised my code a lot more, it's looping all 2.1 billion games in around 103-106 seconds.

Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: Which is the best packed/unpacked so far? [Re: Gullanian]
      #8592709 - 12/29/06 05:17 PM

Quote:

Jukofyork: Thank you for the sorting network idea! Never seen them before, very interesting! It sped my code up from 15million per second to 35 million per second, I think I can squeeze a little more out of it as well!



Hey, np and glad it was helpful. I think this is one of the most interesting threads we've ever had running here!

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8592793 - 12/29/06 05:25 PM

MyKey,

Am I right in saying that your procedure doesn't need a sort for the input cards. If you "train" your array with all the permutations, it would give the correct handrank no matter what order the cards come in (True?).

Another thought is to reduce the size of the array by half by using the 7462 (i.e. unsigned short) possible hand ranks (ref cactus key). Most possibilities for one hand rank group (pairs) is 2860 (12 possible pairs + Combin(12,3) for the other 3 cards) for the bottom 12 bits, while using the top 4 bits for the 9 main hand ranks (high card, pairs, 2pair...). A sixty meg array feels twice as good as a 120 meg one.

Thank You for the interesting posts, I am definitely watching with interest.

Thank You,
Ray


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: RayW]
      #8593417 - 12/29/06 06:28 PM

Quote:

MyKey,

Am I right in saying that your procedure doesn't need a sort for the input cards. If you "train" your array with all the permutations, it would give the correct handrank no matter what order the cards come in (True?).




This is correct.

Quote:

Another thought is to reduce the size of the array by half by using the 7462 (i.e. unsigned short) possible hand ranks (ref cactus key). Most possibilities for one hand rank group (pairs) is 2860 (12 possible pairs + Combin(12,3) for the other 3 cards) for the bottom 12 bits, while using the top 4 bits for the 9 main hand ranks (high card, pairs, 2pair...). A sixty meg array feels twice as good as a 120 meg one.

Thank You for the interesting posts, I am definitely watching with interest.

Thank You,
Ray




I lumped everything into one big table for ease of use.

If I used a different table for each # of cards, and sized them to the most efficent 1-byte/2-byte/4-byte, usage would drop from 120Mb down to 90Mb.

I'm not sure that would be the most efficient in terms of speed though.

There may be some time lost when the CPU loads a 16 bit value from an address that isn't a multiple of 4.

Since I was using just 32 bit values, everything was aligned "properly".


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8593587 - 12/29/06 06:44 PM

Quote:

I believe it to be a better test bed because with the other test, the cards are presented ordered already, which means that some sort algorithms by nature will out perform others. In this scenario, the two sets of 7 cards are not always ordered (but partially ordered). I think it's a fairer test.




This test wouldn't change my times significantly.

int ps1, ps2, ps3, ps4, ps5, ps6, pRank;
int os1, os2, os3, os4, os5, os6, oRank;


holeCard1 = 0;
holeCard2 = 1;

ps1 = Table[holeCard1];
ps2 = Table[ps1+holeCard2];
for(opCard1 = holeCard2 + 1; opCard1 < 51; pCard1++){
cout<<".";cout.flush();
os1 = Table[opCard1];
for(opCard2 = opCard1 + 1; opCard2 < 52; opCard2++)
{// Loop through all the deck cards > holecard2
os2 = Table[os1+opCard1];
for(c1 = holeCard2 + 1; c1 < 48; c1++){
if(c1 == opCard1 || c1 == opCard2)continue;
ps3 = Table[ps2+c1]; os3 = Table[os2+c1];
for(c2 = c1 + 1; c2 < 49; c2++){
if(c2 == opCard1 || c2 == opCard2)continue;
ps4 = Table[ps3+c2]; os4 = Table[os3+c2];
for(c3 = c2 + 1; c3 < 50; c3++){
if(c3 == opCard1 || c3 == opCard2)continue;
ps5 = Table[ps4+c3]; os5 = Table[os4+c3];
for(c4 = c3 + 1; c4 < 51; c4++){
if(c4 == opCard1 || c4 == opCard2)continue;
ps6 = Table[ps5+c4]; os6 = Table[os5+c4];
for(c5 = c4 + 1; c5 < 52; c5++){
if(c5 == opCard1 || c5 == opCard2)continue;
pRank = Table[ps6+c5]; oRank = Table[os6+c5];
// evaluate and tally here}}}}}}}
compare pRank to oRank


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8595550 - 12/29/06 10:11 PM

I modified a version that becomes a large quantity of pointers.

Rank = State0^[c1]^[c2]^[c3]^[c4]^[c5]^[c6]^[c7]

Ain't that pretty?

It also is just a bit faster.

Now a "State" is actually a pointer, to an array of pointers.. etc ... Lions, Tigers, and Bears... oh my.


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8598374 - 12/30/06 05:09 AM

Quote:

I modified a version that becomes a large quantity of pointers.

Rank = State0^[c1]^[c2]^[c3]^[c4]^[c5]^[c6]^[c7]

Ain't that pretty?

It also is just a bit faster.

Now a "State" is actually a pointer, to an array of pointers.. etc ... Lions, Tigers, and Bears... oh my.




Interesting tweak,

How are you able to save your trained array? You were saying that it is faster... How long does it take for the thread's test now?

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
Mogobu The Fool
addict


Reged: 09/10/05
Posts: 617
Loc: On teh internets.
Re: Which is the best packed/unpacked so far? [Re: jukofyork]
      #8599609 - 12/30/06 11:22 AM

Agreed, Juk... most interesting thread I've seen for a long time. I've been out of poker and coding for months now - have a Corp job in Manhattan again - but I decided to peek back here, and found the most interesting thread by looking for where you've been commenting!

Some of the techniques here have been making my head flex, which feels good... don't really understand all of them at first read, but thanks, all, for contributing.

Wanted to throw one more wrench in the works for you all; it's what I was planning to look into next time I was revisiting my hand eval code, which will probably never happen now. (Actually, I had dropped my hand-eval code, which was an optimized version of pokeval ported to VB, and started using Keith Rule's C# version, which you can find at codeproject.com.)

Anyway, as soemone noted, there are very few actual hand rankings in poker, because so many hands reduce to the same ranking. In 5-card poker, there are 7462 different rankings. In 7-card poker, there are only 4824 of them. (Yeah, fewer hands with 7 cards. Think of it this way - give yourself the lowest ranking 5-card hand. Now add two cards without making a better hand. Can't. Therefore, a whole set of 5-card hands are impossible in 7-card poker.)

Anyway, since there are only 4824 results, it seems that mapping the 52!/45! different random poker hands to the 4824 results is a natural fit for a perfect hash problem.

There are a couple of well-described perfect hash generators out there (see, for example, http://burtleburtle.net/bob/hash/perfect.html). Anybody take a stab at playing with them for the poker evaluation problem? Run time on finding the perfect hash might be pretty intensive, but once a solution is found, you've got a VERY fast evaluator. You don't even need a minimal perfect hash - a fast hash which maps to a table with 64k entries will still be plenty small in memory, and will potentially allow a great many more solutions.


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: Which is the best packed/unpacked so far? [Re: Mogobu The Fool]
      #8600982 - 12/30/06 02:21 PM

Quote:

Agreed, Juk... most interesting thread I've seen for a long time. I've been out of poker and coding for months now - have a Corp job in Manhattan again - but I decided to peek back here, and found the most interesting thread by looking for where you've been commenting!

Some of the techniques here have been making my head flex, which feels good... don't really understand all of them at first read, but thanks, all, for contributing.

Wanted to throw one more wrench in the works for you all; it's what I was planning to look into next time I was revisiting my hand eval code, which will probably never happen now. (Actually, I had dropped my hand-eval code, which was an optimized version of pokeval ported to VB, and started using Keith Rule's C# version, which you can find at codeproject.com.)

Anyway, as soemone noted, there are very few actual hand rankings in poker, because so many hands reduce to the same ranking. In 5-card poker, there are 7462 different rankings. In 7-card poker, there are only 4824 of them. (Yeah, fewer hands with 7 cards. Think of it this way - give yourself the lowest ranking 5-card hand. Now add two cards without making a better hand. Can't. Therefore, a whole set of 5-card hands are impossible in 7-card poker.)

Anyway, since there are only 4824 results, it seems that mapping the 52!/45! different random poker hands to the 4824 results is a natural fit for a perfect hash problem.

There are a couple of well-described perfect hash generators out there (see, for example, http://burtleburtle.net/bob/hash/perfect.html). Anybody take a stab at playing with them for the poker evaluation problem? Run time on finding the perfect hash might be pretty intensive, but once a solution is found, you've got a VERY fast evaluator. You don't even need a minimal perfect hash - a fast hash which maps to a table with 64k entries will still be plenty small in memory, and will potentially allow a great many more solutions.





A perfect hash is not what you want.

Since there are 133784560 combos, a perfect hash would produce a hash in the range of 0..133784559 or larger.


If you could devise a hash that wasn't perfect so it overlapped just the right combo of cards, that's still not enough, you also need the value of the hash is in the right order.

If you notice in creating the hash, for each card, there would be a lookup, shifts, xor's etc. That's gotta be slower than one lookup per card.


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: RayW]
      #8601366 - 12/30/06 03:05 PM

Quote:

MyKey,

Am I right in saying that your procedure doesn't need a sort for the input cards. If you "train" your array with all the permutations, it would give the correct handrank no matter what order the cards come in (True?).

Another thought is to reduce the size of the array by half by using the 7462 (i.e. unsigned short) possible hand ranks (ref cactus key). Most possibilities for one hand rank group (pairs) is 2860 (12 possible pairs + Combin(12,3) for the other 3 cards) for the bottom 12 bits, while using the top 4 bits for the 9 main hand ranks (high card, pairs, 2pair...). A sixty meg array feels twice as good as a 120 meg one.

Thank You for the interesting posts, I am definitely watching with interest.

Thank You,
Ray





Since D&L talked about trillions of calculations, and I've been asked how I generate the tables. Here is a partial example of the process I chose to use.

This calculates a StateID for any given 4 card combo. It doesn't have to be lightneing fast, because this is only used to build the tables, not while they are in use.

function Make4(c1, c2, c3, c4 : integer) : int64;
var
ID : int64;
t : integer;
sc : array[1..4] of integer;
begin

// In my "world" 2s = 0, 3s = 1,.....Kc = 50, Ac = 51.
// However in the ID process I prefered that
// 2s = 0x21, 3s = 0x31,.... Kc = 0xD4, Ac = 0xE4
// This allows me to sort in Rank then Suit order

c1 := (((c1 mod 13)+2) shl 4) + (c1 div 13)+1;
c2 := (((c2 mod 13)+2) shl 4) + (c2 div 13)+1;
c3 := (((c3 mod 13)+2) shl 4) + (c3 div 13)+1;
c4 := (((c4 mod 13)+2) shl 4) + (c4 div 13)+1;

// sc[1..4] is a counter of suits (how many spades do I have, etc)

fillchar(sc,sizeof(sc),0);
inc(sc[c1 and $F]);
inc(sc[c2 and $F]);
inc(sc[c3 and $F]);
inc(sc[c4 and $F]);

// if we don't have at least 2 cards of the same suit, we make this card suit 0.

if sc[c1 and $F] < 2 then c1 := c1 and $F0;
if sc[c2 and $F] < 2 then c2 := c2 and $F0;
if sc[c3 and $F] < 2 then c3 := c3 and $F0;
if sc[c4 and $F] < 2 then c4 := c4 and $F0;

// my Q&D sort in Rank then Suit order

repeat
if c1 < c2 then begin t := c1; c1 := c2; c2 := t; end;
if c2 < c3 then begin t := c2; c2 := c3; c3 := t; end;
if c3 < c4 then begin t := c3; c3 := c4; c4 := t; end;
until (c1 >= c2) and (c2 >= c3) and (c3 >= c4);

// the resulting ID is a 64 bit value with each card represented by 8 bits.

ID := c1;
ID := (ID shl 8) + c2;
ID := (ID shl 8) + c3;
ID := (ID shl 8) + c4;
Make4 := ID;
end;

// I cycle thru the 270725 combinations of 4 card hands,
// and save all of the ID's to an array. I sort the array,
// then remove all duplicate ID's. This gives me an array
// of 84448 4-card IDs in sorted order. The value of a
// state, is the index into that array to holds a matching
// StateID.

// EL0 is a pointer to an array of integers
// EL0^[c1] = state1;
// EL1 thru EL6 is an pointer to an array of arrays of integers
// EL1^[state1][c2] = state2;


for c1 := 0 to 48 do
for c2 := c1+1 to 49 do
for c3 := c2+1 to 50 do
for c4 := c3+1 to 51 do
begin

// Check to see if this combo has already been assigned

s4 := EL3^[EL2^[EL1^[EL0^[c1]][c2]][c3]][c4];
if s4 = -1 then
begin

StateID := Make4(c1,c2,c3,c4);

// Binary search for the stateID in the list of StateID's
// keeping the index where we find it

hi := MaxState4; lo := -1; d := -1;
while d <> 0 do
begin
s4 := (hi + lo) shr 1;
d := StateID - StateList4^[s4];
if d < 0 then hi := s4;
if d > 0 then lo := s4;
end;
end;

// We don't need to do all the permutations of 4 cards

EL3^[EL2^[EL1^[EL0^[c1]][c2]][c3]][c4] := s4;
EL3^[EL2^[EL1^[EL0^[c1]][c2]][c4]][c3] := s4;
EL3^[EL2^[EL1^[EL0^[c1]][c4]][c3]][c2] := s4;
EL3^[EL2^[EL1^[EL0^[c4]][c2]][c3]][c1] := s4;
end;
assignfile(f,'Eval3.dat');
rewrite(f,sizeof(TEvalVector));
blockwrite(f,EL3^,MaxState3);
closefile(f);

Edited by mykey1961 (12/30/06 03:12 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8606005 - 12/30/06 11:37 PM

My evaluator uses a perfect minimal hashing technique, and I can get 45 million evaluations per second, which comes down to about 45 cycles per hand.

Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8606059 - 12/30/06 11:44 PM

I would try using one of the no-sorting algorithms. My "259 mill" a sec algorithm (which i would like to partially retract mykey was right part of the code was optimized out in that test - its more like 145 mill a second with a trivial checksum counter (if rank <> 0 then count ++) - anyways, that algorithm doesn't sort. It rights values in multiple locations in arrays, because flush space (4^7) and number space (13^7) are small tables, and combines them to figure out straight flushes, and flush values. In fact 13^7 and 4^7 are overstatements, because it also has its own version of mapped arrays, and reduction by equivalent or impossible classe...just one mapped array though from 5 card to 7 card (thats my index + subindex).

I'm writing a 52 card mapped array for comparison purposes.
The tables will be much bigger, so we see if size of LU tables slow things down when there is a large difference in size.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Phil153
Notable Twit


Reged: 10/18/05
Posts: 4905
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8606659 - 12/31/06 01:13 AM

Quote:

My evaluator uses a perfect minimal hashing technique, and I can get 45 million evaluations per second, which comes down to about 45 cycles per hand.



Sounds like your speed is equivalent to D&L's. He's getting between 4.5 and 8 (lol - just put it in a for loop you dummy and get an exact number, you don't have divide by 0.015) billion cycles per second on his machine. Which equates to about 200 million hands/sec under your code.


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: Phil153]
      #8606820 - 12/31/06 01:36 AM

Quote:

Quote:

My evaluator uses a perfect minimal hashing technique, and I can get 45 million evaluations per second, which comes down to about 45 cycles per hand.



Sounds like your speed is equivalent to D&L's. He's getting between 4.5 and 8 (lol - just put it in a for loop you dummy and get an exact number, you don't have divide by 0.015) billion cycles per second on his machine. Which equates to about 200 million hands/sec under your code.





He's a lawyer, he's expected to make those kinds of mistakes. I'll have to send him a bill for $600 to cover the 3 minutes it took to point that out.


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8608162 - 12/31/06 05:19 AM

Quote:

Quote:

I just gave the primitive loop as a base to work on, I've just managed to speed mine up a lot by creating arrays holding values to loop through, eliminating the need of all the ORs.




I think an updating double linked list of unused cards might allow for faster looping.




One fairly fast and easy Way to ignore the used cards:
int cardused[52]; // keep track of cards used

memset(cardused, 0, sizeof(cardused)); // init first

as you use a card
if (cardused[card]) continue; // forget this one...
cardused[card] = 1; // remember it is used

when completed using a card
cardused[card] = 0; // mark this card as available

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
Mogobu The Fool
addict


Reged: 09/10/05
Posts: 617
Loc: On teh internets.
Re: Which is the best packed/unpacked so far? [Re: mykey1961]
      #8610261 - 12/31/06 01:43 PM

Quote:

Quote:

Anyway, as soemone noted, there are very few actual hand rankings in poker, because so many hands reduce to the same ranking. In 5-card poker, there are 7462 different rankings. In 7-card poker, there are only 4824 of them.




A perfect hash is not what you want.

Since there are 133784560 combos, a perfect hash would produce a hash in the range of 0..133784559 or larger.

If you could devise a hash that wasn't perfect so it overlapped just the right combo of cards, that's still not enough, you also need the value of the hash is in the right order.




I'm using the hash to index a lookup table, so order has no impact here.

Since the solution set has less than 5k different values, you can reduce to a hash table which is far smaller than your source set. It's simply a matter of refining the code used to find the perfect hash such that hash collisions are not considered collisions if the value stored in the hash table the same for both inputs. In other words, if two equivalent hands hash to the same element in the hash table, it is not a collision.

Quote:


If you notice in creating the hash, for each card, there would be a lookup, shifts, xor's etc. That's gotta be slower than one lookup per card.




Actually, it would be against a compressed format where the entire card set is represented in a single 52-bit value. So there would be one "round" of bit manipulations per evaluation. In theory, it could also accomodate 5, 6, and 7 card hands in one function - but then you would need the larger result set of all 7462 possible hands (no big difference, but an efficient function would be harder to find.)


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: Which is the best packed/unpacked so far? [Re: Mogobu The Fool]
      #8627285 - 01/02/07 07:24 AM

Quote:

Agreed, Juk... most interesting thread I've seen for a long time. I've been out of poker and coding for months now - have a Corp job in Manhattan again - but I decided to peek back here, and found the most interesting thread by looking for where you've been commenting!



Hey, glad to hear the new job is going well. I've also been having been having a break from coding (and poker) over the last month or so, but as always have a whole lot of new ideas built up ready for 2007.

Quote:

Some of the techniques here have been making my head flex, which feels good... don't really understand all of them at first read, but thanks, all, for contributing.

Wanted to throw one more wrench in the works for you all; it's what I was planning to look into next time I was revisiting my hand eval code, which will probably never happen now. (Actually, I had dropped my hand-eval code, which was an optimized version of pokeval ported to VB, and started using Keith Rule's C# version, which you can find at codeproject.com.)

Anyway, as soemone noted, there are very few actual hand rankings in poker, because so many hands reduce to the same ranking. In 5-card poker, there are 7462 different rankings. In 7-card poker, there are only 4824 of them. (Yeah, fewer hands with 7 cards. Think of it this way - give yourself the lowest ranking 5-card hand. Now add two cards without making a better hand. Can't. Therefore, a whole set of 5-card hands are impossible in 7-card poker.)

Anyway, since there are only 4824 results, it seems that mapping the 52!/45! different random poker hands to the 4824 results is a natural fit for a perfect hash problem.

There are a couple of well-described perfect hash generators out there (see, for example, http://burtleburtle.net/bob/hash/perfect.html). Anybody take a stab at playing with them for the poker evaluation problem? Run time on finding the perfect hash might be pretty intensive, but once a solution is found, you've got a VERY fast evaluator. You don't even need a minimal perfect hash - a fast hash which maps to a table with 64k entries will still be plenty small in memory, and will potentially allow a great many more solutions.



I remember you mentioning this along time ago, but up until recently I never really put much thought into improving hand evaluators; expecting only minimal performance increases possible in the evaluators themselves (how wrong I was!!!).

Anyway, off back to nurse the hangover... Happy new year! Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: Which is the best packed/unpacked so far? [Re: jukofyork]
      #8627376 - 01/02/07 07:45 AM

Nothing to do with a "perfect hash" (or hand evaluators), but just wondered if anybody knew of a better/faster way to do the following.

I use this function to index into a hashtable (of size "NumEllementsPow2") when passed a 64bit key.

Code:
int FoldKey(unsigned long long N,int NumEllementsPow2)
{ // Get the required (index) key from the large 64-bit key.
// This uses the folding method, as the low/high bits of the random number
// generator ar poor on their own.
// NOTE: Do NOT simply and out the requires bit or use modulous as this will
// give a very poor distribution of indexes using the current scheme.

int IndexKey=0;
int I;

// Make sure all bits are used for the index to get a good distribution.
for (I=64-NumEllementsPow2;I>0;I-=NumEllementsPow2)
IndexKey^=(N>>I);
if (I!=0)
IndexKey^=(N&((1<<(I+NumEllementsPow2+1))-1));

// Take just the bits we need.
return IndexKey&((1<<NumEllementsPow2)-1);

} // End FoldKey.


In theory I wish I could just use the much faster version (which fails to distribute the hash entries properly...):

Code:
int GetKey(unsigned long long N,int NumEllementsPow2)
{ // Get the required (index) key from the large 64-bit key.
return N&((1<<NumEllementsPow2)-1);
}



Since I only have to make the hash codes once, I could use a much more computationally expensive random number generator than I currently use, but so far I can't seem to find one which lets me get away with the code above, always requiring the slow FoldKey() function every time I want to map from 64bit key to the hash table index itself...

Does anybody know of a RNG which will let me just take the bottom N bits and still give a good distribution, and/or a faster method by which I could fold the keys?

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: Which is the best packed/unpacked so far? [Re: jukofyork]
      #8628187 - 01/02/07 10:41 AM

Quote:

Does anybody know of a RNG which will let me just take the bottom N bits and still give a good distribution, and/or a faster method by which I could fold the keys?

Juk



George Marsaglia, who devoted his professional (academic) life to RNGs, posted the following articles to the usenet sci.math group a few years ago. I think you might find them of interest:

http://groups.google.com/group/sci.math/msg/9959175f66dd138f?hl=en

http://groups.google.com/group/sci.math/msg/2a5329cdf4d574a5?hl=en


Post Extras: Print Post   Remind Me!   Notify Moderator  
TheCardGeek
journeyman


Reged: 07/09/06
Posts: 54
Re: 7 Card Hand Evaluators [Re: _D&L_]
      #8638423 - 01/03/07 02:08 AM

I don't know if my hand evaluator is faster than yours (We would have to exchange benchmark code wouldn't we?), but I've spent a long time writing evaluators for different games too. I find it faster to use a 64 bit int as a bit mask for my evaluator than using seperate ints to represent the cards. Card order is irrelevant when represented in a bit mask, and you can represent a hand of any number of cards in a single 52 (64) bit integer.

Post Extras: Print Post   Remind Me!   Notify Moderator  
Phil153
Notable Twit


Reged: 10/18/05
Posts: 4905
Re: 7 Card Hand Evaluators [Re: TheCardGeek]
      #8639080 - 01/03/07 03:38 AM

I believe this is what poker-eval (aka pokersource) uses. The trouble is, those 5 or 7 bits shifts to create the mask, and then the bitwise operations on that mask to determine duplicates, flushedness, and the if...else operations etc all take CPU cycles. Even with lookup tables it's hard to get the CPU cycles down. Every table lookup for bit counts or straight evaluation or whatever is more time gone.

The optimizations in this thread are a step up. Packing a hand into one ulong simply isn't an optimal strategy.


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: Which is the best packed/unpacked so far? [Re: Steve Brecher]
      #8639849 - 01/03/07 06:43 AM

Quote:

Quote:

Does anybody know of a RNG which will let me just take the bottom N bits and still give a good distribution, and/or a faster method by which I could fold the keys?

Juk



George Marsaglia, who devoted his professional (academic) life to RNGs, posted the following articles to the usenet sci.math group a few years ago. I think you might find them of interest:

http://groups.google.com/group/sci.math/msg/9959175f66dd138f?hl=en

http://groups.google.com/group/sci.math/msg/2a5329cdf4d574a5?hl=en



Thanks, I'll give them a try.

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8647993 - 01/03/07 09:17 PM

Hi,

I finished a consolidated version of the "7 Card Hand Evaluators" written in C. I will comment it tonight and see if I can post it tomorrow... Can I attach 300-400 lines of code to a message (or if it is reasonable to do so?) I did modify things a bit, using a 53 card layout to use the 0 card for current hand strength at 5 and 6 cards. Order doesn't matter, and it gets it all done with an average of less then 10 clock cycles per lookup, about .6 seconds on an amd x2 machine (using one processor for this run) to run the full test at the beginning of this list. File size is: 129,951,336 bytes and the setup program writes out the HandRank file in about a minute.
If you folks know a better way to get the code let me know...
BTW. I have been a lurker on this board for almost 2 years, but this thread is the best I have seen... Keep it goin'!

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
pygmyhipo
journeyman


Reged: 04/26/05
Posts: 80
Re: 7 Card Hand Evaluators [Re: RayW]
      #8648777 - 01/03/07 10:23 PM

Quote:

I will comment it tonight and see if I can post it tomorrow
...
BTW. I have been a lurker on this board for almost 2 years, but this thread is the best I have seen... Keep it goin'!





I totally agree about this thread. It's exciting to see this kind of progress. I'm one of the old pokersource / poker-eval maintainers and I didn't imagine that such gains were possible. In fact, I was coming around to the opinion that the Age of Lookup Tables had passed, thanks to the increasing gap between the time for one instruction and the latency of a cache miss. I thought the next generation of poker-eval would drop all memory accesses and just concentrate on a fast, totally cached instruction stream.

Anyway, if any of you feel like sharing code, I'd be happy to add it to the pokersource library. Although all the current code is GPL, you would be free to apply a different license like Apache or whatever to your contributions.

As for Ray's C version, if you post that I volunteer to port it to Java. It would be nice to have a superfast java evaluator that people could use without the hassles that come with JNI in the current pokersource code.

By the way, the pokersource discussion group is at:
http://tech.groups.yahoo.com/group/pokersource/

-pyg


Post Extras: Print Post   Remind Me!   Notify Moderator  
_dave_
_Pooh-Bah_


Reged: 02/17/05
Posts: 2628
Loc: UK
Re: 7 Card Hand Evaluators [Re: pygmyhipo]
      #8649136 - 01/03/07 10:49 PM

WAY over my head, but I have to agree, one of the most interesting threads in a long time... from the OP:

Quote:

Hi all, new user but was told this was a good poker forum!

Not sure if this is the right place to post, please move if it is not.




So glad you did

dave.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: RayW]
      #8649691 - 01/03/07 11:41 PM

RayW:
Quote:

... Can I attach 300-400 lines of code to a message (or if it is reasonable to do so?)


I don't see why not (although I hope replies to it won't quote the whole thing )

...Not to discourage you from contributing to the pokersource library c/o pygmyhipo.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #8651201 - 01/04/07 02:01 AM

I'd like to show my code, but firstly this is partly for my dissertation so I think my university owns part of it, and secondly, 40 clock cycles and a 250mb lookup is a lot worse than what some other people here have!

Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8652348 - 01/04/07 05:06 AM

Well, Results are in:

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

Validation seconds = 0.5620
Total HighPrecision Clocks = 1127079718
HighPrecision clocks per lookup = 8.424587

This uses Cactus Kev's Eval Routine ref http://www.suffecool.net/poker/evaluator.html
Which I moved Paul D. Senzee's Optimized Hand Evaluator
ref http://www.geocities.com/psenzee/code/fast_eval.c
eval_5hand_fast(int c1, int c2, int c3, int c4, int c5)
routine into Cactus Kevs eval_5hand routine.

You will need to add the routines in PokerLib.c to poker.h file.

Here is the main code which I called HandRankSetup.cpp:
Code:
  
// HandRankSetup.cpp : Sets up the HandRank File for VERY fast Lookups
// by Ray Wotton and the 2+2 list My code is GPL, use it as you like

#include "stdafx.h"
#include "windows.h"
#include "poker.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>

const char HandRanks[][16] = {"BAD!!","High Card","Pair","Two Pair","Three of a Kind","Straight","Flush","Full House","Four of a Kind","Straight Flush"};

__int64 IDs[612978];
int HR[32487834];

int numIDs = 1;
int numcards = 0;
int maxHR = 0;
__int64 maxID = 0;

__int64 MakeID(__int64 IDin, int newcard) // adding a new card to this ID
{
__int64 ID = 0;
int suitcount[4 + 1];
int rankcount[13 + 1];
int workcards[8]; // intentially keeping one as a 0 end
int cardnum;
int getout = 0;

memset(workcards, 0, sizeof(workcards));
memset(rankcount, 0, sizeof(rankcount));
memset(suitcount, 0, sizeof(suitcount));

for (cardnum = 0; cardnum < 6; cardnum++) { // can't have more than 6 cards!
workcards[cardnum + 1] = (int) ((IDin >> (8 * cardnum)) & 0xff); // leave the 0 hole for new card
}

// my cards are 2c = 1, 2d = 2 ... As = 52
newcard--; // make 0 based!

workcards[0] = (((newcard >> 2) + 1) << 4) + (newcard & 3) + 1; // add next card formats card to rrrr00ss

for (numcards = 0; workcards[numcards]; numcards++) {
suitcount[workcards[numcards] & 0xf]++; // need to see if suit is significant
rankcount[(workcards[numcards] >> 4) & 0xf]++; // and rank to be sure we don't have 4!
if (numcards) {
if (workcards[0] == workcards[numcards]) { // can't have the same card twice
getout = 1; // if so need to get out after counting numcards
}
}
}

if (getout) {
return 0; // duplicated another card (ignore this one)
}


int needsuited = numcards - 2; // for suit to be significant - need to have n-2 of same suit

if (numcards > 4) {
for (int rank = 1; rank < 14; rank++) {
if (rankcount[rank] > 4) { // if I have more than 4 of a rank then I shouldn't do this one!!
return 0; // can't have more than 4 of a rank so return an ID that can't be!
}
}
}

// However in the ID process I prefered that
// 2s = 0x21, 3s = 0x31,.... Kc = 0xD4, Ac = 0xE4
// This allows me to sort in Rank then Suit order

// if we don't have at least 2 cards of the same suit for 4, we make this card suit 0.

if (needsuited > 1) {
for (cardnum = 0; cardnum < numcards; cardnum++) { // for each card
if (suitcount[workcards[cardnum] & 0xf] < needsuited) { // check suitcount to the number I need to have suits significant
workcards[cardnum] &= 0xf0; // if not enough - 0 out the suit - now this suit would be a 0 vs 1-4
}
}
}

// Sort Using XOR. Network for N=7, using Bose-Nelson Algorithm: Thanks to the thread!
#define SWAP(I,J) {if (workcards[I] < workcards[J]) {workcards[I]^=workcards[J]; workcards[J]^=workcards[I]; workcards[I]^=workcards[J];}}

SWAP(0, 4);
SWAP(1, 5);
SWAP(2, 6);
SWAP(0, 2);
SWAP(1, 3);
SWAP(4, 6);
SWAP(2, 4);
SWAP(3, 5);
SWAP(0, 1);
SWAP(2, 3);
SWAP(4, 5);
SWAP(1, 4);
SWAP(3, 6);
SWAP(1, 2);
SWAP(3, 4);
SWAP(5, 6);

// long winded way to put the pieces into a __int64
// cards in bytes --66554433221100
// the resulting ID is a 64 bit value with each card represented by 8 bits.
ID = (__int64) workcards[0] +
((__int64) workcards[1] << 8) +
((__int64) workcards[2] << 16) +
((__int64) workcards[3] << 24) +
((__int64) workcards[4] << 32) +
((__int64) workcards[5] << 40) +
((__int64) workcards[6] << 48);

return ID;
}

int SaveID(__int64 ID)
{
if (ID == 0) return 0; // don't use up a record for a 0!

if (ID >= maxID) { // take care of the most likely first goes on the end...
if (ID > maxID) { // greater than create new else it was the last one!
IDs[numIDs++] = ID; // add the new ID
maxID = ID;
}
return numIDs - 1;
}

// find the slot I will find it (by a pseudo bsearch algorithm)
int low = 0;
int high = numIDs - 1;
__int64 testval;
int holdtest;

while (high - low > 1) {
holdtest = (high + low + 1) / 2;
testval = IDs[holdtest] - ID;
if (testval > 0) high = holdtest;
else if (testval < 0) low = holdtest;
else return holdtest; // got it!!
}
// I guess it couldn't be found so must be added to the current location (high)
// make space... // don't expect this much!
memmove(&IDs[high + 1], &IDs[high], (numIDs - high) * sizeof(IDs[0]));

IDs[high] = ID; // do the insert into the hole created
numIDs++;
return high;
}

int DoEval(__int64 IDin)
{
// I guess I have some explaining to do here... I used the Cactus Kevs Eval ref http://www.suffecool.net/poker/evaluator.html
// I Love the pokersource for speed, but I needed to do some tweaking to get it my way
// and Cactus Kevs stuff was easy to tweak ;-)
int handrank = 0;
int cardnum;
int workcard;
int rank;
int suit;
int mainsuit = 20; // just something that will never hit... need to eliminate the main suit from the iterator
int suititerator = 0;
int holdrank;
int workcards[8]; // intentially keeping one as a 0 end
int holdcards[8];
int numevalcards = 0;

// See Cactus Kevs page for explainations for this type of stuff...
const int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41 };

memset(workcards, 0, sizeof(workcards));
memset(holdcards, 0, sizeof(holdcards));

if (IDin) { // if I have a good ID then do it...
for (cardnum = 0; cardnum < 7; cardnum++) { // convert all 7 cards (0s are ok)
holdcards[cardnum] = (int) ((IDin >> (8 * cardnum)) & 0xff);
if (holdcards[cardnum] == 0) break; // once I hit a 0 I know I am done
numevalcards++; // if not 0 then count the card
if (suit = holdcards[cardnum] & 0xf) { // find out what suit (if any) was significant
mainsuit = suit; // and remember it
}
}


for (cardnum = 0; cardnum < numevalcards; cardnum++) { // just have numcards...
workcard = holdcards[cardnum];

// convert to cactus kevs way!! ref http://www.suffecool.net/poker/evaluator.html
// +--------+--------+--------+--------+
// |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
// +--------+--------+--------+--------+
// p = prime number of rank (deuce=2,trey=3,four=5,five=7,...,ace=41)
// r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12)
// cdhs = suit of card
// b = bit turned on depending on rank of card

rank = (workcard >> 4) - 1; // my rank is top 4 bits 1-13 so convert
suit = workcard & 0xf; // my suit is bottom 4 bits 1-4, order is different, but who cares?
if (suit == 0) { // if suit wasn't significant though...
suit = suititerator++; // Cactus Kev needs a suit!
if (suititerator == 5) // loop through available suits
suititerator = 1;
if (suit == mainsuit) { // if it was the sigificant suit... Don't want extras!!
suit = suititerator++; // skip it
if (suititerator == 5) // roll 1-4
suititerator = 1;
}
}
// now make Cactus Keys Card
workcards[cardnum] = primes[rank] | (rank << 8) | (1 << (suit + 11)) | (1 << (16 + rank));
}

switch (numevalcards) { // run Cactus Keys routines
case 5 : holdrank = eval_5cards(workcards[0],workcards[1],workcards[2],workcards[3],workcards[4]);
break;
// if 6 cards I would like to find HandRank for them
// Cactus Key is 1 = highest - 7362 lowest I need to get the min for the permutations
case 6 : holdrank = eval_5cards(workcards[0],workcards[1],workcards[2],workcards[3],workcards[4]);
holdrank = __min(holdrank, eval_5cards(workcards[0],workcards[1],workcards[2],workcards[3],workcards[5]));
holdrank = __min(holdrank, eval_5cards(workcards[0],workcards[1],workcards[2],workcards[4],workcards[5]));
holdrank = __min(holdrank, eval_5cards(workcards[0],workcards[1],workcards[3],workcards[4],workcards[5]));
holdrank = __min(holdrank, eval_5cards(workcards[0],workcards[2],workcards[3],workcards[4],workcards[5]));
holdrank = __min(holdrank, eval_5cards(workcards[1],workcards[2],workcards[3],workcards[4],workcards[5]));
break;
case 7 : holdrank = eval_7hand(workcards);
break;
default : // problem!! shouldn't hit this...
printf(" Problem with numcards = %d!!\n", numcards);
break;
}

// I would like to change the format of Catus Kev's ret value to:
// hhhhrrrrrrrrrrrr hhhh = 1 high card -> 9 straight flush
// r..r = rank within the above 1 to max of 2861
handrank = 7463 - holdrank; // now the worst hand = 1

if (handrank < 1278) handrank = handrank - 0 + 4096 * 1; // 1277 high card
else if (handrank < 4138) handrank = handrank - 1277 + 4096 * 2; // 2860 one pair
else if (handrank < 4996) handrank = handrank - 4137 + 4096 * 3; // 858 two pair
else if (handrank < 5854) handrank = handrank - 4995 + 4096 * 4; // 858 three-kind
else if (handrank < 5864) handrank = handrank - 5853 + 4096 * 5; // 10 straights
else if (handrank < 7141) handrank = handrank - 5863 + 4096 * 6; // 1277 flushes
else if (handrank < 7297) handrank = handrank - 7140 + 4096 * 7; // 156 full house
else if (handrank < 7453) handrank = handrank - 7296 + 4096 * 8; // 156 four-kind
else handrank = handrank - 7452 + 4096 * 9; // 10 straight-flushes

}
return handrank; // now a handrank that I like
}


int _tmain(int argc, _TCHAR* argv[])
{
int count = 0;
int card;

__int64 ID;
int IDslot;

clock_t timer = clock(); // remember when I started

int handTypeSum[10];

memset(handTypeSum, 0, sizeof(handTypeSum)); // init
memset(IDs, 0, sizeof(IDs));
memset(HR, 0, sizeof(HR));


// step through the ID array - always shifting the current ID and adding 52 cards to the end of the array.
// when I am at 7 cards put the Hand Rank in!!
// stepping through the ID array is perfect!!

int IDnum;
int holdid;

printf("\nGetting Card IDs!\n");
// as this loops through and find new combinations it adds them to the end
// I need this list to be stable when I set the handranks (next set) (I do the insertion sort on new IDs these)
// so I had to get the IDs first and then set the handranks
for (IDnum = 0; IDs[IDnum] || IDnum == 0; IDnum++) { // start at 1 so I have a zero catching entry (just in case)
for (card = 1; card < 53; card++) { // the ids above contain cards upto the current card. Now add a new card
ID = MakeID(IDs[IDnum], card); // get the new ID for it
if (numcards < 7) holdid = SaveID(ID); // and save it in the list if I am not on the 7th card
}
printf("\rID - %d", IDnum); // just to show the progress -- this will count up to 612976
}

printf("\nSetting HandRanks!\n");
// this is as above, but will not be adding anything to the ID list, so it is stable
for (IDnum = 0; IDs[IDnum] || IDnum == 0; IDnum++) { // start at 1 so I have a zero catching entry (just in case)
for (card = 1; card < 53; card++) {
ID = MakeID(IDs[IDnum], card);
if (numcards < 7) IDslot = SaveID(ID) * 53 + 53; // when in the index mode (< 7 cards) get the id to save
else IDslot = DoEval(ID); // if I am at the 7th card, get the HandRank to save
maxHR = IDnum * 53 + card + 53; // find where to put it
HR[maxHR] = IDslot; // and save the pointer to the next card or the handrank
}

if (numcards == 6 || numcards == 7) {
// an extra, If you want to know what the handrank when there is 5 or 6 cards
// you can just do HR[u3] or HR[u4] from below code for Handrank of the 5 or 6 card hand
HR[IDnum * 53 + 53] = DoEval(IDs[IDnum]); // this puts the above handrank into the array
}
printf("\rID - %d", IDnum); // just to show the progress -- this will count up to 612976 same as above!
}

printf("\nNumber IDs = %d\nmaxHR = %d\n", numIDs, maxHR); // for warm fuzzys

timer = clock() - timer; // end the timer

printf("Training seconds = %.2f\n", (float)timer/CLOCKS_PER_SEC); // display training time

LARGE_INTEGER timings, endtimings; // for high precision timing

timer = clock(); // now get current time for Testing!

// another algorithm right off the thread

int c0, c1, c2, c3, c4, c5, c6;
int u0, u1, u2, u3, u4, u5;

QueryPerformanceCounter(&timings); // start High Precision clock

for (c0 = 1; c0 < 53; c0++) {
u0 = HR[53+c0];
for (c1 = c0+1; c1 < 53; c1++) {
u1 = HR[u0+c1];
for (c2 = c1+1; c2 < 53; c2++) {
u2 = HR[u1+c2];
for (c3 = c2+1; c3 < 53; c3++) {
u3 = HR[u2+c3];
for (c4 = c3+1; c4 < 53; c4++) {
u4 = HR[u3+c4];
for (c5 = c4+1; c5 < 53; c5++) {
u5 = HR[u4+c5];
for (c6 = c5+1; c6 < 53; c6++) {
handTypeSum[HR[u5+c6] >> 12]++;
count++;
}
}
}
}
}
}
}

QueryPerformanceCounter(&endtimings); // end the high precision clock

timer = clock() - timer; // get the time in this

for (int i = 0; i <= 9; i++) // display the results
printf("\n%16s = %d", HandRanks[i], handTypeSum[i]);

printf("\nTotal Hands = %d\n", count);

__int64 clocksused = (__int64)endtimings.QuadPart - (__int64) timings.QuadPart; // calc clocks used from the High Precision clock

// and display the clock results
printf("\nValidation seconds = %.4lf\nTotal HighPrecision Clocks = %I64d\nHighPrecision clocks per lookup = %lf\n", (double)timer/CLOCKS_PER_SEC, clocksused, (double) clocksused / 133784560.0) ;

// output the array now that I have it!!
FILE * fout = fopen("HandRanks.dat", "wb");
if (!fout) {
printf("Problem creating the Output File!\n");
return 1;
}
fwrite(HR, sizeof(HR), 1, fout); // big write, but quick

fclose(fout);

return 0;
}

///////////////////////////////// end code!!



Wow!! I don't give any promises, but I verified it to the best of my ability over the last few days... As I said earlier, with the setups mentioned above, you will run the setup in about a minute, and poker hands (hand card order doesn't make any difference by the way!!) VERY quickly!!

Any questions let me know...
As I mentioned in the code, my stuff can all be GPL'd.
pygmyhipo, you guys with the pokersource are absolutely incredible what you can do!! Whenever I had a general purpose evaluator to do, that is what I used! I know I should have used the pokersource for the evaluator for this, but perhaps you could rewrite the DoEval section to stub out the pokersource lib... This was a thread project, and it works GREAT!!

Have a Profitable Day...
Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: RayW]
      #8652423 - 01/04/07 05:23 AM

Just Thought I would put out the entire report for the code:

Getting Card IDs!
ID - 612976
Setting HandRanks!
ID - 612976
Number IDs = 612977
maxHR = 32487833
Training seconds = 57.71

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

Validation seconds = 0.5780
Total HighPrecision Clocks = 1141158660
HighPrecision clocks per lookup = 8.529823

Also some code to get you started using the file:
Code:
 // TestHandRank.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>

const char HandRanks[][16] = {"BAD!!","High Card","Pair","Two Pair","Three of a Kind","Straight","Flush","Full House","Four of a Kind","Straight Flush"};

int HR[32487834]; // hold the handranks
LARGE_INTEGER timings, endtimings; // for good timing

int _tmain(int argc, _TCHAR* argv[])
{
int count = 0;
int handTypeSum[10];
int c0, c1, c2, c3, c4, c5, c6;
int u0, u1, u2, u3, u4, u5;


memset(handTypeSum, 0, sizeof(handTypeSum)); // do init..
memset(HR, 0, sizeof(HR));

FILE * fin = fopen("..\\HandRanks.dat", "rb"); // get the hand array saved by HandRankSetup
if (!fin) { // problem say so
printf("Problem Opening Input File!\n");
return 1;
}

size_t bytesread = fread(HR, sizeof(HR), 1, fin); // get the HandRank Array

fclose(fin); // close up

clock_t timer = clock(); // start regular clock

QueryPerformanceCounter(&timings); // start High Precision clock

for (c0 = 1; c0 < 53; c0++) { // run the test
u0 = HR[53+c0];
for (c1 = c0+1; c1 < 53; c1++) {
u1 = HR[u0+c1];
for (c2 = c1+1; c2 < 53; c2++) {
u2 = HR[u1+c2];
for (c3 = c2+1; c3 < 53; c3++) {
u3 = HR[u2+c3];
for (c4 = c3+1; c4 < 53; c4++) {
u4 = HR[u3+c4];
for (c5 = c4+1; c5 < 53; c5++) {
u5 = HR[u4+c5];
for (c6 = c5+1; c6 < 53; c6++) {
handTypeSum[HR[u5+c6] >> 12]++;
count++;
}
}
}
}
}
}
}

QueryPerformanceCounter(&endtimings); // end the high precision clock

timer = clock() - timer; // end the regular clock

for (int i = 0; i <= 9; i++) // display results
printf("\n%16s = %d", HandRanks[i], handTypeSum[i]);

printf("\nTotal Hands = %d\n", count);

__int64 clocksused = (__int64)endtimings.QuadPart - (__int64) timings.QuadPart; // calc clocks used from the High Precision clock

// and display the clock results
printf("\nValidation seconds = %.4lf\nTotal HighPrecision Clocks = %I64d\nHighPrecision clocks per lookup = %lf\n", (double)timer/CLOCKS_PER_SEC, clocksused, (double) clocksused / 133784560.0) ;

return 0;
}




Which returns:

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

Validation seconds = 0.5620
Total HighPrecision Clocks = 1127908727
HighPrecision clocks per lookup = 8.430784

Not Bad
Have a Profitable Day!
Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 7 Card Hand Evaluators [Re: RayW]
      #8652535 - 01/04/07 06:17 AM

This is soooo sweet!

I just gave it a quick test and it's running at 233 million evals per second on my machine (~12.7 clocks per lookup). That totally blows away the old 21 million evals per second I was getting with pokersource... Time to convert my old code to use unpacked hands me thinks!!!

I'm assuming the only benefit to using the perfect hash version faster lookup creation? (I just used Cactus Kev's source).

Thanks - Juk

PS: One other (unrelated) small thing I just worked out was how to get the code from within code-tags without losing all of the indentation: just hit "quote" and copy the text from within the edit box...


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8652540 - 01/04/07 06:18 AM

Quote:

I'd like to show my code, but firstly this is partly for my dissertation so I think my university owns part of it, and secondly, 40 clock cycles and a 250mb lookup is a lot worse than what some other people here have!



Hey think of it this way: if you hadn't of made the original post, then this thread would never have started, so we have alot to thank you for!

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #8652725 - 01/04/07 07:08 AM

Hi Juk!
Quote:

I'm assuming the only benefit to using the perfect hash version faster lookup creation? (I just used Cactus Kev's source).



Yes, the only reason I suggested Paul D. Senzee's Optimized Hand Evaluator was to keep the initial creation under a minute (and my debugging sanity )... You don't need it (you only run this once anyway).

Quote:

PS: One other (unrelated) small thing I just worked out was how to get the code from within code-tags without losing all of the indentation: just hit "quote" and copy the text from within the edit box...




Thank You for that Tip!! I was trying to figure out how to keep the formatting going on and coming out of this BBS.

Sounds like you are Blazin' so I guess the instructions weren't toooo bad!

Have a Profitable Day!
Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: RayW]
      #8656753 - 01/04/07 02:37 PM

This is quite possibly the only time I've looked at someone else's code and knew what they were doing the whole way.

I like the additional vector [0] to give 5, and 6 card evaluations as well.

My code peaked out at 12.5 cycles. Since our access method is the same, I've got to believe the difference is due to my 10 year old compiler.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: RayW]
      #8656876 - 01/04/07 02:46 PM

Quote:

Just Thought I would put out the entire report for the code:



Thanks for sharing -- very interesting!

On my 3.4G P4:
Validation seconds = 0.6410
Total HighPrecision Clocks = 2201553640
HighPrecision clocks per lookup = 16.455962

In testing order dependence, I found there was indeed none w/r correctness, but there is one w/r timing:

Code:
// reverse the loop limits
for (c0 = 52; c0 > 6 ; c0--) {
u0 = HR[53+c0];
for (c1 = c0-1; c1 > 5; c1--) {
u1 = HR[u0+c1];
for (c2 = c1-1; c2 > 4; c2--) {
u2 = HR[u1+c2];
for (c3 = c2-1; c3 > 3; c3--) {
u3 = HR[u2+c3];
for (c4 = c3-1; c4 > 2; c4--) {
u4 = HR[u3+c4];
for (c5 = c4-1; c5 > 1; c5--) {
u5 = HR[u4+c5];
for (c6 = c5-1; c6 > 0; c6--) {
handTypeSum[HR[u5+c6] >> 12]++;
count++;
}
}
}
}
}
}
}


Validation seconds = 0.7660
Total HighPrecision Clocks = 2622109016
HighPrecision clocks per lookup = 19.599489

I just ran the tests, and haven't yet looked into why there is a difference.

Separately...
It makes no perceivable timing difference on our reasonably fast hardware, but I hope this thread is not locking us into this benchmark paradigm, e.g.,

for (c0 = 0; c0 < 52; c0++)
for (c1 = c0+1; c0 < 52; c0++)
...
for (c6 = c5+1; c6 < 52; c6++)

rather than

for (c0 = 0; c0 < 46; c0++)
for (c1 = c0+1; c0 < 47; c0++)
...
for (c6 = c5+1; c6 < 52; c6++)

Something in my twisted cycle-saving brain wants to see the latter


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #8657461 - 01/04/07 03:26 PM

Hi,

I noticed that I can increment faster than decrement as well (by about two cycles per lookup). I didn't consider it a killer since I was within one system clock tick for the full run anyway. Honestly, worst case this seems better then most others best case, and you don't have to worry about sending the cards in the wrong order. You have the Hand Ranking at the 5 or 6 card levels also. You can hit these pretty much any order you find available. Your cards, Opponent possible cards, flop...

I was thinking why I might be getting better numbers that you folks, I set the SSE2 code generation and fully optimizing all compile options, along with instrumenting and optimizing the outcome on VC2005. Other than that it is just a release run.
Good Luck!
Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: RayW]
      #8657758 - 01/04/07 03:44 PM

RayW:
Quote:

I noticed that I can increment faster than decrement as well (by about two cycles per lookup). I didn't consider it a killer since I was within one system clock tick for the full run anyway. Honestly, worst case this seems better then most others best case, and you don't have to worry about sending the cards in the wrong order. ...



No worries, just curiosity! I thought maybe that there were more cache misses with the downward-counting loops, so I measured the mean absolute distance between successive table targets -- u1-u0, u2-u1, ...:

diffs with up loops:
34,556
599,907
2,769,070
6,278,534
12,873,344
46

diffs with down loops:
42,453
730,512
2,908,161
5,481,569
11,114,621
7

...doesn't seem significant.


Post Extras: Print Post   Remind Me!   Notify Moderator  
MrPokerToad
stranger


Reged: 12/15/04
Posts: 12
Re: 7 Card Hand Evaluators [Re: RayW]
      #8661068 - 01/04/07 07:38 PM

(oops)

Post Extras: Print Post   Remind Me!   Notify Moderator  
pygmyhipo
journeyman


Reged: 04/26/05
Posts: 80
Re: 7 Card Hand Evaluators [Re: RayW]
      #8661173 - 01/04/07 07:49 PM

Thanks for posting the code, Ray. I ported it to GCC (using -O3, on a linux box) with a few minor tweaks. Success! Some benchmarks on the 7-card hands validation time:

750 MHz Athlon (gcc 4.1.1): 1.84 secs
3.0 GHz Xeon (gcc 3.4.6): 0.79 secs

Not sure why the Athlon is more than 25% faster, it's hard to say given the different gcc versions.

Porting to Java looks straightforward. I'll let you know how that turns out.

-pyg


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: pygmyhipo]
      #8661519 - 01/04/07 08:25 PM

And now the down side.

make an array

int Hands[1000000][7], or int64 Hands[1000000]

and fill each line with 7 unique random cards.

Start your timer, and find the hand "rank" for each hand.

Due to cache misses, you'll probably get a cycle/hand value that is ugly.



Edited by mykey1961 (01/04/07 08:32 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: pygmyhipo]
      #8663804 - 01/05/07 12:26 AM

Hi pyg!

Quote:

I ported it to GCC (using -O3, on a linux box) with a few minor tweaks.
Some benchmarks on the 7-card hands validation time:

750 MHz Athlon (gcc 4.1.1): 1.84 secs





Wow! I am excited that it worked that easily for you!
Your validation times for an Athlon 750 is only 3x slower than my AMD x2, which I would have thought would have been a bigger difference than that... I guess that new GCC compiler has a real good optimiser! Perhaps the memory bus speed isn't that different...

I am looking forward to seeing how the Java conversion goes, thank you for doing these conversions (Linux/Java) so others can have access to this...

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8663963 - 01/05/07 12:43 AM

mykey1961 said,
Quote:

And now the down side.

make an array

int Hands[1000000][7], or int64 Hands[1000000]

and fill each line with 7 unique random cards.

Start your timer, and find the hand "rank" for each hand.

Due to cache misses, you'll probably get a cycle/hand value that is ugly.



Yikes!

BAD!! = 0
High Card = 174274
Pair = 438503
Two Pair = 234621
Three of a Kind = 48395
Straight = 46295
Flush = 30097
Full House = 25870
Four of a Kind = 1634
Straight Flush = 311
Total Hands = 1000000

Validation seconds = 0.5940
Total HighPrecision Clocks = 2014806880
HighPrecision clocks per lookup = 2014.806880

And with my ancient Hand_7_Eval, which uses 284KB [sic] of tables:

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

Validation seconds = 0.0310
Total HighPrecision Clocks = 117501456
HighPrecision clocks per lookup = 117.501456

Here is the relevant portion of the test code. The commented-out lines were used for the first test above -- the one using RayW's tables based on your approach. The code as shown was used for the second test -- the one using my HandEval code, which uses a 52-bit input within a 64-bit integer:
Code:

int deck[52], virginDeck[52];
for (int d = 0; d < 52; d++)
//virginDeck[d] = d + 1; // lookup table wants cards in 1..52
virginDeck[d] = d;

for (int i = 0; i < 1000000; i++) {
printf("\rDealing hand: %d", i);
memmove(deck, virginDeck, sizeof(deck));
int r = RNG_C::Random_Under(52);
//hands[i][0] = deck[r];
Hand_T h = ((Hand_T)1 << deck[r]);
for (int c = 1; c < 7; c++) {
memmove(&deck[r], &deck[r+1], (52-c-r)*sizeof(int)); // remove dealt card
r = RNG_C::Random_Under(52-c);
//hands[i][c] = deck[r];
h |= ((Hand_T)1 << deck[r]);
}
hands[i] = h;
}
printf("\n");
Init_Hand_Eval();

clock_t timer = clock(); // start regular clock

QueryPerformanceCounter(&timings); // start High Precision clock

for (int i = 0; i < 1000000; i++) {
//int* h = hands[i];
//int p = 53;
//for (int c = 0; c < 7; c++)
// p = HR[p + *h++];
//handTypeSum[p >> 12]++;
handTypeSum[(Hand_7_Eval(hands[i]) >> 24)+1]++;
count++;
}



Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8664111 - 01/05/07 12:59 AM

Hi mykey!

Quote:

And now the down side.

make an array

int Hands[1000000][7], or int64 Hands[1000000]

and fill each line with 7 unique random cards.

Start your timer, and find the hand "rank" for each hand.

Due to cache misses, you'll probably get a cycle/hand value that is ugly.





As you know, the main reason that this idea is so fast is that most of the "overhead" for a card is done is done in a previous stage where it only has to do 1/48th (or whatever) the work, so the total overhead for the average lookup is tiny. If you incur the entire overhead for every card for each individual lookup, of course it would take significantly longer, but wouldn't it take significantly longer in any library you go with given a random array of 7 cards 1-52 or 0-51? Honestly, I don't think that the overhead to HandRank for random cards is all that slow compared to any other library out there (its only one lookup and one sum per card) and of course by doing those operations you get the final handrank for this hand.

Even on worst case that other libraries can beat this routine for a random hand every now and then is OK by me. I don't look for a random hand very often, where I do the most work with an evaluator is doing an enumeration on a wider scale, and that is where I need the performance. Even if it is slower for random hands (and I don't think it is), it wouldn't be a big problem for my normal evaluation needs.

Thank You for checking that out, what kind of numbers were you getting? Have you tried the same test starting with the same set of input cards (as close as possible) with another library?

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
jbs
newbie


Reged: 11/01/03
Posts: 32
Re: 7 Card Hand Evaluators [Re: RayW]
      #8664534 - 01/05/07 01:44 AM

Ray, as you can see from Steve's post above, the big table approach is indeed a lot slower for individual hand lookups, because memory accesses are really slow. (I posted something similar on the pokersource list.)

The big table approach is never going to work very well for arbitrary evaluation (as opposed to enumeration) for the simple reason that even a single memory access is either slower than Steve's entire evaluator, or if not then very close. Actually I think even Steve's might be improved a little by using tables less.


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #8664720 - 01/05/07 02:07 AM

Hi Steve!!

Quote:

h |= ((Hand_T)1 << deck[r]);





Interesting you had this outside the eval routine... The equivelent statement for me is:
Code:
  h = HR[h + deck[r]]; 



Please initialize h to 53 before the beginning of the hand loop. The lookup loop for me will be:
Code:
 handTypeSum[hands[i] >> 12]++;  



I think you will find that this might be a little quicker...

Of course if this isn't a good alternative, put the hand init (quoted code above) into the same for loop in the eval section, I think you will find similar results. I think the for loop inside the eval area is definitely a killer in this...

Either way, even if there really was a speed difference (and I don't think it is as big as it looks), how often would you do a million random hands? If you had to do them, would the half a sec make a big difference? How does your evaluator do for the big enumerations (that at least I seem to always be running)?

What evaluator did you use to do the comparison?

Thank you for the info, definitely had something interesting there...

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
TheCardGeek
journeyman


Reged: 07/09/06
Posts: 54
Re: 7 Card Hand Evaluators [Re: RayW]
      #8664726 - 01/05/07 02:08 AM

RayW,

Thanks for your very interesting post. I successfully ported your code into .Net 2.0 (C#) to test it side by side with the Hand Evaluator I am using.
I am using a derivative work of poker source port bye Keith Rule (http://www.codeproject.com/csharp/pokerhandevaldoc.asp).

Anyway, on my Pentium M 2.0G I get the following comparison benchmarks using the algorithm in your test method:


Code:
***** PokerOddsCalc.HandEvaluatorTest.Test30_Throughput_HandEvaluator (Keith Rule Hand Evaluator)

Iterations: 369052320
Execution Time: 15000ms
Throughput: 24603.488 eps



Code:
***** PokerOddsCalc.HandEvaluatorTest.Test31_Throughput_FiveCardEvaluator_Fast (Paul Senzee's optimization of Cactus Kev Evaluator)

Iterations: 470411760
Execution Time: 15000ms
Throughput: 31360.784 eps



Code:
***** PokerOddsCalc.HandEvaluatorTest.Test32_Throughput_HandRanks (RayW HandRank Evaluator port)

Iterations: 2675691200
Execution Time: 15218.75ms
Throughput: 175815.438193018 eps



Using the standard nested loop algorithm in all three, your evaluator certainly blows the others away which is excellent for complete equity comparisons.



When I benchmarked using random cards during each iteration the scores reversed themselves with the Poker Src port performing much better than the other two, and yours slowest (But your previous post acknowledges this).

Your algorithm is better suited for the kinds of appications that need this kind of performance, so I'll be implementing it more widely soon.

Mostly, I just wanted to let you know your algorithm is up and running in C#, and its working great!


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: TheCardGeek]
      #8664854 - 01/05/07 02:25 AM

Hi!

This is getting translated like crazy and it is only a day old... Was it easy to move to C#? I gotta say that I love this thread!

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
TheCardGeek
journeyman


Reged: 07/09/06
Posts: 54
Re: 7 Card Hand Evaluators [Re: RayW]
      #8664881 - 01/05/07 02:28 AM

Yeah, it was a cinch to port to C#. I imagine Java will be easy.

Post Extras: Print Post   Remind Me!   Notify Moderator  
jbs
newbie


Reged: 11/01/03
Posts: 32
Re: 7 Card Hand Evaluators [Re: RayW]
      #8665193 - 01/05/07 03:10 AM

Ray,

Random hands are useful for sampling situations where its impractical to do a full enumeration. While its true that a million hands isn't that much slower in absolute terms, think about what happens with a billion hands or more.

This is not to take anything away from your algorithm. Its a huge improvement for enumeration, just not appropriate for every application. Nothing wrong with that.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: RayW]
      #8665333 - 01/05/07 03:32 AM

RayW said,
Quote:

Quote:

h |= ((Hand_T)1 << deck[r]);





Interesting you had this outside the eval routine... The equivelent statement for me is:
Code:
  h = HR[h + deck[r]]; 





Hmm. I'm not sure we're going to agree on this. For each evaluator, as part of the pre-test initialization I set up 1M hand specifications in the form that the evaluator takes: 7 cards (ints in 1..52) for yours, and a 52-bit string for mine.
Quote:


Please initialize h to 53 before the beginning of the hand loop. The lookup loop for me will be:
Code:
 handTypeSum[hands[i] >> 12]++;  





(The init to 53 was done -- it's 6 lines up from the closing brace in my post.) But continuing on the theme... where does the test harness end and the test begin? With respect to your evaluator, I think it has to begin with the first of seven lookups, not the last. With respect to mine, I did what I think is reasonable.
Quote:



I think you will find that this might be a little quicker...

Of course if this isn't a good alternative, put the hand init (quoted code above) into the same for loop in the eval section, I think you will find similar results. I think the for loop inside the eval area is definitely a killer in this...


With this code...Code:

int* h = hands[0];
for (int i = 0; i < 1000000; i++) {
int p = HR[53 + *h++];
p = HR[p + *h++];
p = HR[p + *h++];
p = HR[p + *h++];
p = HR[p + *h++];
p = HR[p + *h++];
handTypeSum[HR[p + *h++] >> 12]++;
//handTypeSum[(Hand_7_Eval(hands[i]) >> 24)+1]++;
count++;
}

The result is:
Validation seconds = 0.5940
Total HighPrecision Clocks = 1991107088
HighPrecision clocks per lookup = 1991.107088

...i.e., no change in timing.
Quote:


Either way, even if there really was a speed difference (and I don't think it is as big as it looks), how often would you do a million random hands? If you had to do them, would the half a sec make a big difference? How does your evaluator do for the big enumerations (that at least I seem to always be running)?

What evaluator did you use to do the comparison?



Sometimes research -- stuff that may run for hours or days -- requires large numbers of evaluations of random hands. (More to the point, I theorize, is large numbers of evaluations with chronologically close references to large data tables that are not part of the evaluator.) My evaluator is, I think, in the same ballpark as the pokersource EvalN routine. Mine is about a dozen years old, with a few interim changes. Its source is available at http://www.stevebrecher.com/Software/software.html. The only reason I used mine as a comparison was that it was a low-RAM alternative that I have handy. My point was only to confirm by experiment mykey1961's claim that the table-lookup eval degrades under some conditions.
Quote:

Thank you for the info, definitely had something interesting there...


Hey, obviously we're all sick enough to love this stuff! But seriously... I think the lesson here is that it's good to have a kit, rather than just one tool.

Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #8665748 - 01/05/07 04:52 AM

Hi Steve!!

Quote:

I think the lesson here is that it's good to have a kit, rather than just one tool.




You are absolutely right. I know that the code I posted wasn't everything to all people (just good enough for me ) Everything has good and not as good points, and it would help us to find ways to get around them. I really think if we work together, we can come up with a good set of routines that will work for everyone. Perhaps if we integrate this code with pokersource it would be the cat's meow. Pokersource could handle the random evals, and the thread's routine could handle the enumerations (which is the way they are set-up anyway). We could definitely use the pokersource eval routine for the training run...

What do you folks think?
Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
mykey1961
enthusiast


Reged: 10/18/05
Posts: 249
Re: 7 Card Hand Evaluators [Re: RayW]
      #8666215 - 01/05/07 07:14 AM

Quote:

Hi Steve!!

Quote:

I think the lesson here is that it's good to have a kit, rather than just one tool.




You are absolutely right. I know that the code I posted wasn't everything to all people (just good enough for me ) Everything has good and not as good points, and it would help us to find ways to get around them. I really think if we work together, we can come up with a good set of routines that will work for everyone. Perhaps if we integrate this code with pokersource it would be the cat's meow. Pokersource could handle the random evals, and the thread's routine could handle the enumerations (which is the way they are set-up anyway). We could definitely use the pokersource eval routine for the training run...

What do you folks think?
Ray...




I tried to look at Pokersource a while back but since I'm not fluent in C, and what I saw appeared to use every trick in the C language I couldn't make heads or tails of it.

If someone can give measure the cycles/hand that pokersource acheives for random hands, we can possibly improve on that method too.


Post Extras: Print Post   Remind Me!   Notify Moderator  
TheCardGeek
journeyman


Reged: 07/09/06
Posts: 54
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8666922 - 01/05/07 09:32 AM

Quote:


If someone can give measure the cycles/hand that pokersource acheives for random hands, we can possibly improve on that method too.




The Keith Rule evaluator I benchmarked above is a derivative of Poker Source. I think some other posters in the thread posted their timings of poker source also.

In my testing RayW's code performed 752% over the poker source based code for a complete enumeration whereas it trailed by 22% when performing random evaluations.

Code:
***** PokerOddsCalc.HandEvaluatorTest.Test30_Throughput_HandEvaluator (Keith Rule Hand Evaluator)

Iterations: 369052320
Execution Time: 15000ms
Throughput: 24603.488 eps



Post Extras: Print Post   Remind Me!   Notify Moderator  
New York Jet
addict


Reged: 08/27/04
Posts: 546
Loc: I collect money
Re: 7 Card Hand Evaluators [Re: TheCardGeek]
      #8673327 - 01/05/07 06:37 PM

Quote:

I successfully ported your code into .Net 2.0 (C#)...



Could you please post it? Thanks in advance!!!


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: New York Jet]
      #8685473 - 01/06/07 11:09 PM

I modified RayW's TestHandRank.cpp to do either random hands or enumeration, each with any of three evaluators.

LOOKUP is the mykey1961 big-table evaluator as implemented by RayW; HANDEVAL is mine; EVALN is the pokersource Eval_N.

RANDOM1M is 1,000,000 random hands; ENUMERATE is all 52c7 hands.

For each evaluator/test, the best of three consecutive runs:

LOOKUP/RANDOM1M:
HighPrecision clocks per lookup = 1977.461472

HANDEVAL/RANDOM1M:
HighPrecision clocks per lookup = 112.314720

EVALN/RANDOM1M:
HighPrecision clocks per lookup = 91.655640

LOOKUP/ENUMERATE:
HighPrecision clocks per lookup = 16.126806

HANDEVAL/ENUMERATE:
HighPrecision clocks per lookup = 79.533031

EVALN/ENUMERATE:
HighPrecision clocks per lookup = 81.567737

The source code is at
http://www.stevebrecher.com/misc/TestHandRank.cpp


Post Extras: Print Post   Remind Me!   Notify Moderator  
Smelly Cat
stranger


Reged: 01/07/07
Posts: 2
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8693939 - 01/07/07 07:44 PM

I have a pure Java 7-card evaluator with 12 million/sec on 2.2GHz Athlon 3500+. Check it out at:
http://www.jbridge.net/jimmy


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: Smelly Cat]
      #8699481 - 01/08/07 05:38 AM

Just a thought, my evaluator uses the 250MB LUT which is one of it's main downsides, but zipped up it reduces to around 7.5mb due to the large amount of repeating data in it. I know a little about zipping, but don't know how slow it would be to calculate the region you are trying to read in the compressed file. Probably too slow, but it makes a solution that is similar to mine more scalable.

Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8703909 - 01/08/07 03:04 PM

Hey guys, I found this thread fascinating. I am not very fluent in C++ so my attempt to convert the code to Java didn't seem to work. Did anyone have an attempt to convert the code to other languages yet to see how it performs.

Post Extras: Print Post   Remind Me!   Notify Moderator  
darse
stranger


Reged: 10/06/04
Posts: 6
Re: 7 Card Hand Evaluators [Re: mark007]
      #8721628 - 01/09/07 07:41 PM

For those who haven't achieved full clarity yet, here is a post I made to the Poker Academy forums on the same topic (http://forums.poker-academy.com/viewtopic.php?t=4787).

----

Here is a high-level explanation of the nested table approach, with an example. This is a description of the system I use, which I think is the same as the one in the 2+2 thread (there might be some minor differences, but they should be pretty close since it is the natural thing to do, for some value of "natural").

Imagine six separate tables, corresponding to the number of cards we have currently processed. Each table holds an index value (or a pointer to a particular location) into the next table. It's like a network of pointers, expanding at each stage, but having a branching factor much less than 52 because each set of cards is sorted and reduced to only the essential information.

Let's suppose the first card is the 9h. It is represented as the 1-card hand "9h". The table entry for the 9h contains 52 indices (or pointers) corresponding to the 52 cards in the deck. The pointer for 9h[9h] might point to -1, indicating an error, since a legal hand can't have two 9h.

The second card is the Kc. The hand ID is the full hand sorted by rank and suit, so we currently have the hand "Kc9h". This entry in the second table is pointed to from two places in the first table, namely 9h[Kc] and Kc[9h]. There are 52 out-going pointers for this hand, two of which point to an error state.

The third card is the Kh. Kc9h[Kh] => KhKc9h. There are six different paths through the network leading to this state.

The fourth card is the Qc. KhKc9h[Qc] => KhKcQc9h. We retain the suit information because both hearts and clubs have a potential flush after 7 cards. If this card had been the Qd, then neither diamonds nor clubs could make a potential flush. They would be mapped to a "meta-suit" for no possible flush, which I will call "o" for "offsuit" or "other". Thus, the Qd would have produced KhKc9h[Qd] => KhKoQo9h.

The fifth card is the 2h. KhKcQc9h[2h] => KhKoQo9h2h. Clubs are now irrelevant with respect to possible flushes.

The sixth card is the 5h. KhKoQo9h2h[5h] => KhKoQo9h5h2h. Each entry of the sixth (and largest) table also has 52 out-going pointers, but instead of an index, they contain hand values.

The seventh card is the 7s. KhKoQo9h5h2h[7s] = value(KKQ97) which is 3844. If this had been the 7h, the table entry would contain the value of K9752f, which is 6367.

Note that a 6-card hand with five flush cards can ignore the offsuit card, as it cannot have a bearing on the final value. Thus, the large 6-card table can be reduced in size a little by using a "meta-rank" for "don't care", merging many cases into one. We cannot eliminate the smallest rank of a 6-card flush, however, because it might be part of a straight flush after the seventh card is known.

Note also that the sequential composition of hand IDs uses a lot less memory than more simplistic approaches. This can be further reduced if we dispense with the duplicate cards pointing to an error state, but the indexing gets trickier.

There are two main reasons this method is much faster for systematic enumeration tasks. First, we move most of the work out of the inner-most loops, and do it once instead of repeating it many times. Second, we ensure cache coherence to minimize the number of expensive accesses to main memory. For example, each fetch of an 8K block contains exactly the information that will be needed for the upcoming enumeration cases.

This technique might also be useful for hybrid Monte Carlo simulations over sets of cases, such as all possible river cards for a given 4-card board, or all possible 2-card hands in connection with a random board.

- Darse.


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: darse]
      #8729377 - 01/10/07 11:57 AM

I have converted the code to Java but when running it, I am getting an out of bounds exception at one point.

I am not an expert on the differences between c++ and java but I think my conversion of the following line may be incorrect.

Code:
if (suit = holdcards[cardnum] & 0xf) {	// find out what suit (if any) was significant
mainsuit = suit; // and remember it
}



Is suit being assigned to holdcards[cardnum] & 0xf, or being compared to it? Also the problem may lie with the SWAP function. I have created a swap function as follows

Code:

public static void SWAP(int I, int J) {
if (workcards[I] < workcards[J]) {
workcards[I]^=workcards[J];
workcards[J]^=workcards[I];
workcards[I]^=workcards[J];
}

}



but I am pretty sure this can be converted to

Code:

Arrays.sort(workcards);



in java as this should sort the array fine.... I'm lost as to where else c++ differs as my code is compiling fine.


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mark007]
      #8729936 - 01/10/07 12:45 PM

Hi!

Quote:

Is suit being assigned to holdcards[cardnum] & 0xf, or being compared to it? Also the problem may lie with the SWAP function.




The holdcards[cardnum] is bitwise anded with the lower 4 bits to get the suit,
Then it is assigned to the variable suit.
Then if suit is non-zero it will do the mainsuit = suit (the if)

I shouldn't do equalities in IF statements, but it is a weakness of mine...

Out of bounds wouldn't hit you here though... Most likely it is in the MakeID routine, either the sorting or creating the new ID itself. If you can debug and see where the out of bounds error occurs, I might be able to tell you what went wrong... I have a feeling, I seen every out of bounds that could happen in this code!

Just know if that the Arrays.sort routine will return the psuedo cards in the lower order bits, and that should work, but if you SWAP routine works, isn't that good enough? Keep in mind that the cards will be pretty much in order already, so if the Arrays.sort routine uses qsort, it would be VERY inefficient (worse than a bubble sort).

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: RayW]
      #8730060 - 01/10/07 12:58 PM

Thanks for that Ray. I have changed that line to reflect what it should do (just in more Java friendly terms).

Code:

suit=holdcards[cardnum] & 0xf;
if (suit!=0){ // find out what suit (if any) was significant

mainsuit = suit; // and remember it
}



and the code runs without problems BUT there seems to be a difference between using Array.sort and the swap function as I get the array out of bounds exception.

Code:

public static void SWAP(int I, int J)
{
if (workcards[I] < workcards[J])
{
workcards[I]^=workcards[J];
workcards[J]^=workcards[I];
workcards[I]^=workcards[J];
}
}



The out of bounds exception occurs at

Code:

while (high - low > 1) {
holdtest = (high + low + 1) / 2;
testval = IDs[holdtest] - ID;
if (testval > 0) high = holdtest;
else if (testval < 0) low = holdtest;
else return holdtest; // got it!!
}
// I guess it couldn't be found so must be added to the current location (high)
// make space... // don't expect this much!

// OUT OF BOUNDS BELOW, high reaches value 612978
IDs[high] = ID; // do the insert into the hole created
numIDs++;
return high;
}



Its baffling me because there were very few changes needed in your code, mostly the addition of a few !=0 's...

Any ideas where the code is having problems?

Edited by mark007 (01/10/07 01:22 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mark007]
      #8732725 - 01/10/07 04:47 PM

Hi Mark,

I believe the best way to get this working is to put a bit of test code in the MakeID routine to notice when the numcards goes from one level to another and print out the numIDs when it changes. If the count is correct through the 3rd card, but is off on the 4th then you know that it is the grouping function of makeids (or the SWAP or Arrays.sort not working right).

So add just before in MakeID:
int needsuited = numcards - 2;
Code:
 
if (oldnumcards > numcards) {
printf("numcards = %d\tnumIDs = %d\n", numcards, numIDs);
oldnumcards = numcards; // break point here if you need it
}


compare the numIDs with the numbers that mkey had, that will tell you a lot.
I would leave it with the SWAP routine if you have a choice.

Does it blow up with the swap in place?

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: RayW]
      #8733103 - 01/10/07 05:21 PM

Hi Ray,

I have reverted to the old SWAP and it seems to work perfect (verified it was sorting correctly myself).

I did make the mistake of removing the line

Code:

memmove(&IDs[high + 1], &IDs[high], (numIDs - high) * sizeof(IDs[0]));



My conversion from this to java would be

Code:

System.arraycopy(IDs,high,IDs,(high+1),(numIDs - high) );



And all ID's seem to be created now (not sure if they are correct). Java uses the "number of items" when copying from one array to the other, not the amount of data as in C. In this case I got numIDs - high from your code, which seems correct?

The problem is that, now after creating the IDs, when DoEval is ran, after approximately 260000 iterations, I get an out of bounds exception in my eval_7hand routine (which I know for a fact works perfectly).

My eval_7hand sees that the first card it encounters with this ID, has a card value of more than 12 Ill do my best to investigate this but I assume it isn't creating the ID correctly at some point.

Edited by mark007 (01/10/07 05:23 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: mark007]
      #8734914 - 01/10/07 09:21 PM

Here is what my java code is outputting so far... I'm sure someone who knows c++ and java inside out could convert between easily

Getting Card IDs!


ID - 0
ID - 1
ID - 2
ID - 3

.
.
.
.
ID - 612973
ID - 612974
ID - 612975
ID - 612976

Setting HandRanks!

ID - 0
ID - 1
ID - 2

.
.
.
.
ID - 260528
ID - 260529
ID - 260530
ID - 260531
ID - 260532
ID - 260533 < < OUT OF BOUNDS IN 7 Card Evaluator (value of 13 (max 12) in a card value)

Edit: I just realised, __int64 is not the equilivant of a simple long in java, as java doesn't use unsigned datatypes..yukk

Edited by mark007 (01/10/07 09:42 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: mykey1961]
      #8747706 - 01/11/07 09:29 PM

Code thats blowing up on me so far
Code:

static long IDs[] = new long[612978];
static int numIDs = 1;
static long maxID = 0;
static int numcards = 0;
static int maxHR = 0;
static int HR[] = new int[32487834];
static int workcards[];
static int oldnumcards=1;

public static void main (String [] args)
{
doSetup();
}

public static int SaveID(long ID) {


if (ID == 0) return 0; // don't use up a record for a 0!

if (ID >= maxID) { // take care of the most likely first goes on the end...
if (ID > maxID) { // greater than create new else it was the last one!


IDs[numIDs++] = ID; // add the new ID
maxID = ID;
}
return numIDs - 1;
}

// find the slot I will find it (by a pseudo bsearch algorithm)
int low = 0;
int high = numIDs - 1;
long testval;
int holdtest;

while (high - low > 1) {
holdtest = (high + low + 1) / 2;
testval = IDs[holdtest] - ID;
if (testval > 0)
high = holdtest;
else if (testval < 0)
low = holdtest;
else return holdtest; // got it!!
}
// I guess it couldn't be found so must be added to the current location (high)
// make space... // don't expect this much!

System.arraycopy(IDs,high,IDs,(high+1),(numIDs - high) );


IDs[high] = ID; // do the insert into the hole created

numIDs++;
return high;
}


public static long MakeID(long IDin, int newcard) // adding a new card to this ID
{

int suitcount[] = new int[4 + 1];
int rankcount[] = new int[13 + 1];
workcards = new int [8]; // intentially keeping one as a 0 end
int cardnum;
int getout = 0;
long ID = 0;

for (cardnum = 0; cardnum < 6; cardnum++) { // can't have more than 6 cards!
workcards[cardnum + 1] = (int)((IDin >>> (8 * cardnum)) & 0xff); // leave the 0 hole for new card
}

// my cards are 2c = 1, 2d = 2 ... As = 52
newcard--; // make 0 based!

workcards[0] = (((newcard >>> 2) + 1) << 4) + (newcard & 3) + 1; // add next card formats card to rrrr00ss


for (numcards = 0; workcards[numcards]!=0; numcards++) {
suitcount[workcards[numcards] & 0xf]++; // need to see if suit is significant
rankcount[(workcards[numcards] >>> 4) & 0xf]++; // and rank to be sure we don't have 4!

if (numcards!=0) {
if (workcards[0] == workcards[numcards]) { // can't have the same card twice
getout = 1; // if so need to get out after counting numcards
}
}
}


if (getout!=0) {
return 0; // duplicated another card (ignore this one)
}


int needsuited = numcards - 2; // for suit to be significant - need to have n-2 of same suit

if (oldnumcards < numcards) {

System.out.println("Changing to " + numcards + " " + numIDs);
oldnumcards = numcards; // break point here if you need it

}

if (numcards > 4) {
for (int rank = 1; rank < 14; rank++) {
if (rankcount[rank] > 4) { // if I have more than 4 of a rank then I shouldn't do this one!!
return 0; // can't have more than 4 of a rank so return an ID that can't be!
}
}
}

// However in the ID process I prefered that
// 2s = 0x21, 3s = 0x31,.... Kc = 0xD4, Ac = 0xE4
// This allows me to sort in Rank then Suit order

// if we don't have at least 2 cards of the same suit for 4, we make this card suit 0.

if (needsuited > 1) {
for (cardnum = 0; cardnum < numcards; cardnum++) { // for each card
if (suitcount[workcards[cardnum] & 0xf] < needsuited) { // check suitcount to the number I need to have suits significant
workcards[cardnum] &= 0xf0; // if not enough - 0 out the suit - now this suit would be a 0 vs 1-4
}
}
}

// Sort Using XOR. Network for N=7, using Bose-Nelson Algorithm: Thanks to the thread!


SWAP(0, 4);
SWAP(1, 5);
SWAP(2, 6);
SWAP(0, 2);
SWAP(1, 3);
SWAP(4, 6);
SWAP(2, 4);
SWAP(3, 5);
SWAP(0, 1);
SWAP(2, 3);
SWAP(4, 5);
SWAP(1, 4);
SWAP(3, 6);
SWAP(1, 2);
SWAP(3, 4);
SWAP(5, 6);


// long winded way to put the pieces into a long
// cards in bytes --66554433221100
// the resulting ID is a 64 bit value with each card represented by 8 bits.
ID = (long) workcards[0] +
((long) workcards[1] << 8) +
((long) workcards[2] << 16) +
((long) workcards[3] << 24) +
((long) workcards[4] << 32) +
((long) workcards[5] << 40) +
((long) workcards[6] << 48);

return ID;
}

public static void SWAP(int I, int J) {
if (workcards[I] < workcards[J]) {
workcards[I]^=workcards[J];
workcards[J]^=workcards[I];
workcards[I]^=workcards[J];
}
}

public static int DoEval(long IDin) {


// I guess I have some explaining to do here... I used the Cactus Kevs Eval ref http://www.suffecool.net/poker/evaluator.html
// I Love the pokersource for speed, but I needed to do some tweaking to get it my way
// and Cactus Kevs stuff was easy to tweak ;-)
int handrank=9999;
int cardnum;
int workcard;
int rank;
int suit=0;
int mainsuit = 20; // just something that will never hit... need to eliminate the main suit from the iterator
int suititerator = 0;
int holdrank=9999;
workcards=new int[8]; // intentially keeping one as a 0 end
int holdcards[]=new int[8];
int numevalcards = 0;

// See Cactus Kevs page for explainations for this type of stuff...
final int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41 };


if (IDin!=0) { // if I have a good ID then do it...
for (cardnum = 0; cardnum < 7; cardnum++) { // convert all 7 cards (0s are ok)
holdcards[cardnum] = (int) ((IDin >>> (8 * cardnum)) & 0xff);
if (holdcards[cardnum] == 0) break; // once I hit a 0 I know I am done
numevalcards++; // if not 0 then count the card

if ((suit = holdcards[cardnum] & 0xf) != 0) { // find out what suit (if any) was significant
mainsuit = suit; // and remember it
}
}


for (cardnum = 0; cardnum < numevalcards; cardnum++) { // just have numcards...
workcard = holdcards[cardnum];

// convert to cactus kevs way!! ref http://www.suffecool.net/poker/evaluator.html
// +--------+--------+--------+--------+
// |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
// +--------+--------+--------+--------+
// p = prime number of rank (deuce=2,trey=3,four=5,five=7,...,ace=41)
// r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12)
// cdhs = suit of card
// b = bit turned on depending on rank of card

rank = (workcard >>> 4) - 1; // my rank is top 4 bits 1-13 so convert

suit = workcard & 0xf; // my suit is bottom 4 bits 1-4, order is different, but who cares?
if (suit == 0) { // if suit wasn't significant though...
suit = suititerator++; // Cactus Kev needs a suit!
if (suititerator == 5) // loop through available suits
suititerator = 1;
if (suit == mainsuit) { // if it was the sigificant suit... Don't want extras!!
suit = suititerator++; // skip it
if (suititerator == 5) // roll 1-4
suititerator = 1;
}
}
// now make Cactus Keys Card
workcards[cardnum] = primes[rank] | (rank << 8) | (1 << (suit + 11)) | (1 << (16 + rank));
}

switch (numevalcards) { // run Cactus Keys routines
case 5 : holdrank = eval_5cards(workcards[0],workcards[1],workcards[2],workcards[3],workcards[4]);

break;
// if 6 cards I would like to find HandRank for them
// Cactus Key is 1 = highest - 7362 lowest I need to get the min for the permutations
case 6 :

holdrank = eval_5cards(workcards[0],workcards[1],workcards[2],workcards[3],workcards[4]);
holdrank = min(holdrank, eval_5cards(workcards[0],workcards[1],workcards[2],workcards[3],workcards[5]));
holdrank = min(holdrank, eval_5cards(workcards[0],workcards[1],workcards[2],workcards[4],workcards[5]));
holdrank = min(holdrank, eval_5cards(workcards[0],workcards[1],workcards[3],workcards[4],workcards[5]));
holdrank = min(holdrank, eval_5cards(workcards[0],workcards[2],workcards[3],workcards[4],workcards[5]));
holdrank = min(holdrank, eval_5cards(workcards[1],workcards[2],workcards[3],workcards[4],workcards[5]));

break;
case 7 :

holdrank = eval_7cards(workcards[0],workcards[1],workcards[2],workcards[3],workcards[4],workcards[5],workcards[6]);

break;
default : // problem!! shouldn't hit this...
System.out.println(" Problem with numcards = " + numcards);
break;

}

// I would like to change the format of Catus Kev's ret value to:
// hhhhrrrrrrrrrrrr hhhh = 1 high card -> 9 straight flush
// r..r = rank within the above 1 to max of 2861

handrank = 7463 - holdrank; // now the worst hand = 1

if (handrank < 1278) handrank = handrank - 0 + 4096 * 1; // 1277 high card
else if (handrank < 4138) handrank = handrank - 1277 + 4096 * 2; // 2860 one pair
else if (handrank < 4996) handrank = handrank - 4137 + 4096 * 3; // 858 two pair
else if (handrank < 5854) handrank = handrank - 4995 + 4096 * 4; // 858 three-kind
else if (handrank < 5864) handrank = handrank - 5853 + 4096 * 5; // 10 straights
else if (handrank < 7141) handrank = handrank - 5863 + 4096 * 6; // 1277 flushes
else if (handrank < 7297) handrank = handrank - 7140 + 4096 * 7; // 156 full house
else if (handrank < 7453) handrank = handrank - 7296 + 4096 * 8; // 156 four-kind
else handrank = handrank - 7452 + 4096 * 9; // 10 straight-flushes

}
return handrank; // now a handrank that I like
}
public static int min(int a, int b) {
if (a<b)
return a;
else
return b;
}
public static void doSetup() {
int count = 0;
int card;

long ID;
int IDslot;

int handTypeSum[]=new int[10];

// step through the ID array - always shifting the current ID and adding 52 cards to the end of the array.
// when I am at 7 cards put the Hand Rank in!!
// stepping through the ID array is perfect!!

int IDnum;
int holdid;

System.out.println("\nGetting Card IDs!\n");
// as this loops through and find new combinations it adds them to the end
// I need this list to be stable when I set the handranks (next set) (I do the insertion sort on new IDs these)
// so I had to get the IDs first and then set the handranks

for (IDnum = 0; IDs[IDnum]!=0 || IDnum == 0; IDnum++) { // start at 1 so I have a zero catching entry (just in case)

for (card = 1; card < 53; card++) { // the ids above contain cards upto the current card. Now add a new card
ID = MakeID(IDs[IDnum], card); // get the new ID for it

if (numcards < 7) holdid = SaveID(ID); // and save it in the list if I am not on the 7th card

}

System.out.println("\rID - " + IDnum); // just to show the progress -- this will count up to 612976

}

System.out.println("\nSetting HandRanks!\n");

// this is as above, but will not be adding anything to the ID list, so it is stable

for (IDnum = 0; IDs[IDnum]!=0 || IDnum == 0; IDnum++) { // start at 1 so I have a zero catching entry (just in case)

for (card = 1; card < 53; card++) {
ID = MakeID(IDs[IDnum], card);

if (numcards < 7) IDslot = SaveID(ID) * 53 + 53; // when in the index mode (< 7 cards) get the id to save
else IDslot = DoEval(ID); // if I am at the 7th card, get the HandRank to save

maxHR = IDnum * 53 + card + 53; // find where to put it
HR[maxHR] = IDslot; // and save the pointer to the next card or the handrank
}

if (numcards == 6 || numcards == 7) {
// an extra, If you want to know what the handrank when there is 5 or 6 cards
// you can just do HR[u3] or HR[u4] from below code for Handrank of the 5 or 6 card hand

HR[IDnum * 53 + 53] = DoEval(IDs[IDnum]); // this puts the above handrank into the array
}
//if (IDnum%10000==0)
System.out.println("\rID - " + IDnum); // just to show the progress -- this will count up to 612976 same as above!
}

System.out.println("\nNumber IDs = \nmaxHR = " + numIDs + " " + maxHR); // for warm fuzzys


int c0, c1, c2, c3, c4, c5, c6;
int u0, u1, u2, u3, u4, u5;


//Begin timer, to be done

System.out.println("Starting Test");
for (c0 = 1; c0 < 53; c0++) {
u0 = HR[53+c0];
for (c1 = c0+1; c1 < 53; c1++) {
u1 = HR[u0+c1];
for (c2 = c1+1; c2 < 53; c2++) {
u2 = HR[u1+c2];
for (c3 = c2+1; c3 < 53; c3++) {
u3 = HR[u2+c3];
for (c4 = c3+1; c4 < 53; c4++) {
u4 = HR[u3+c4];
for (c5 = c4+1; c5 < 53; c5++) {
u5 = HR[u4+c5];
for (c6 = c5+1; c6 < 53; c6++) {
handTypeSum[HR[u5+c6] >>> 12]++;
count++;
}
}
}
}
}
}
}

//stop timer, to be done

for (int i = 0; i <= 9; i++) // display the results
System.out.println(handTypeSum[i]);

System.out.println("\nTotal Hands = " + count);

//file output, to be done
}



Post Extras: Print Post   Remind Me!   Notify Moderator  
Mogobu The Fool
addict


Reged: 09/10/05
Posts: 617
Loc: On teh internets.
Re: 7 Card Hand Evaluators [Re: mark007]
      #8768458 - 01/13/07 11:58 PM

I'm still loving this thread when I get time to peek at it...

Just as an FYI, you should all feel free to post code, descriptions, etc. over that the overcards.com wiki pages: http://overcards.com/wiki/moin.cgi . The web pages and forums are somewhat dead, what with my retirement from poker, but the wiki pages have a life of their own as a centrum for AHK scripting nuts who play online poker. Feel free to bend the wiki to your own needs to post segments of code to discuss here - the wiki pages will keep an automatic version history of the code changes posted; just peek at the AHK pages for page layout ideas.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Brad Quick
stranger


Reged: 01/14/07
Posts: 1
Re: 7 Card Hand Evaluators [Re: Mogobu The Fool]
      #8771252 - 01/14/07 10:39 AM

This thread got me to register for these forums.

This evaluator is great. I got it running in C++ and with it, I'm able to exaustively calculate the chance of every starting hand winning after the flop in .035 seconds. After the flop is the worst case because there are the most combinations. It goes faster for other game stages.

BTW, this is on an Intel MacBook Pro.

- Brad


Post Extras: Print Post   Remind Me!   Notify Moderator  
Mogobu The Fool
addict


Reged: 09/10/05
Posts: 617
Loc: On teh internets.
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #8771675 - 01/14/07 12:10 PM

Quote:

I'm assuming the only benefit to using the perfect hash version faster lookup creation? (I just used Cactus Kev's source).





No, the perfect hash version's advantage comes from faster actual lookups... The last step in Cactus Kev's approach, after using the product of primes to establish a unique code which represnts any version of a 7-card holding, is to look up that product in a table. In Cactus Kev's code, the lookup is done via binary search. In Senzee's version, doing a hash and lookup is considerable faster on average - resulting in evals about 2.7 times faster in Senzee's tests.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Mogobu The Fool
addict


Reged: 09/10/05
Posts: 617
Loc: On teh internets.
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8771779 - 01/14/07 12:26 PM

Quote:

I know a little about zipping, but don't know how slow it would be to calculate the region you are trying to read in the compressed file. Probably too slow, but it makes a solution that is similar to mine more scalable.




I don't think that's an option... the way to calculate the location is to decompress the file up to that point. The only way to get by the paging slowdowns is to keep the entire LUT in fast memory... Hence my obsession with finding a compact perfect hash. I'm virtually certain one can find a fast perfect hash that will have a lookup table of 32k entries.


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mark007]
      #8772901 - 01/14/07 03:07 PM

Hi!

I have run this code in Java (other than the eval routines which I stubbed out with a return 1). They worked great! I also put in within DoEval (after rank = (workcard >>> 4) - 1;):
Code:
	                	 // my rank is top 4 bits 1-13 so convert
if (rank > 12) {
System.out.println(" Problem with rank = " + rank);
}


Which never was hit... I had to put in the heap memory allocator in:
java -Xms130m -Xmx150m HandRankSetup
to keep away from Heap errors. You did well doing the conversion... I believe that your code would work as is!

(Pretty speedy too even in Java!)

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: RayW]
      #8772958 - 01/14/07 03:14 PM

Hi Ray,

Yes the java code "works" up until the point where the actual evaluations are done but breaks there. I do not think it is working correctly therefore as I know my old eval functions work given proper input. Any chance you could help us java folk by adding some printf 's in your c code to show, for example, the actual value of each id as it is created, as they seem to be created differently using my c code..


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mark007]
      #8772972 - 01/14/07 03:17 PM

Hi Mark,

Another thought...

If you aren't using Cactus Kev's routine to do the evaluation of the hand, you would have a problem with the translation of the cards in the DoEval routine!! Are you using Cactus Kev's Eval routines?

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: RayW]
      #8772992 - 01/14/07 03:25 PM

Yep I am, I assume the problem would lie with the creation of the ID's, thanks alot by the way. I can't wait to see how fast this works in java compared to c. If you have a spare minute you might spend a moment to get your c code to print out the value or maybe even binary representation of the Id's as they are created.

Of course to be removed later when the differences are found.


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mark007]
      #8773011 - 01/14/07 03:32 PM

Hi Mark,

I am running your code in Java. It worked fine.... No rank problems as I mentioned (put the rank > 12 code in as above and see if you hit that line). I didn't hit it. Also you need the heap memory increase (I am sure you had that!!). I didn't see anything wrong, and it came through cleanly (including rank). Let me know how you do...

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: RayW]
      #8773018 - 01/14/07 03:34 PM

Thanks Ray, I don't know where I am going wrong so.... I actually had that line in my code and it is never hit either, yet my 7 card evaluator blows up. Ill have a fiddle around but its strange it works fine for you

Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: mark007]
      #8773140 - 01/14/07 03:51 PM

Just before the switch statement where the eval's are done, I added the following lines. This shows that one of the workcards being sent to my evaluator has a rank of >12. I don't know whats going on now...

My code gets the out of bounds first in the 7 card evaluator, and with 7 card evals commented out, the 6 card evaluator goes out of bounds soon afterwards.
Code:

for (int cardNo=0; cardNo<5; cardNo++)
{
if (((workcards[cardNo]>>>8)& 0xF) > 12)
{
System.out.println("WOOPS, this cards rank was above 12");
System.exit(0);
}
}

switch (numevalcards) { // run Cactus Keys routines
case 5 : holdrank = eval_5cards(workcards[0],workcards[1],workcards[2],workcards[3],workcards[4]);



Edited by mark007 (01/14/07 04:07 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: mark007]
      #8773732 - 01/14/07 05:27 PM

Hi EVERYONE!!!

I did have a bug in the program in the DoEval Routine!!!

Please change the line at the top of the routine:
int suititerator = 0;

to

int suititerator = 1;

This should eliminate your problem with Java... For the C People out there... You should change this line in your code, but it didn't make a difference in the C routines because the only thing that this affected is the 0s for suit coming into the high bit of the rank. Suit didn't matter on these (because of the 0 suit), and if you don't have a suited hand it works off the prime numbers (not the rank >> 8). Interesting hit though, to be safe please rerun your Setup Programs...

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: RayW]
      #8773975 - 01/14/07 05:59 PM

Sweet, that fixed my problems Here is the output and thanks alot for the efforts.

Getting Card IDs!
Setting HandRanks!
maxHR = 612977 32487833
Training time: 44.813 seconds
Starting Test
0
23294460
58627800
31433400
6461620
6180020
4047644
3473184
224848
41584

Total Hands = 133784560
0.783 seconds

Edited by mark007 (01/14/07 06:00 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
rvg72
STTF Coder Dude


Reged: 06/01/05
Posts: 2342
Loc: Canada
Re: 7 Card Hand Evaluators [Re: mark007]
      #8777914 - 01/15/07 01:35 AM

Anyone ever post the .NET version? I'd love to take a look at that.

rvg


Post Extras: Print Post   Remind Me!   Notify Moderator  
thomash
stranger


Reged: 01/17/07
Posts: 3
Re: 7 Card Hand Evaluators [Re: rvg72]
      #8796672 - 01/17/07 11:15 AM

I am thinking of not storing an integer hand rank in the table but storing the actual effective hand strength. With effective hand strength I mean the probability that this hand will win the game if it comes to showdown. I start by generating the effective hand strengths for the 7-card combinations and then work my way backwards so I've got tables for 6,5 and 2 cards.

Has anyone tried this so far?

Thomas


Post Extras: Print Post   Remind Me!   Notify Moderator  
Grunch
Bounty Hunter


Reged: 08/25/04
Posts: 9623
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8797627 - 01/17/07 12:30 PM

Just can't resist...

My code isn't like any of yours I don't think. My goal wasn't to write a fast evaluator, necesarrily, but to see if I could figure out how to evaluate a 7-card holdem hand with logic & no lookup tables. So that's what I did. Its not fast, but it does what I wanted. Just a different approach.

Code:

#include "stdafx.h"
#include "score.h"
#include <set>
#include <vector>



static const size_t sic = 0, sis = 1, sih = 2, sid = 3;
static const size_t ria = 0, rik = 1, riq = 2, rij = 3, rit = 4, ri9 = 5, ri8 = 6, ri7 = 7, ri6 = 8, ri5 = 9, ri4 = 10, ri3 = 11, ri2 = 12;

struct matrix
{
// running totals of each suit
typedef std::pair<int,int> suit_ttl; // first = total cards of this suit, second = high card in this suit
suit_ttl suit_ttls[4];

// running totals of each rank
typedef int rank_ttl;
rank_ttl rank_ttls[13];

// table card matrix
struct col
{
bool suit[4];
bool rank[13];
};
col board[7];
};

void BuildHEMatrix(card cards[7], bool suit[4][7], bool rank[13][7])
{
for( int i = 0; i < 7; ++i )
{
suit[cards[i].suit][i] = true;
rank[cards[i].rank][i] = true;
}
}

inline handval BuildScore(int mask, int a, int b, int c, int d, int e)
{
handval ret = a;

ret <<= 4;
ret |= b;

ret <<= 4;
ret |= c;

ret <<= 4;
ret |= d;

ret <<= 4;
ret |= e | mask;

return ret;
}

handval ScoreHE(const card cards[7])
{
// build the card matrix
int flush_ttl[4] = {0};
int flush_hicard[4] = {0};
// int flush_locard[4] = {13};
int rank_ttl[13] = {0};

bool card_matrix[13][4] = {false};

for( int i = 0; i < 7; ++i )
{
const card& c = cards[i];
card_matrix[c.rank][c.suit] = true;

++flush_ttl[c.suit];
if( c.rank > flush_hicard[c.suit] ) flush_hicard[c.suit] = c.rank;
// if( c.rank < flush_locard[c.suit] ) flush_locard[c.suit] = c.rank;
++rank_ttl[c.rank];
}

// look for straight flush
for(int s = 0; s < 4; ++s )
{
int remain = flush_ttl[s];
int run = 0;
int sf_rank = -1;

for( int r = flush_hicard[s]; run < 5 && r <= 12 && (remain+run)>=5; --r, --remain )
{
if( card_matrix[r][s] )
{
if( ++run == 1 ) sf_rank = r;
}
else run = 0;
}

if( 5 == run ) return BuildScore(hipk_sflush, sf_rank, sf_rank-1, sf_rank-2, sf_rank-3, sf_rank-4 ); // we found a straight flush
}

// look for quads, trips, pairs etc
int quads_rank = -1,
trips_rank = -1,
twpr_rank = -1,
pr_rank = -1,
str_rank = -1,
str_run = 0;

for( int r = 12; r >= 0; --r )
{
if( quads_rank == -1 && rank_ttl[r] == 4 ) quads_rank = r;
if( trips_rank == -1 && rank_ttl[r] == 3 ) trips_rank = r;
if( twpr_rank == -1 && rank_ttl[r] == 2 )
{
if( pr_rank == -1 ) pr_rank = r;
else
{
if( pr_rank > r )
{
twpr_rank = pr_rank;
pr_rank = r;
}
else
twpr_rank = r;
}
}
if( str_rank == -1 )
{
if( rank_ttl[r] == 0 ) str_run = 0;
else if( ++str_run == 5 ) str_rank = r+4;
}
}

// score quads
if( quads_rank > -1 )
{
int kick = -1;
for( int r = 12; r >= 0 && -1 == kick; --r ) if( rank_ttl[r] > 0 && rank_ttl[r] != 4 ) kick = r; // find kicker
return BuildScore(hipk_quads, quads_rank, quads_rank, quads_rank, quads_rank, kick);
}

// score boat
if( trips_rank != -1 && pr_rank != -1 )
{
if( twpr_rank != -1 ) return BuildScore(hipk_boat, trips_rank, trips_rank, trips_rank, twpr_rank, twpr_rank);
else return BuildScore(hipk_boat, trips_rank, trips_rank, trips_rank, pr_rank, pr_rank);
}

// score flush
for( int s = 0; s < 4; ++s )
{
if( flush_ttl[s] < 5 ) continue;
std::vector<int> fc;
for( int r = flush_hicard[s]; r >=0 && fc.size() < 5; --r )
{
if( card_matrix[r][s] ) fc.push_back(r);
}
return BuildScore(hipk_flush, fc[0], fc[1], fc[2], fc[3], fc[4]);
}

// score straight
if( str_rank != -1 ) return BuildScore(hipk_straight, str_rank, str_rank-1, str_rank-2, str_rank-3, str_rank-4);

// score trips
if( trips_rank != -1 )
{
std::vector<int> kickers;
for( int r = 12; r >= 0 && kickers.size() < 2; --r )
{
if( r != trips_rank && rank_ttl[r] > 0 ) kickers.push_back(r);
}
return BuildScore(hipk_trips, trips_rank, trips_rank, trips_rank, kickers[0], kickers[1]);
}

// score two pair
if( twpr_rank != -1 )
{
int kick = -1;
for( int r = 12; r >= 0 && -1 == kick; --r )
if( r != twpr_rank && r != pr_rank && rank_ttl[r] > 0 )
kick = r; // find kicker
return BuildScore(hipk_2pr, twpr_rank, twpr_rank, pr_rank, pr_rank, kick);
}

// score pair
if( pr_rank != -1 )
{
std::vector<int> kicks;
for( int r = 12; r >= 0 && kicks.size() < 3; --r )
if( rank_ttl[r] == 1 ) kicks.push_back(r); // find 3 kickers
return BuildScore(hipk_1pr, pr_rank, pr_rank, kicks[2], kicks[1], kicks[0]);
}

// score hi cards
std::vector<int> kicks;
for( int r = 12; r >= 0 && kicks.size() < 5; --r )
if( rank_ttl[r] > 0 ) kicks.push_back(r);
return BuildScore(hipk_hicard, kicks[4], kicks[3], kicks[2], kicks[1], kicks[0]);
}




Post Extras: Print Post   Remind Me!   Notify Moderator  
Grunch
Bounty Hunter


Reged: 08/25/04
Posts: 9623
Re: 7 Card Hand Evaluators [Re: Grunch]
      #8797802 - 01/17/07 12:42 PM

here's a test harness:

Code:

/***
John Dibling
johndotdiblingatsungarddotcom

***/

#include "stdafx.h"
#include "score.h"
#include <algorithm>
#include <time.h>
#include <string>
#include <iostream>
//#include "x:\utils\stlext\stringext.h"
#include <string>
#include <vector>
#include <algorithm>
#include <list>
#include <cstdarg>
#include <ctype.h>


/***

NOTE : The following functions in namespace std are from my STL Extensions library.
Only the necesarry code has been copied out of the STXEXT library and pasted here.

***/

namespace std
{
/* ---

Formatted Print

template<class C>
int strprintf(basic_string<C>* pString, const C* pFmt, ...);

template<class C>
int vstrprintf(basic_string<C>* pString, const C* pFmt, va_list args);

Returns :

# characters printed to output


Effects :

Writes formatted data to a string. strprintf() works exactly the same as sprintf(); see your
documentation for sprintf() for details of peration. vstrprintf() also works the same as sprintf(),
but instead of accepting a variable paramater list it accepts a va_list argument.

Requires :

pString is a pointer to a basic_string<>

--- */

inline int vstrprintf(string* pString, const char* pFmt, va_list args)
{
// prologue
static const size_t ChunkSize = 1024;
int retval = 0;
// get local work buffer
size_t nBufSize = 0;
char* pBuf = 0;
// format up string
int i = -1;
for( ; i == -1; )
{
// realloc local buffer
if( pBuf )
delete pBuf;
pBuf = new char[nBufSize+=ChunkSize];
// try to sprintf
i = _vsnprintf(pBuf,nBufSize,pFmt,args);
}
retval = i;
// epilogue
pString->assign(pBuf,retval);
delete pBuf;
return retval;
};

/* ---

Inline Formatted Print

string strprintf(const char* Format, ...);

Returns :

Formatted string


Effects :

Writes formatted data to a string. formatstr() works the same as sprintf(); see your
documentation for sprintf() for details of operation.

--- */

std::string formatstr(const char * fmt, ...)
{
std::string ret;

va_list args;
va_start(args, fmt);
int retval = vstrprintf(&ret, fmt, args);
va_end(args);
return ret;
}

}; // namespace std


void deal_cards(card * deck, size_t n)
{
static bool seeded = false;
if( !seeded )
{
srand((unsigned)time(0));
seeded = true;
}

for( size_t i = 0; i < n; ++i )
{
for( bool cont = true; cont; )
{
deck[i].rank = (cardrank)int(((double)rand()/(double)RAND_MAX) * 12);
deck[i].suit = (cardsuit)int( 3*((double)rand()/(double)RAND_MAX) );
cont = &deck[i] != std::find(&deck[0],&deck[i],deck[i]);
}
}
}

std::string card_abbrev(const card& c, bool incl_suit)
{
std::string ret;
static const char suitc[] = "cshd";
static const char rankc[] = "23456789TJQKA";
ret += rankc[c.rank];
if( incl_suit ) ret += suitc[c.suit];
return ret;
}

std::string print_hand(card* first, card* last)
{
std::string ret;
for( card* it = first; it != last; ++it )
{
if( it != first ) ret += " ";
ret += card_abbrev(*it,true);
}
return ret;
}

std::string rank_name(int rank)
{
switch( rank )
{
case 12 : return "Ace";
case 11 : return "King";
case 10 : return "Queen";
case 9 : return "Jack";
case 8 : return "Ten";
case 7 : return "Nine";
case 6 : return "Eight";
case 5 : return "Seven";
case 4 : return "Six";
case 3 : return "Five";
case 2 : return "Four";
case 1 : return "Trey";
case 0 : return "Deuce";
default : return "Bug";
}
}

std::string suit_name(cardsuit s)
{
switch( s )
{
case club : return "Clubs";
case spade : return "Spades";
case diamond : return "Diamonds";
case heart : return "Hearts";
default : return "Wands";
}
}

std::string card_name(card c)
{
return std::formatstr("%s of %s", rank_name(c.rank).c_str(), suit_name(c.suit).c_str());
}

std::string card_name(int r, cardsuit s)
{
return std::formatstr("%s of %s", rank_name(r).c_str(), suit_name(s).c_str());
}

std::string score2string(const handval& val)
{
switch( val & 0xF0000000 )
{
case hipk_sflush :
if( (0xf & (val >> 16)) == 12 ) return "Royal Flush";
else return std::formatstr("Straight Flush, %s High",
rank_name((0xf & (val >> 16))).c_str());
case hipk_quads:
return std::formatstr("Four of a Kind, %ss (%s Kicker)",
rank_name((0xf & (val >> 16))).c_str(),
rank_name((0xf & val)).c_str());
case hipk_boat :
return std::formatstr("Full House, %ss Full of %ss",
rank_name((0xf & (val >> 16))).c_str(),
rank_name((0xf & val)).c_str());
case hipk_flush :
return std::formatstr("Flush, %s-high",
rank_name((0xf & (val >> 16))).c_str());
case hipk_straight :
return std::formatstr("Straight, %s-high",
rank_name((0xf & (val >> 16))).c_str());
case hipk_trips :
return std::formatstr("Three of a Kind, %ss (%s-%s Kickers)",
rank_name((0xf & (val >> 16))).c_str(),
rank_name((0xf & (val >> 4))).c_str(),
rank_name((0xf & val)).c_str());
case hipk_2pr :
return std::formatstr("Two Pair, %ss over %ss (%s Kicker)",
rank_name((0xf & (val >> 16))).c_str(),
rank_name((0xf & (val >> 8))).c_str(),
rank_name((0xf & val)).c_str());
case hipk_1pr :
return std::formatstr("One Pair, %ss (%s-%s-%s Kickers)",
rank_name((0xf & (val >> 16))).c_str(),
rank_name((0xf & (val >> 8))).c_str(),
rank_name((0xf & (val >> 4))).c_str(),
rank_name((0xf & val)).c_str());
case hipk_hicard :
return std::formatstr("High Card, %s-%s-%s-%s-%s",
rank_name((0xf & (val >> 16))).c_str(),
rank_name((0xf & (val >> 12))).c_str(),
rank_name((0xf & (val >> 8))).c_str(),
rank_name((0xf & (val >> 4))).c_str(),
rank_name((0xf & val)).c_str());

default : return "Something Wierd!";
}
}

struct deal_block
{
card deal[7];
deal_block(card in[7]) { std::copy(&in[0], &in[7], &deal[0]); }
};

int _tmain(int argc, _TCHAR* argv[])
{
card exp[] = {
{club,7}, {club,6}, {club,5}, {club,4}, {club,3}, {club,2}, {club,1}, // sf
{club,7}, {spade,10}, {club,5}, {heart,10}, {diamond,10}, {club,2}, {club,10},// quads
{heart,12}, {heart,11}, {diamond,11}, {diamond,12}, {spade,12}, {club,3}, {club,2}, // full house
{club,7}, {club,6}, {club,9}, {club,4}, {club,3}, {club,2}, {club,1},// flush
{club,7}, {heart,6}, {spade,5}, {diamond,4}, {club,3}, {club,2}, {club,1},// straight
{heart,12}, {heart,11}, {diamond,11}, {diamond,11}, {spade,9}, {club,3}, {club,2},// trips
{heart,12}, {heart,11}, {diamond,11}, {diamond,12}, {spade,9}, {club,3}, {club,2},//2pair
{heart,12}, {heart,11}, {diamond,10}, {diamond,9}, {spade,7}, {club,3}, {club,2},// pair
{heart,12}, {heart,11}, {diamond,10}, {diamond,9}, {spade,7}, {club,3}, {club,2}, // high card
};

size_t deals = (sizeof(exp)/sizeof(exp[0]))/7;
for( size_t i = 0; i < deals; ++i )
{
size_t ii = 7*i;
handval hv = ScoreHE(&exp[ii]);
std::cout << print_hand(&exp[ii], &exp[ii+7]).c_str() << " -- " << score2string(hv).c_str() << std::endl;
}

for( size_t i = 0; i < deals; ++i )
{
size_t ii = 7*i;
card shuffled[7];
std::copy(&exp[ii], &exp[ii+7], shuffled);
std::random_shuffle(&shuffled[0], &shuffled[7]);
handval hv = ScoreHE(shuffled);
std::cout << print_hand(shuffled, &shuffled[7]).c_str() << " -- " << score2string(hv).c_str() << std::endl;
}
for( int i = 0; i < 10; ++i )
{
card a[7];
size_t na = sizeof(a)/sizeof(a[0]);
deal_cards(a,na);
std::cout << "[" << i+1 << "] : ";
for( size_t j = 0; j < na; ++j ) std::cout << card_abbrev(a[j],true).c_str() << " ";
std::cout << std::endl;
ScoreHE(a);
}

// time 1 million scores
#ifdef _DEBUG
const int trials = 200;
#else
const int trials = 10000000;
#endif
std::cout << "Dealing " << trials << " games..." << std::endl;
std::vector<deal_block> bigdeal;
bigdeal.reserve(trials);
card deal[7];
for( int i = 1; i <= trials; ++i )
{
if( i%(trials/10) == 0 )
std::cout << ".";
deal_cards(deal,7);
bigdeal.push_back(deal);
}

std::cout << std::endl << "Scoring games..." << std::endl;

unsigned long results[9] = {0};
time_t start = time(0);
handval hv;
for( int i = 0; i < trials; ++i )
{
if( i%(trials/10) == 0 )
std::cout << ".";
hv = ScoreHE(bigdeal.at(i).deal);
++results[hv>>28];
}
time_t end = time(0);

std::cout << std::endl << "Elapsed Time: " << (long)end-start;
if( (end-start)>0 ) std::cout << ", Scores per second: " << trials/((long)end-start);
std::cout << std::endl << "Results:" << std::endl
<< "S-Flushes: " << results[8] << "\t (" << 100*results[8]/trials << "%)" << std::endl
<< "Quads: " << results[7] << "\t (" << 100*results[7]/trials << "%)" << std::endl
<< "Full Boats: " << results[6] << "\t (" << 100*results[6]/trials << "%)" << std::endl
<< "Flushes: " << results[5] << "\t (" << 100*results[5]/trials << "%)" << std::endl
<< "Straights: " << results[4] << "\t (" << 100*results[4]/trials << "%)" << std::endl
<< "Trips: " << results[3] << "\t (" << 100*results[3]/trials << "%)" << std::endl
<< "2-Pairs: " << results[2] << "\t (" << 100*results[2]/trials << "%)" << std::endl
<< "1-Pairs: " << results[1] << "\t (" << 100*results[1]/trials << "%)" << std::endl
<< "Hi-Cards: " << results[0] << "\t (" << 100*results[0]/trials << "%)" << std::endl;


return 0;
}




Post Extras: Print Post   Remind Me!   Notify Moderator  
mark007
stranger


Reged: 01/08/07
Posts: 12
Re: 7 Card Hand Evaluators [Re: thomash]
      #8800015 - 01/17/07 03:04 PM

Hand Strength or probability that your hand is ahead / behind depends on the other players cards, aswell as how many players you are against. Even if you just compare your hand versus x random hands, the number x (players playing) has a massive impact on the probabilities so I don't think what you are thinking of doing thomash is possible in this sense.

Post Extras: Print Post   Remind Me!   Notify Moderator  
thomash
stranger


Reged: 01/17/07
Posts: 3
Re: 7 Card Hand Evaluators [Re: mark007]
      #8801160 - 01/17/07 04:11 PM

I am building a bot for two player games only at the moment. So the multiple players aren't an obstacle.

If I did have multiple opponents couldn't I just take the probability to the power of how many players there are and still have fairly good estimate?

Thomas


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: thomash]
      #8802217 - 01/17/07 05:12 PM

That would be a reasonable estimate, but because the opponents hands are independant of each other the numbers will not be exact.

Post Extras: Print Post   Remind Me!   Notify Moderator  
thomash
stranger


Reged: 01/17/07
Posts: 3
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8811516 - 01/18/07 09:32 AM

I assume you mean because opponents hands are NOT independent of each other the numbers will not be exact.

Thomas


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: thomash]
      #8812779 - 01/18/07 11:39 AM

Sorry, my mistake, you are right, because they are not independant it means powering will be incorrect.

I am hopefully going to run some exhaustive analysis on 2 opponents to see exactly how inaccurate powering down will be, although that will entail:

(50 choose 5) * (45 choose 2) * (43 choose 2) = 1,894,107,877,200

game simulations (1.8 trillion), so would take a reasonable amount of time to compute on a desktop PC!


Post Extras: Print Post   Remind Me!   Notify Moderator  
chrisj1948
stranger


Reged: 01/19/07
Posts: 2
Loc: UK
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #8826846 - 01/19/07 12:36 PM

The error introduced by exponentiating from a win probability against a single opponent to generate a probability against several gets even worse if you have set your opponents on a limited range of cards

Just as a quick example. Where the opponents had been set to have handranks in the top 25% a hand which gives you a 30% chance of winning against one opponent drops to 17% against 3 when using a Monte Carlo iterator for the calculation, but is about 3% if you exponentiate.

Regards
Chris


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: mark007]
      #8839948 - 01/20/07 11:42 AM

I ported the table-lookup evaluator introduced in this thread to Java. Below are timings for the C and Java versions for it ("Loookup"); C and Java timings for HandEval, my evaluator; and C timings for Eval_N from pokersource. These results are from a Pentium 4 3.4G processor. The Java runs used Sun's JVM 6; where the -server JVM switch improved the timing it is noted. The Java source and class files are available at http://www.stevebrecher.com/misc/TestEvaluators.jar

Code:
1,000,000 pre-generated random hands, same for each evaluator (but different hands [different RNGs] for C and Java):
C:
HandEval: 0.0310 sec., 108 CPU cycles/hand
Lookup: 0.5780 sec., 1958 CPU cycles/hand
Eval_N 0.0310 sec., 92 CPU cycles/hand
Java:
HandEval: 0.0927 sec. (single-threaded)
HandEval: 0.0924 sec. (dual-threaded, with -server)
Lookup: 0.6097 sec. (single-threaded)
Lookup: 0.4271 sec. (dual-threaded)

52c7 enumeration:
C:
HandEval: 3.2810 sec., 83 CPU cycles/hand
Lookup: 0.6410 sec., 16 CPU cycles/hand
Eval_N: 3.2030 sec., 81 CPU cycles/hand
Java:
HandEval: 5.5252 sec. (single-threaded, with -server)
HandEval: 6.3874 sec. (dual-threaded, with -server)
Lookup: 0.8572 sec. (single-threaded)
Lookup: 1.1239 sec. (dual-threaded, with -server)



Post Extras: Print Post   Remind Me!   Notify Moderator  
Jurrr
Carpal \'Tunnel


Reged: 12/18/06
Posts: 2715
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #8875206 - 01/23/07 07:40 AM

Steve, very much appreciated. Does it work faster than your "old" one?

Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: Jurrr]
      #8882739 - 01/23/07 06:15 PM

Quote:

Steve, very much appreciated. Does it work faster than your "old" one?


Which? The C/Java HandEval? The C version is about 12% faster than the Dec 2006 version than is on my web site; I'll update the latter by and by. The Java version hasn't changed.

Edit, P.S. The 12% is for a particular Hold 'Em Showdown scenario that I've been using for benchmarking.

Edited by Steve Brecher (01/23/07 06:21 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
theblitz
Pooh-Bah


Reged: 09/07/04
Posts: 1920
Loc: Israel
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #9003608 - 02/01/07 05:00 AM

Steve,
I wanted to send you a PM because I need to ask you something about your calculator and would rather not post here.

How can I get in contact with you?


Post Extras: Print Post   Remind Me!   Notify Moderator  
ThirdEye
stranger


Reged: 08/18/06
Posts: 8
Re: 7 Card Hand Evaluators [Re: theblitz]
      #9004174 - 02/01/07 07:03 AM

wow, i just spent two hours working myself through this excellent thread.

i'm working on some pokertools myself using the c# poker-eval port from keith and always thought its "fast enough". the magnitude of improvement you guys worked out here is really amazing.

two thumbs up... great work


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 260 Mill / 343 Mill a sec [Re: ThirdEye]
      #9021895 - 02/02/07 12:48 PM

Made my full lookup table version.
Here's the performance:

Using the following interation method:

date1 = Now

For c1 = 1 To 46
For c2 = c1 + 1 To 47
For c3 = c2 + 1 To 48
For c4 = c3 + 1 To 49
For c5 = c4 + 1 To 50
For c6 = c5 + 1 To 51
For c7 = c6 + 1 To 52

rank = HR(HR(HR(HR(c1 * 140660 - c2 * 2704 + c3 * 52 - c4) + c5) + c6) + c7)

Next c7
Next c6
Next c5
Next c4
Next c3
Next c2
Next c1

date2 = Now

'validate is global variable, outputed to screen
'prevents loop from being optimized out

validate1 = rank

Using the above iteration method takes .515625 seconds, or about 260 million hands a sec. or 3.85e-9 hand per second.

Using a different iteration method....i call the "cheating algorithm" (although its the one used by the latest series of posters) it gets faster speeds by solving part of the lookup process after each for loop (thus lumping together analysis)

date1 = Now

For c1 = 1 To 46
t1 = c1 * 140660
For c2 = c1 + 1 To 47
t2 = t1 - c2 * 2704
For c3 = c2 + 1 To 48
t3 = t2 + c3 * 52
For c4 = c3 + 1 To 49
t4 = HR(t3 - c4)
For c5 = c4 + 1 To 50
t5 = HR(t4 + c5)
For c6 = c5 + 1 To 51
t6 = HR(t5 + c6)
For c7 = c6 + 1 To 52

rank = HR(t6 + c7)
Next c7
Next c6
Next c5
Next c4
Next c3
Next c2
Next c1

date2 = Now

'validate is global variable, outputed to screen
'prevents loop from being optimized out

validate1 = rank

Using the above iteration method takes .390625 seconds, or about 343 million hands a sec. or 2.91e-9 hand per second.

Some things i did differently
1) used visual basic (studio, 2005, professional, .net 2.0)

2) Replaced three tables with integer math. The integer math was faster than looking up variables, table values, and adding results together. More tables can be replaced with integer math, but the memory requirements increase exponentially. (7mb to replace three, 360 mb to replace4, etc.)

3) Put everything into one table. Optimizer went crazy when i did this. Perhaps it didn't have to repeatedly lookup base memory addresses for the array.

Room to improve:

Pointers might be faster. Instead of retrieving a value, and re-inputing this value into the array to get a new memory adress, the table could be optimized so that that values are the new memory address. The relative position of data does not change in an array, and here we have all the data stored in one array.

Downside: Visual basic doesn't let me use pointers. I'm still learning file I/O and some other stuff in C++ so it may be a while before i can try this. But i got pointers down.

P.S. Great job everyone (pats on back), we got some super fast evalutors. Now whose up for starting a game theory AI thread to put these evalutors to use?


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 260 Mill / 343 Mill a sec [Re: _D&L_]
      #9027935 - 02/02/07 07:53 PM

Hi!

Nice Results! Just a quick question about the timings. If I run my optimizer with loops just like this, I get a totally unrealistic speed. I think my optimizer figures out that rank isn't used so why go through all the work. If you put the hand type counter in and display the results, what are your timings? What type/speed computer are you running on? I have a feeling that it would slow down a little bit... It would be interesting to see what your code for VB.Net looks like. How do you feel about showing it to the rest of the world? Personally, I think I will stick with my C code (for now at least!)

Ray


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: 260 Mill / 343 Mill a sec [Re: RayW]
      #9028554 - 02/02/07 08:42 PM

It doesn't get optimized out...i verified that (made that mistake before). Its why i use validate, because it has to use the last rank in the cycle to pass to validate, a global variable, which is outputted to screen. Using a counter, a trivial counter the difference was .51 to .61. The discrepency is that the counter is updated every iteration.

Sure i'll post what i got. Comments in this thread have been valuable, so i don't mind sharing.... I'll post it next msg.


Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: VB source code 1 of 2 [Re: _D&L_]
      #9028593 - 02/02/07 08:46 PM

The following is Post 1 of 2 of my code. This is the current run-time code. The code to make the tables, etc, that I used will be posted next.

Quote:


Public Class Main

Dim date1 As Date
Dim date2 As Date
Dim validate1 As Int32

Private Sub evaluate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles evaluate.Click

Dim interval As TimeSpan

Call determine_winner() 'normal algorithm
'Call determine_winner2() 'cheating algorithm

interval = date2 - date1

output.Text += " " + CStr(interval.TotalSeconds) + " " + validate1.ToString

End Sub





Quote:


Private Sub determine_winner()

Dim HR(37976331) As Int32
Dim c1, c2, c3, c4, c5, c6, c7, rank As Int32
Dim h1, h2, h3, h4, h5, h6, h7, h8, h9 As Int32

FileOpen(6, "C:\Program Files\DevStudio\VB\Poker5\HR.bin", OpenMode.Binary)
FileGet(6, HR, 5)
FileClose(6)

date1 = Now

For c1 = 1 To 46
For c2 = c1 + 1 To 47
For c3 = c2 + 1 To 48
For c4 = c3 + 1 To 49
For c5 = c4 + 1 To 50
For c6 = c5 + 1 To 51
For c7 = c6 + 1 To 52

rank = HR(HR(HR(HR(c1 * 140660 - c2 * 2704 + c3 * 52 - c4) + c5) + c6) + c7)

Select Case rank
Case 0 To 3000
h1 += 1

Case 3001 To 7000
h2 += 1
Case 7001 To 9000
h3 += 1
Case 9001 To 11000
h4 += 1
Case 11001 To 12000
h5 += 1
Case 12001 To 15000
h6 += 1
Case 15001 To 16000
h7 += 1
Case 16001 To 17000
h8 += 1
Case Else
h9 += 1
End Select

Next c7
Next c6
Next c5
Next c4
Next c3
Next c2
Next c1

date2 = Now

'validate is global variable, outputed to screen
'prevents loop from being optimized out

validate1 = h1 + h2 + h3 + h4 + h5 + h6 + h7 + h8 + h9

'Comparison data, check for accuracy
'23294460: hi card
'58627800: one pair
'31433400: 2 pair
'6461620: three kind
'6180020: straight
'4047644: flush
'3473184: full house
'224848: four kind
'41584: straight flush


End Sub





Quote:


Private Sub determine_winner2()

Dim HR(37976331) As Int32
Dim c1, c2, c3, c4, c5, c6, c7, rank As Int32
Dim h1, h2, h3, h4, h5, h6, h7, h8, h9 As Int32
Dim t1, t2, t3, t4, t5, t6 As Int32

'Dim HR(37976331) As Int32

FileOpen(6, "C:\Program Files\DevStudio\VB\Poker5\HR.bin", OpenMode.Binary)
FileGet(6, HR, 5)
FileClose(6)

date1 = Now

For c1 = 1 To 46
t1 = c1 * 140660
For c2 = c1 + 1 To 47
t2 = t1 - c2 * 2704
For c3 = c2 + 1 To 48
t3 = t2 + c3 * 52
For c4 = c3 + 1 To 49
t4 = HR(t3 - c4)
For c5 = c4 + 1 To 50
t5 = HR(t4 + c5)
For c6 = c5 + 1 To 51
t6 = HR(t5 + c6)
For c7 = c6 + 1 To 52

rank = HR(t6 + c7)
Select Case rank
Case 0 To 3000
h1 += 1
Case 3001 To 7000
h2 += 1
Case 7001 To 9000
h3 += 1
Case 9001 To 11000
h4 += 1
Case 11001 To 12000
h5 += 1
Case 12001 To 15000
h6 += 1
Case 15001 To 16000
h7 += 1
Case 16001 To 17000
h8 += 1
Case Else
h9 += 1
End Select

Next c7
Next c6
Next c5
Next c4
Next c3
Next c2
Next c1

date2 = Now

'validate is global variable, outputed to screen
'prevents loop from being optimized out

'validate1 = rank
validate1 = h1 + h2 + h3 + h4 + h5 + h6 + h7 + h8 + h9

'Comparison data, check for accuracy
'23294460: hi card
'58627800: one pair
'31433400: 2 pair
'6461620: three kind
'6180020: straight
'4047644: flush
'3473184: full house
'224848: four kind
'41584: straight flush


End Sub
End Class






Post Extras: Print Post   Remind Me!   Notify Moderator  
_D&L_
member


Reged: 05/11/06
Posts: 128
Re: VB source code 2 of 2 [Re: _D&L_]
      #9029108 - 02/02/07 09:30 PM

Ok, I can't provide all the necessary source code to make the tables. Problem is my iterative process of design. I used my old hand evaluator to process results to create my new one. And the code to make old evalutor is burried in old builds, and is an extremely confusing network of functions. That being said...i'll show u the design process I used. If you have access to any sort of 7 card evaluator, u can sub that for mine, and it may work (u may have to adjust for how card numbers & suits are represnted).


Private Sub Create_tables()


'this is the main function to create all the tables

Dim table1(7314320), table1A(7314320), table2(), table2A(), table2B() As Int32
Dim table3(), table3A(), table3B(), table4(), table4A() As Int32
Dim HR() As Int32

Call Table1_SP(table1A) 'Table1_Starting Points

'New arrays are initalized big, then redimmed to size needed

table2A = New Int32(10000000) {}
Call Table2_UI(table2A) 'Table2_Unique Indexes

table3A = New Int32(10000000) {}
Call Table3_UI(table3A) 'Table2_Unique Index)es

'Finds starting position in table in table 2A*52 = SP in table 2
Table1_main(table1A, table2A, table1)

table2B = New Int32(table2A.GetUpperBound(0) * 52 + 52) {}
Table2_UI_Four_to_five(table2A, table2B)

table2 = New Int32(table2B.GetUpperBound(0)) {}
Table2_Main(table2, table2B, table3A)

table3B = New Int32(table3A.GetUpperBound(0) * 52 + 52) {}
Table3_UI_five_to_six(table3A, table3B)

table4A = New Int32(10000000) {}
Call Table4A_UI(table3B, table4A)

table3 = New Int32(table3B.GetUpperBound(0)) {}
Table3_Main(table3, table3B, table4A)

table4 = New Int32(table4A.GetUpperBound(0) * 52 + 52) {}
Table4_main(table4, table4A)

HR = New Int32() {}
Call supertable(HR, table1, table2, table3, table4)

End Sub




Private Sub Table1_SP(ByRef table1A() As Int32)

Dim c1, c2, c3, c4 As Int32 'counters
Dim Index As Int32
Dim OC(3) As Int32

For c1 = 0 To table1A.GetUpperBound(0)
table1A(c1) = -1
Next c1

For c1 = 1 To 52
For c2 = 1 To 52
For c3 = 1 To 52
For c4 = 1 To 52
Select Case c1
Case c2, c3, c4
Continue For
End Select

Select Case c2
Case c3, c4
Continue For
End Select

Select Case c3
Case c4
Continue For
End Select

Index = c1 * 140660 - c2 * 2704 + c3 * 52 - c4

table1A(Index) = FourCard_Lexograph(New Int32() {c1, c2, c3, c4})

Next c4
Next c3
Next c2
Next c1

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table1A.bin", OpenMode.Binary)
FilePut(7, table1A, 1)
FileClose(7)

End Sub



Private Sub Table1_main(ByRef table1A() As Int32, ByRef table2A() As Int32, ByRef Table1() As Int32)
Dim c1 As Int32
Dim length As Int32
Dim value As Int32

For c1 = 0 To Table1.GetUpperBound(0)
Table1(c1) = -1
Next

'put starting point into table 1
'Link 4 card UIs in table1 and table2, with table 2's index

length = table2A.GetUpperBound(0)
For c1 = 0 To table1A.GetUpperBound(0)
value = table1A(c1)
If value = -1 Then Continue For
Table1(c1) = Search_Array(table2A, length, value) * 52
Next c1

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table1.bin", OpenMode.Binary)
FilePut(7, Table1, 1)
FileClose(7)

End Sub
Private Sub Table2_UI(ByRef Table2A() As Int32)

Dim c1, c2, c3, c4, c5 As Int32 'counters
Dim value As Int32

'''''''' Code to make table 2A (unique Indexes) ''''''''
''' Generate all four card UIs


For c1 = 0 To Table2A.GetUpperBound(0)
Table2A(c1) = -1
Next c1

For c1 = 1 To 49
For c2 = c1 + 1 To 50
For c3 = c2 + 1 To 51
For c4 = c3 + 1 To 52

value = FourCard_Lexograph(New Int32() {c1, c2, c3, c4})

For c5 = 0 To Table2A.GetUpperBound(0)
Select Case Table2A(c5)
Case value
Exit For
Case -1
Table2A(c5) = value
Exit For
End Select
Next c5
Next c4
Next c3
Next c2
Next c1

Array.Sort(Table2A)
Array.Reverse(Table2A)

For c1 = Table2A.GetUpperBound(0) To 0 Step -1
Select Case Table2A(c1)
Case -1
Continue For
Case Else
ReDim Preserve Table2A(c1)
Exit For
End Select
Next

Array.Reverse(Table2A)

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table2A.bin", OpenMode.Binary)
FilePut(7, c1, 1)
FilePut(7, Table2A, 5)
FileClose(7)

End Sub


Private Sub Table2_UI_Four_to_five(ByRef table2A() As Int32, ByRef table2B() As Int32)

''''''Fan out 2As-4UIs, with 5UIs in table 2

Dim c1, c2, h1, h2, h3, h4 As Int32
Dim value As Int32

For c1 = 0 To table2B.GetUpperBound(0)
table2B(c1) = -1
Next

For c1 = 0 To table2A.GetUpperBound(0)
value = table2A(c1)

h1 = (value - 1) \ 283140
value -= h1 * 283140

h2 = (value - 1) \ 4290
value -= h2 * 4290

h3 = (value - 1) \ 65
value -= h3 * 65

h4 = value

For c2 = 1 To 52
Select Case c2
Case h1, h2, h3, h4
Continue For
Case Else
table2B(c1 * 52 + c2) = FiveCard_Lexograph(New Int32() {h1, h2, h3, h4, c2})
End Select
Next c2
Next c1

c1 = table2A.GetUpperBound(0) * 52 + 52

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table2B.bin", OpenMode.Binary)
FilePut(7, c1, 1)
FilePut(7, table2B, 5)
FileClose(7)
End Sub


Private Sub Table2_Main(ByRef Table2() As Int32, ByRef Table2B() As Int32, ByRef table3A() As Int32)

Dim c1 As Int32
Dim length, value As Int32

For c1 = 0 To Table2.GetUpperBound(0)
Table2(c1) = -1
Next

'update table2
length = table3A.GetUpperBound(0)

For c1 = 0 To Table2B.GetUpperBound(0)
If Table2B(c1) = -1 Then Continue For
value = Table2B(c1)
Table2(c1) = Search_Array(table3A, length, value) * 52
Next c1

length = Table2.GetUpperBound(0)

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table2.bin", OpenMode.Binary)
FilePut(7, length, 1)
FilePut(7, Table2, 5)
FileClose(7)

End Sub



Private Sub Table3_UI(ByRef Table3A() As Int32)
Dim c1, c2, c3, c4, c5, c6 As Int32 'counters
Dim value As Int32

For c1 = 0 To Table3A.GetUpperBound(0)
Table3A(c1) = -1
Next c1

''''''' Code to make table 3A - Five Card UIs ''''''''

For c1 = 1 To 48
For c2 = c1 + 1 To 49
For c3 = c2 + 1 To 50
For c4 = c3 + 1 To 51
For c5 = c4 + 1 To 52

value = FiveCard_Lexograph(New Int32() {c1, c2, c3, c4, c5})

For c6 = 0 To Table3A.GetUpperBound(0)
Select Case Table3A(c6)
Case value
Exit For
Case -1
Table3A(c6) = value
Exit For
End Select
Next c6
Next c5
Next c4
Next c3
Next c2
Next c1

Array.Sort(Table3A)
Array.Reverse(Table3A)

For c1 = Table3A.GetUpperBound(0) To 0 Step -1
Select Case Table3A(c1)
Case -1
Continue For
Case Else
ReDim Preserve Table3A(c1)
Exit For
End Select
Next

Array.Reverse(Table3A)

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table3A.bin", OpenMode.Binary)
FilePut(7, c1, 1)
FilePut(7, Table3A, 5)
FileClose(7)

End Sub




Private Sub Table3_UI_five_to_six(ByRef table3A() As Int32, ByRef table3B() As Int32)
Dim c1, c2, h1, h2, h3, h4, h5 As Int32
Dim value As Int32

For c1 = 0 To table3B.GetUpperBound(0)
table3B(c1) = -1
Next c1

For c1 = 0 To table3A.GetUpperBound(0)
value = table3A(c1)

h1 = (value - 1) \ 18687240
value -= h1 * 18687240

h2 = (value - 1) \ 283140
value -= h2 * 283140

h3 = (value - 1) \ 4290
value -= h3 * 4290

h4 = (value - 1) \ 65
value -= h4 * 65

h5 = value

For c2 = 1 To 52
Select Case c2
Case h1, h2, h3, h4, h5
Continue For
Case Else
table3B(c1 * 52 + c2) = SixCard_Lexograph(New Int32() {h1, h2, h3, h4, h5, c2})
End Select
Next c2
Next c1

c1 = table3A.GetUpperBound(0) * 52 + 52

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table3B.bin", OpenMode.Binary)
FilePut(7, c1, 1)
FilePut(7, table3B, 5)
FileClose(7)


End Sub



Private Sub Table3_Main(ByRef table3() As Int32, ByRef table3B() As Int32, ByRef table4A() As Int32)
Dim c1 As Int32
Dim length, value As Int32

For c1 = 0 To table3.GetUpperBound(0)
table3(c1) = -1
Next

'update table2
length = table4A.GetUpperBound(0)

For c1 = 0 To table3B.GetUpperBound(0)
If table3B(c1) = -1 Then Continue For
value = table3B(c1)
table3(c1) = Search_Array(table4A, length, value) * 52
Next c1

length = table3.GetUpperBound(0)

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table3.bin", OpenMode.Binary)
FilePut(7, length, 1)
FilePut(7, table3, 5)
FileClose(7)


End Sub


Private Sub Table4A_UI(ByRef table3B() As Int32, ByRef table4A() As Int32)

Dim c1, c2, value, length As Int32

For c1 = 0 To table4A.GetUpperBound(0)
table4A(c1) = -1
Next c1

c1 = 0
length = table3B.GetUpperBound(0) + 1

For c1 = 0 To table3B.GetUpperBound(0)
value = table3B(c1)
If value = -1 Then Continue For
For c2 = 0 To table4A.GetUpperBound(0)
Select Case table4A(c2)
Case value
Exit For
Case -1
table4A(c2) = value
Exit For
End Select
Next c2
Next c1

Array.Sort(table4A)
Array.Reverse(table4A)

For c1 = table4A.GetUpperBound(0) To 0 Step -1
Select Case table4A(c1)
Case -1
Continue For
Case Else
ReDim Preserve table4A(c1)
Exit For
End Select
Next

Array.Reverse(table4A)

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table4A.bin", OpenMode.Binary)
FilePut(7, c1, 1)
FilePut(7, table4A, 5)
FileClose(7)

End Sub


Private Sub Table4_main(ByRef table4() As Int32, ByRef table4A() As Int32)
Dim c1, c2, h1, h2, h3, h4, h5, h6, h7, suit As Int32
Dim value, index, temp As Int32
Dim OC(5) As Int32
Dim HC(6) As Int32

Dim rank As Int32



For c1 = 0 To table4A.GetUpperBound(0)
value = table4A(c1)
index = c1 * 52

suit = (value - 1) \ 373071582
value -= suit * 373071582

h1 = (value - 1) \ 13817466
value -= h1 * 13817466

h2 = (value - 1) \ 511758
value -= h2 * 511758

h3 = (value - 1) \ 18954
value -= h3 * 18954

h4 = (value - 1) \ 702
value -= h4 * 702

h5 = (value - 1) \ 26
value -= h5 * 26

h6 = value

OC = New Int32() {h1, h2, h3, h4, h5, h6}

For c2 = 0 To 5 'Pass non-suited as clubs, suited as spades
Select Case OC(c2)
Case Is <= 13
OC(c2) = OC(c2) * 4 - 3 'clubs
Case Else
OC(c2) = (OC(c2) - 13) * 4 - 2 'diamond
End Select
Next

'Good luck trying to understand my logic here. Basically i'm tricking my old evaluator. I feed it clubs if the card is not part of a possible flush, and diamonds if it is. I then tell my evaluator to ignore all club flushes.

For c2 = 1 To 52
If SN(c2) <> suit Then temp = (CN(c2) - 1) * 4 - 3 Else temp = (CN(c2) - 1) * 4 - 2 ' club else diamond
rank = determine_winner2(OC(0), OC(1), OC(2), OC(3), OC(4), OC(5), temp)
'If rank = 0 Then rank = rank
table4(index + c2) = rank
Next c2
Next c1


c1 = table4.GetUpperBound(0)
FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\table4.bin", OpenMode.Binary)
FilePut(7, c1, 1)
FilePut(7, table4, 5)
FileClose(7)


End Sub

Private Sub supertable(ByRef HR() As Int32, ByRef table1() As Int32, ByRef table2() As Int32, ByRef table3() As Int32, ByRef table4() As Int32)
Dim length As Int32
Dim shift1, shift2, shift3 As Int32
Dim c1 As Int32

length = table1.GetUpperBound(0) + table2.GetUpperBound(0) + 1 + table3.GetUpperBound(0) + 1 + table4.GetUpperBound(0) + 1

HR = New Int32(length) {}

shift1 += table1.GetUpperBound(0) + 1
For c1 = 0 To table1.GetUpperBound(0)
HR(c1) = table1(c1) + shift1
Next

shift2 = shift1 + table2.GetUpperBound(0) + 1
For c1 = 0 To table2.GetUpperBound(0)
HR(c1 + shift1) = table2(c1) + shift2
Next

shift3 = shift2 + table3.GetUpperBound(0) + 1
For c1 = 0 To table3.GetUpperBound(0)
HR(c1 + shift2) = table3(c1) + shift3
Next

For c1 = 0 To table4.GetUpperBound(0)
HR(c1 + shift3) = table4(c1)
Next

FileOpen(7, "C:\Program Files\DevStudio\VB\Poker5\HR.bin", OpenMode.Binary)
FilePut(7, length, 1)
FilePut(7, HR, 5)
FileClose(7)
End Sub


Private Function FourCard_Lexograph(ByRef OC() As Int32) As Int32

'Don't let the word Lexograph fool you, really these are just unique ID's. I abandoned using lexographs. No need for em.


Select Case SN(OC(0))
Case SN(OC(1)), SN(OC(2)), SN(OC(3))
Case Else
OC(0) = 52 + CN(OC(0)) - 1
End Select

Select Case SN(OC(1))
Case SN(OC(0)), SN(OC(2)), SN(OC(3))
Case Else
OC(1) = 52 + CN(OC(1)) - 1
End Select

Select Case SN(OC(2))
Case SN(OC(0)), SN(OC(1)), SN(OC(3))
Case Else
OC(2) = 52 + CN(OC(2)) - 1
End Select

Select Case SN(OC(3))
Case SN(OC(0)), SN(OC(1)), SN(OC(2))
Case Else
OC(3) = 52 + CN(OC(3)) - 1
End Select

Array.Sort(OC)

FourCard_Lexograph = OC(0) + OC(1) * 65 + OC(2) * 4290 + OC(3) * 283140

End Function



Private Function FiveCard_Lexograph(ByVal OC() As Int32) As Int32

Dim c1 As Int32
Dim c2 As Int32
Dim count As Int32

For c1 = 0 To 4
count = 0

If OC(c1) <= 52 Then
For c2 = 0 To 4
If SN(OC(c1)) = SN(OC(c2)) Then count += 1
Next c2

If count < 3 Then OC(c1) = 52 + CN(OC(c1)) - 1
End If

Next c1

If OC(0) = OC(1) And OC(1) = OC(2) And OC(2) = OC(3) And OC(3) = OC(4) Then FiveCard_Lexograph = -1 : Exit Function

Array.Sort(OC)

FiveCard_Lexograph = OC(0) + OC(1) * 65 + OC(2) * 4290 + OC(3) * 283140 + OC(4) * 18687240

End Function


Private Function SixCard_Lexograph(ByVal OC() As Int32) As Int32

Dim c1 As Int32
Dim c2 As Int32
Dim count As Int32
Dim suit As Int32

For c1 = 1 To 4
count = 0
For c2 = 0 To 5
If SN(OC(c2)) = c1 Then count += 1
Next
If count >= 4 Then suit = c1 : Exit For
Next

If suit > 0 Then
For c1 = 0 To 5
Select Case OC(c1)
Case Is <= 52
If SN(OC(c1)) = suit Then OC(c1) = 13 + CN(OC(c1)) - 1 Else OC(c1) = CN(OC(c1)) - 1
Case Else
OC(c1) -= 52
End Select
Next c1
Else
For c1 = 0 To 5
Select Case OC(c1)
Case Is <= 52
OC(c1) = CN(OC(c1)) - 1
Case Else
OC(c1) -= 52
End Select
Next c1
End If


Array.Sort(OC)

SixCard_Lexograph = OC(0) + OC(1) * 26 + OC(2) * 702 + OC(3) * 18954 + OC(4) * 511758 + OC(5) * 13817466 + suit * 373071582

End Function


Private Function Search_Array(ByRef table() As Int32, ByVal UB1 As Int32, ByVal value As Int32) As Int32

'I'm not a programmer - so i don't know where to look for optimized array search functions, so i wrote my own. I think its pretty efficient, hi speed, for any size array. Keeps dividing the search space by 2 till a solution converges, or returns -1 value is not in the array.

Dim LB1 As Int32

Do
Search_Array = LB1 + (UB1 - LB1) \ 2

If value > table(Search_Array) Then
LB1 = Search_Array
ElseIf value < table(Search_Array) Then
UB1 = Search_Array
Else
Exit Function
End If

If UB1 - LB1 = 1 Then
Select Case value
Case table(LB1)
Search_Array = LB1
Exit Function
Case table(UB1)
Search_Array = UB1
Exit Function
Case Else
Search_Array = -1
Debug.Print("error 3")
Exit Function
End Select
End If
Loop

End Function



Ok the only function I haven't given is the determine winner function, and that is because it calls a bunch of tables created by other functions in my old (read ancient) code. I have the tables in my program because they are stored on hard-drive, but the creation functions are in my old code.

'I admit RayW's code is probably 1000x times easier to follow if u know C++...(i don't btw). Sorry didn't write my code to be well understood, just to create the tables i needed.


Post Extras: Print Post   Remind Me!   Notify Moderator  
theblitz
Pooh-Bah


Reged: 09/07/04
Posts: 1920
Loc: Israel
Re: 7 Card Hand Evaluators [Re: Gullanian]
      #9176734 - 02/14/07 02:54 AM

I just went through this thred again and there seem to be a few additons since I was last here.
I tried to read most of it but might have missed something so don't flame me if I am repeating something.

A large number of threads talk about lookup tables and how big they become. Also, hashing was consider to be too large.
This, however, assumes that the number of possible combinations is 52*51*52....
This is not so.

There are many combinations that are identical. The simplest example is that if all the cards are the same suit then it doesn't matter which one it is.

The reduction in the size of the lookup table would be so large that I think that it would easily offset the extra overhead required to reduce the hand to a unique value.


Post Extras: Print Post   Remind Me!   Notify Moderator  
Gullanian
train time, imo


Reged: 12/21/06
Posts: 1748
Re: 7 Card Hand Evaluators [Re: theblitz]
      #9192087 - 02/15/07 03:57 AM

Pruning data sets down this complex is extremely difficult. You are also looking at significant performance loss. An O(1) lookup into a larger data set will probably be more efficient.

Post Extras: Print Post   Remind Me!   Notify Moderator  
tripos
stranger


Reged: 03/01/07
Posts: 7
Re: 7 Card Hand Evaluators [Re: RayW]
      #9380347 - 03/01/07 12:00 PM

I tried the code Ray posted, thanks to him, and the rest of you, mykey, cactus, jukofyork, gullanian and others for contributions. (Those others specifically do NOT include _D&L_.)

I made the "HandRanks.dat" file. Problem is my machine spends ages loading it. I do:

Code:

#include <fstream>
using namespace std;

int main(void)
{
int HR[32487834];
ifstream inFile ("HandRanks.dat", ios::in | ios::binary);
inFile.read((char*) HR, sizeof(HR));

return 0;
}



It takes about 2 minutes to load, not very encouraging...


Post Extras: Print Post   Remind Me!   Notify Moderator  
twobitplayer
journeyman


Reged: 02/18/07
Posts: 58
Re: 7 Card Hand Evaluators [Re: tripos]
      #9382213 - 03/01/07 02:19 PM

Maybe drop to lower level routines, fread, read, or whatever is the native OS calls for the OS you are using, with an upped buffer size?

Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 7 Card Hand Evaluators [Re: twobitplayer]
      #9382407 - 03/01/07 02:34 PM

Quote:

Maybe drop to lower level routines, fread, read, or whatever is the native OS calls for the OS you are using, with an upped buffer size?



I don't think it will have any effect on speed, but you should be very careful allocating such a big data structure on the stack - it can create random crash bugs caused by stack overflows and sometimes it doesn't hit until you start adding more code and/or port to a new compiler/os:

"int HR[32487834];" should be replaced by either "int* HR=new int[32487834];" or "static int HR[32487834];" to avoid problems.

Linux gcc/g++ is very susceptible to this as IIRC they only allocate an 8MB stack by default.

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
tripos
stranger


Reged: 03/01/07
Posts: 7
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #9407689 - 03/03/07 02:37 PM

Quote:

Maybe drop to lower level routines, fread, read, or whatever is the native OS calls for the OS you are using, with an upped buffer size?




I don't think that will have any effect on speed either. Since the fread(...) function is just C's equivalent of C++'s inFile.read(...) .

I didn't know there could be a problem with the stack, thanks Yuk, I guess, I put the keyword static infront of my array. Not that I noticed any difference but it felt right


Post Extras: Print Post   Remind Me!   Notify Moderator  
tripos
stranger


Reged: 03/01/07
Posts: 7
Re: 7 Card Hand Evaluators [Re: tripos]
      #9414919 - 03/04/07 08:34 AM

Code:

#include "stdio.h"
#include "time.h"

main( )
{
int duration = time(0);

static int HR[32487834];
FILE *fp;
fp = fopen("HandRanks.dat", "r");
fread(HR, sizeof(HR), 1, fp);
fclose(fp);

duration = time(0) - duration;
printf("it took %d seconds\n", duration);
}



Using c source and a c compiler, output is:
it took 115 seconds


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: tripos]
      #9415250 - 03/04/07 10:19 AM

Quote:

Code:

#include "stdio.h"
#include "time.h"

main( )
{
int duration = time(0);

static int HR[32487834];
FILE *fp;
fp = fopen("HandRanks.dat", "r");
fread(HR, sizeof(HR), 1, fp);
fclose(fp);

duration = time(0) - duration;
printf("it took %d seconds\n", duration);
}



Using c source and a c compiler, output is:
it took 115 seconds



Hi,
My routine is very much like this to read in the Handranks. Seems that your PC might be paging memory out to the disk (check to see your free memory). Keep in mind you need about 120MB free for this to work. This is my first guess... I have other thoughts if this isn't your issue...

Ray...


Post Extras: Print Post   Remind Me!   Notify Moderator  
tripos
stranger


Reged: 03/01/07
Posts: 7
Re: 7 Card Hand Evaluators [Re: RayW]
      #9418957 - 03/04/07 04:34 PM


I think I found the problem, I tried running the code on my windows machine at home and this time it took 26 seconds. The other times I've tried it I've used the univesity's linux machines, and I think reading off a remote hard-drive has added some latency to the process. I'm pretty sure the whole file is in memory because when testing all 52C7 it takes a fraction of a second to complete.


Post Extras: Print Post   Remind Me!   Notify Moderator  
tripos
stranger


Reged: 03/01/07
Posts: 7
Re: 7 Card Hand Evaluators [Re: RayW]
      #9460586 - 03/07/07 11:55 AM

Extending the discussion on hand evaluators, the next step is perhaps applications of hand evaluators.

Presenting an example with c++ source below, we pick up Kh9h on the preflop, we ask what the pot equities are against 2c2d, 2c2h, ..., AhAs. There are 1225 opponent combinations.

In the following code all pot equities are calculated and placed into the array "float playerEquities[1326]".

In an opponent modelling scheme, take the opponent probability distribution and multiply with the playerEquities.

SIGMA(w(i) * Eq(i))

That is the sum of all the opponent probability weights with the pot equities. On a random = flat distribution, all the weights are 1/1225, so we can sum the equities and do the division at the end.

You need Ray's "HandRanks.dat" to run the code.
And a file called "functions.cpp", code for this follows


Code:

#include <iostream>
#include <fstream>
#include <ctime>
#include "functions.cpp"

using namespace std;

unsigned int subDeck = 0;
unsigned int topDeck = 0;


int main(void)
{
//******* Read Ray's File
static int HR[32487834];

cout << "loading cards, in some cases be more patient than others..." << endl;
ifstream myFile ("HandRanks.dat", ios::in | ios::binary);
myFile.read((char*) HR, sizeof(HR));
myFile.close();

cout << "loading completed" << endl;

int duration = time(0);

//******** Assign Deck
subDeck = 0xffffffff;
topDeck = 0x000fffff;

//********** This example assigns {Kh, 9h} to the player
int playerCard1 = 46; //Kh
int playerCard2 = 30; //9h
extractCard(playerCard1);
extractCard(playerCard2);

//Save state of deck after player has been dealt.
int subDeckSP1 = subDeck; //SP = Save Point, "1" is 1 player dealt
int topDeckSP1 = topDeck;

//Need to build a 50 card array which contains all the cards except Kh and 9h.
int preFlopUnknowns[50];
buildNaturalArray(preFlopUnknowns);


int combinadic;

//this is where all the equity results are stored
//the lookup index is found from the 2 card combinadic
float playerEquities[1326];
memset(playerEquities, 0, sizeof(playerEquities));

//Entry 1 is constant for this calculation, so:
int entry1 = HR[HR[53 + playerCard1 + 1] + playerCard2 + 1]; //+1 -MOVE INTO RAY SYST.



//Now the strategy is to go through every preflop opponent possibility,
//go through them in combinadics natural order which is
//{0,1}, {0,2}, {1,2}, {0,3}, {1,4}, {2,4}, {3,4}, ... {48, 49}

for (int OC1 = 0; OC1 <= 49; OC1++)
for (int OC0 = 0; OC0 < OC1; OC0++)
{
//here OC0 is always going to represent the larger of the
//two cards



int opponentCard0 = preFlopUnknowns[OC0];
int opponentCard1 = preFlopUnknowns[OC1];

combinadic = (opponentCard1 * (opponentCard1-1)) / 2 + opponentCard0;

int entry2 = HR[HR[53 + opponentCard0 + 1] + opponentCard1 + 1];

cout << "\rcombinadic: " << combinadic;

//Make sure the deck is at the correct Save Point:
subDeck = subDeckSP1;
topDeck = topDeckSP1;
extractCard(opponentCard0);
extractCard(opponentCard1);
//It's called RAY array, because Ray doesn't use my system
//I call my system NATURAL, and I call Ray's system for RAY
int RC[48]; //RC = Remaining Cards
buildRayArray(RC);


int results[1712304];
int c0, c1, c2, c3, c4;
int u0, u1, u2, u3;

int count = 0;

for (c0 = 0; c0 < 48; c0++) {
u0 = HR[entry1+RC[c0]];
for (c1 = c0+1; c1 < 48; c1++) {
u1 = HR[u0+RC[c1]];
for (c2 = c1+1; c2 < 48; c2++) {
u2 = HR[u1+RC[c2]];
for (c3 = c2+1; c3 < 48; c3++) {
u3 = HR[u2+RC[c3]];
for (c4 = c3+1; c4 < 48; c4++) {
results[count++] = HR[u3+RC[c4]];
}
}
}
}
}

count = 0;

for (c0 = 0; c0 < 48; c0++) {
u0 = HR[entry2+RC[c0]];
for (c1 = c0+1; c1 < 48; c1++) {
u1 = HR[u0+RC[c1]];
for (c2 = c1+1; c2 < 48; c2++) {
u2 = HR[u1+RC[c2]];
for (c3 = c2+1; c3 < 48; c3++) {
u3 = HR[u2+RC[c3]];
for (c4 = c3+1; c4 < 48; c4++) {
results[count++] -= HR[u3+RC[c4]];
}
}
}
}
}

int equityCount = 0;
for (int i=0; i<1712304; i++)
{
if (results[i] > 0) equityCount += 2;
if (results[i] == 0) equityCount += 1;
}

float equity = equityCount / 3424608.0;
playerEquities[combinadic] = equity;
}

cout << endl;


duration = time(0) - duration;
cout << "It took " << duration << " seconds" << endl;


//CHECK: Sum the player equities and divide by 50C2 = 1225

float sumOfEquities = 0;

for (int i=0; i<1326; i++)
{
sumOfEquities += playerEquities[i];
}



float meanEquity = sumOfEquities / 1225;

cout << "Kh9h equity against a random hand is: " << meanEquity << endl;


return 0;
}




//***************** functions.cpp

Code:

extern unsigned int subDeck;
extern unsigned int topDeck;


int extractCard(int cardNumber)
{
if (cardNumber < 32)
{
subDeck ^= 1 << cardNumber;
}
else
{
cardNumber -= 32;
topDeck ^= 1 << cardNumber;

}
}



void buildRayArray(int* remainingCards)
{
int positionCounter = 0;

for (int i=0; i<32; i++)
if (subDeck & (1 << i))
remainingCards[positionCounter++] = i + 1;


for (int i=0; i<20; i++)
if (topDeck & (1 << i))
remainingCards[positionCounter++] = i + 32 + 1;
}


void buildNaturalArray(int* remainingCards)
{
int positionCounter = 0;

for (int i=0; i<32; i++)
if (subDeck & (1 << i))
remainingCards[positionCounter++] = i;


for (int i=0; i<20; i++)
if (topDeck & (1 << i))
remainingCards[positionCounter++] = i + 32;
}


/*
* Use below print methods for purposes of testing, for numerical calculations
* only these may be removed.
*
*/



const char cardsNatural[][3] =
{"2c", "2d", "2h", "2s", "3c", "3d", "3h", "3s", "4c", "4d", "4h", "4s", "5c",
"5d", "5h", "5s", "6c", "6d", "6h", "6s", "7c", "7d", "7h", "7s", "8c", "8d",
"8h", "8s", "9c", "9d", "9h", "9s", "Tc", "Td", "Th", "Ts", "Jc", "Jd", "Jh",
"Js", "Qc", "Qd", "Qh", "Qs", "Kc", "Kd", "Kh", "Ks", "Ac", "Ad", "Ah", "As"
};


void printDeck()
{
for (int i=0; i<32; i++)
if (subDeck & (1 << i))
std::cout << cardsNatural[i] << std::endl;



for (int i=0; i<20; i++)
if (topDeck & (1 << i))
std::cout << cardsNatural[i+32] << std::endl;

}




OUTPUT IS:

loading cards, in some cases be more patient than others...
loading completed
combinadic: 1325
It took 45 seconds
Kh9h equity against a random hand is: 0.599885


Post Extras: Print Post   Remind Me!   Notify Moderator  
tripos
stranger


Reged: 03/01/07
Posts: 7
Re: 7 Card Hand Evaluators [Re: tripos]
      #9468104 - 03/07/07 07:57 PM

Sorry for those who are hurting their heads analysing the above, there are two mistakes in the comments.

The natural order of the combinadics should be:
{0,1}, {0,2}, {1,2}, {0,3}, {1,3}, {2,3}, {0.4}, {1,4}, {2,4}, {3,4}, ... {48, 49}

hopefully I got it right this time... let these be ={c0, c1} we can check this by running it throught the combinadics formula for two indexes
(c1 * (c1-1)) / 2 + c0. Should produce

0, 1, 2, 3, 4, 5,..., 1224

Let's see - yes, is good.

This leads me to the second error where I point out just below the double for loop that OC0 is always the largest, it is in fact OC1 which is always the largest. I got it right in the code anyway...

well, enjoy


Post Extras: Print Post   Remind Me!   Notify Moderator  
404url
stranger


Reged: 03/29/07
Posts: 4
Re: 7 Card Hand Evaluators [Re: tripos]
      #9765615 - 03/30/07 01:17 AM

I last visited these forums quite a few months ago. After reading a few of the ideas here I began coding my own evaluator in Java. It was based upon the tabled lookup ideas from the forum. After returning here a little while ago I was amazed at the amount of progress that had been made. Though my algorithm was quite similar to some of the ones presented here it was not nearly as fast by almost a factor of two. In any case, after looking at some of the code I have made some improvements. There was some some unnecessary code in various places. For instance, the "suit iterator" part of Ray's C code was not necessary. It is fine (and also semantically correct) to leave the undefined suit with a value of zero. The bitwise ANDs used in Cactus Kev's evaluator take care of that case. The other changes are apparent upon inspection. In any case, here is a trimmed and somewhat optimized Java version. I placed the Flushes, etc. lookup tables in static arrays in their own wrapper classes. Java's constructor size limitation forced this.
Code:

public class Evaluator {

/* Card to integer conversions:

2c = 1 2d = 14 2h = 27 2s = 40
3c = 2 3d = 15 3h = 28 3s = 41
4c = 3 4d = 16 4h = 29 4s = 42
5c = 4 5d = 17 5h = 30 5s = 43
6c = 5 6d = 18 6h = 31 6s = 44
7c = 6 7d = 19 7h = 32 7s = 45
8c = 7 8d = 20 8h = 33 8s = 46
9c = 8 9d = 21 9h = 34 9s = 47
Tc = 9 Td = 22 Th = 35 Ts = 48
Jc = 10 Jd = 23 Jh = 36 Js = 49
Qc = 11 Qd = 24 Qh = 37 Qs = 50
Kc = 12 Kd = 25 Kh = 38 Ks = 51
Ac = 13 Ad = 26 Ah = 39 As = 52

*/

public final static int NO_SUIT = 0;
public final static int CLUBS = 1;
public final static int DIAMONDS = 2;
public final static int HEARTS = 3;
public final static int SPADES = 4;

public final static int BAD_CARD = -1;
public final static int DEUCE = 2;
public final static int THREE = 3;
public final static int FOUR = 4;
public final static int FIVE = 5;
public final static int SIX = 6;
public final static int SEVEN = 7;
public final static int EIGHT = 8;
public final static int NINE = 9;
public final static int TEN = 10;
public final static int JACK = 11;
public final static int QUEEN = 12;
public final static int KING = 13;
public final static int ACE = 14;

public final static int NUM_SUITS = 4;
public final static int NUM_RANKS = 13;
public final static int NUM_CARDS = 52;

public static int[] handRanks = new int[32487834]; // array to hold hand rank lookup table
public static boolean verbose = true; // toggles verbose mode

private static int[] hand; // re-usable array to hold cards in a hand
private static long[] keys = new long[612978]; // array to hold key lookup table
private static int numKeys = 1; // counter for number of defined keys in key array
private static long maxKey = 0; // holds current maximum key value
private static int numCards = 0; // re-usable counter for number of cards in a hand
private static int cardIndex = 0; // re-usable index for cards in a hands
private static int maxHandRankIndex = 0;

private static long startTimer;
private static long stopTimer;


// Inserts a key into the key array and returns the insertion index.
public static int insertKey(long key) {

// check to see if key is valid
if (key == 0) {
return 0;
}

// short circuit insertion for most common cases
if (key >= maxKey) {
if (key > maxKey) {
keys[numKeys++] = key; // appends the new key to the key array
maxKey = key;
}
return numKeys - 1;
}

// use binary search to find insertion point for new key
int low = -1;
int high = numKeys;
int pivot;
long difference;

while (high - low > 1) {
pivot = (low + high) >>> 1;
difference = keys[pivot] - key;
if (difference > 0) {
high = pivot;
}
else if (difference < 0) {
low = pivot;
}
else {
return pivot; // key already exists
}
}

// key does not exist so must be inserted
System.arraycopy(keys, high, keys, high + 1, numKeys - high);
keys[high] = key;

numKeys++;
return high;

} // END insertKey method



// Returns a key for the hand created by adding a new card to the hand
// represented by the given key. Returns 0 if new card already appears in hand.
private static long makeKey(long baseKey, int newCard) {

int[] suitCount = new int[NUM_SUITS + 1]; // number of times a suit appears in a hand
int[] rankCount = new int[NUM_RANKS + 1]; // number of times a rank appears in a hand
hand = new int[8];

// extract the hand represented by the key value
for (cardIndex = 0; cardIndex < 6; cardIndex++) {

// hand[0] is used to hold the new card
hand[cardIndex + 1] = (int)((baseKey >>> (8 * cardIndex)) & 0xFF);
}

hand[0] = formatCard8bit(newCard);

// examine the hand to determine number of cards and rank/suit counts
for (numCards = 0; hand[numCards] != 0; numCards++) {
suitCount[hand[numCards] & 0xF]++;
rankCount[(hand[numCards] >>> 4) & 0xF]++;

// check to see if new card is already contained in hand (rank and suit considered)
if (numCards != 0 && hand[0] == hand[numCards]) {
return 0;
}
}

// check to see if we already have four of a particular rank
if (numCards > 4) {
for (int rank = 1; rank < 14; rank++) {
if (rankCount[rank] > 4) return 0;
}
}

// determine the minimum number of suits required for a flush to be possible
int minSuitCount = numCards - 2;

// check to see if suit is significant
if (minSuitCount > 1) {
// examine each card in the hand
for (cardIndex = 0; cardIndex < numCards; cardIndex++) {
// if the suit is not significant then strip it from the card
if (suitCount[hand[cardIndex] & 0xF] < minSuitCount) {
hand[cardIndex] &= 0xF0;
}
}
}

sortHand();

long key = 0;
for (int i = 0; i < 7; i++) {
key += (long)hand[i] << (i * 8);
}

return key;

} // END makeKey method



// Formats and returns a card in 8-bit packed representation.
private static int formatCard8bit(int card) {

// 8-Bit Packed Card Representation
// +--------+
// |rrrr--ss|
// +--------+
// r = rank of card (deuce = 1, trey = 2, four = 3, five = 4,..., ace = 13)
// s = suit of card (suits are arbitrary, can take value from 0 to 3)

card--;
return (((card >>> 2) + 1) << 4) + (card & 3) + 1;

} // END formatCard8bit method



// Sorts the hand using Bose-Nelson Sorting Algorithm (N = 7).
private static void sortHand() {
swapCard(0, 4);
swapCard(1, 5);
swapCard(2, 6);
swapCard(0, 2);
swapCard(1, 3);
swapCard(4, 6);
swapCard(2, 4);
swapCard(3, 5);
swapCard(0, 1);
swapCard(2, 3);
swapCard(4, 5);
swapCard(1, 4);
swapCard(3, 6);
swapCard(1, 2);
swapCard(3, 4);
swapCard(5, 6);
} // End sortHand method



// Swaps card i with card j.
private static void swapCard(int i, int j) {
if (hand[i] < hand[j]) {
hand[i] ^= hand[j];
hand[j] ^= hand[i];
hand[i] ^= hand[j];
}
} // END swapCard method



// Determines the relative strength of a hand (the hand is given by its unique key value).
private static int getHandRank(long key) {

// The following method implements a modified version of "Cactus Kev's Five-Card
// Poker Hand Evaluator" to determine the relative strength of two five-card hands.
// Reference: http://www.suffecool.net/poker/evaluator.html

hand = new int[8];
int currentCard;
int rank;
int handRank = 9999;
int holdrank = 9999;
int suit = 0;
int numCards = 0;

final int[] primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};

if (key != 0) {

for (cardIndex = 0; cardIndex < 7; cardIndex++) {

currentCard = (int)((key >>> (8 * cardIndex)) & 0xFF);
if (currentCard == 0) break;
numCards++;

// Cactus Kev Card Representation
// +--------+--------+--------+--------+
// |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
// +--------+--------+--------+--------+
// p = prime number of rank (deuce = 2, trey = 3, four = 5, five = 7,..., ace = 41)
// r = rank of card (deuce = 0, trey = 1, four = 2, five = 3,..., ace = 12)
// cdhs = suit of card
// b = bit turned on depending on rank of card

// extract suit and rank from 8-bit packed representation
rank = (currentCard >>> 4) - 1;
suit = currentCard & 0xF;

// change card representation to Cactus Kev Representation
hand[cardIndex] = primes[rank] | (rank << 8) | (1 << (suit + 11)) | (1 << (16 + rank));
}

switch (numCards) {
case 5 :

holdrank = eval_5hand(hand[0],hand[1],hand[2],hand[3],hand[4]);
break;

case 6 :

// Cactus Kev's Evaluator ranks hands from 1 (Royal Flush) to 7462 (Seven High Card)
holdrank = eval_5hand( hand[0],hand[1],hand[2],hand[3],hand[4]);
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[3],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[3],hand[4],hand[5]));
break;

case 7 :

holdrank = eval_5hand( hand[0],hand[1],hand[2],hand[3],hand[4]);
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[3],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[3],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[4],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[2],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[3],hand[4],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[3],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[1],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[3],hand[4],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[3],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[2],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[0],hand[3],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[3],hand[4],hand[5]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[3],hand[4],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[3],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[2],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[1],hand[3],hand[4],hand[5],hand[6]));
holdrank = Math.min(holdrank, eval_5hand(hand[2],hand[3],hand[4],hand[5],hand[6]));
break;

default :

System.out.println("ERROR: Invalid hand in GetRank method.");
break;

}

// Hand Rank Representation
// +--------+--------+
// |hhhheeee|eeeeeeee|
// +--------+--------+
// h = poker hand (1 = High Card, 2 = One Pair, 3 = Two Pair,..., 9 = Straight Flush)
// e = equivalency class (Rank of equivalency class relative to base hand)

// +-----------------------------------+----------------------------------+-----------------+
// 5-Card Equivalency Classes 7-Card Equivalency Classes
// +-----------------------------------+----------------------------------+-----------------+
// 1277 407 High Card
// 2860 1470 One Pair
// 858 763 Two Pair
// 858 575 Three of a Kind
// 10 10 Straight
// 1277 1277 Flush
// 156 156 Full House
// 156 156 Four of a Kind
// 10 10 Straight Flush
// +----------+------------------------+----------------------------------+-----------------+
// Total: 7462 4824
// +----------+------------------------+----------------------------------+-----------------+

handRank = 7463 - holdrank; // Invert ranking metric (1 is now worst hand)

if (handRank < 1278) handRank = handRank - 0 + 4096 * 1; // High Card
else if (handRank < 4138) handRank = handRank - 1277 + 4096 * 2; // One Pair
else if (handRank < 4996) handRank = handRank - 4137 + 4096 * 3; // Two Pair
else if (handRank < 5854) handRank = handRank - 4995 + 4096 * 4; // Three of a Kind
else if (handRank < 5864) handRank = handRank - 5853 + 4096 * 5; // Straight
else if (handRank < 7141) handRank = handRank - 5863 + 4096 * 6; // Flush
else if (handRank < 7297) handRank = handRank - 7140 + 4096 * 7; // Full House
else if (handRank < 7453) handRank = handRank - 7296 + 4096 * 8; // Four of a Kind
else handRank = handRank - 7452 + 4096 * 9; // Straight Flush

}
return handRank;

} // END getHandRank method



private static int getIndex(int key) {

// use binary search to find key
int low = -1;
int high = 4888;
int pivot;

while (high - low > 1) {
pivot = (low + high) >>> 1;
if (Products.table[pivot] > key) {
high = pivot;
}
else if (Products.table[pivot] < key) {
low = pivot;
}
else {
return pivot;
}
}
return -1;

} // END getIndex method



private static int eval_5hand(int c1, int c2, int c3, int c4, int c5) {
int q = (c1 | c2 | c3 | c4 | c5) >> 16;
short s;

// check for Flushes and Straight Flushes
if ((c1 & c2 & c3 & c4 & c5 & 0xF000) != 0) return Flushes.table[q];

// check for Straights and High Card hands
if ((s = Unique.table[q]) != 0) return s;

q = (c1 & 0xFF) * (c2 & 0xFF) * (c3 & 0xFF) * (c4 & 0xFF) * (c5 & 0xFF);
q = getIndex(q);

return Values.table[q];

} // END eval_5hand method



public static void generateTables() {

int card;
int handRank;
int keyIndex;
long key;

if (verbose) {
System.out.print("\nGenerating and sorting keys...");
startTimer = System.currentTimeMillis();
}

for (keyIndex = 0; keys[keyIndex] != 0 || keyIndex == 0; keyIndex++) {

for (card = 1; card < 53; card++) { // add a card to each previously calculated key
key = makeKey(keys[keyIndex], card); // create the new key

if (numCards < 7) insertKey(key); // insert the new key into the key lookup table
}
}

if (verbose) {
stopTimer = System.currentTimeMillis();
System.out.printf("done.\n\n%35s %d\n", "Number of Keys Generated:", (keyIndex + 1));
System.out.printf("%35s %f seconds\n\n", "Time Required:", ((stopTimer - startTimer) / 1000.0));
System.out.print("Generating hand ranks...");
startTimer = System.currentTimeMillis();
}

for (keyIndex = 0; keys[keyIndex] != 0 || keyIndex == 0; keyIndex++) {

for (card = 1; card < 53; card++) {
key = makeKey(keys[keyIndex], card);

if (numCards < 7) {
handRank = insertKey(key) * 53 + 53; // if number of cards is < 7 insert key
}
else {
handRank = getHandRank(key); // if number of cards is 7 insert hand rank
}

maxHandRankIndex = keyIndex * 53 + card + 53; // calculate hand rank insertion index
handRanks[maxHandRankIndex] = handRank; // populate hand rank lookup table with appropriate value
}

if (numCards == 6 || numCards == 7) {
// insert the hand rank into the hand rank lookup table
handRanks[keyIndex * 53 + 53] = getHandRank(keys[keyIndex]);
}
}

if (verbose) {
stopTimer = System.currentTimeMillis();
System.out.printf("done.\n\n%35s %f seconds\n\n", "Time Required:", ((stopTimer - startTimer) / 1000.0));
}

} // END generateTables method



public static void main (String [] args) {
generateTables();

int c0, c1, c2, c3, c4, c5, c6;
int u0, u1, u2, u3, u4, u5;
int numHands = 0;
int handRank;
int[] handEnumerations = new int[10];
int[][] equivalencyEnumerations = new int[10][3000];
String[] handDescriptions = {"Invalid Hand", "High Card", "One Pair", "Two Pair", "Three of a Kind",
"Straight", "Flush", "Full House", "Four of a Kind", "Straight Flush"};

if (verbose) {
System.out.print("Enumerating hand frequencies and equivalency classes...");
startTimer = System.currentTimeMillis();
}

for (c0 = 1; c0 < 53; c0++) {
u0 = handRanks[53 + c0];
for (c1 = c0 + 1; c1 < 53; c1++) {
u1 = handRanks[u0 + c1];
for (c2 = c1 + 1; c2 < 53; c2++) {
u2 = handRanks[u1 + c2];
for (c3 = c2 + 1; c3 < 53; c3++) {
u3 = handRanks[u2 + c3];
for (c4 = c3 + 1; c4 < 53; c4++) {
u4 = handRanks[u3 + c4];
for (c5 = c4 + 1; c5 < 53; c5++) {
u5 = handRanks[u4 + c5];
for (c6 = c5 + 1; c6 < 53; c6++) {
handRank = handRanks[u5 + c6];
handEnumerations[handRank >>> 12]++;
equivalencyEnumerations[handRank >>> 12][handRank & 0xFFF]++;
numHands++;
}
}
}
}
}
}
}

if (verbose) {
stopTimer = System.currentTimeMillis();
System.out.printf("done.\n\n%35s %f seconds\n\n", "Time Required:", ((stopTimer - startTimer) / 1000.0));
}

System.out.println("SEVEN-CARD POKER HAND FREQUENCIES AND EQUIVALENCY CLASSES\n");
System.out.printf(" %-17s %15s %15s\n", "HAND", "FREQUENCY", "CLASSES");
System.out.println(" -------------------------------------------------");

int sumEquivalency;
int numClasses = 0;
for (int i = handEnumerations.length - 1; i >= 0; i--) {
sumEquivalency = 0;
for (int j = 0; j < equivalencyEnumerations[i].length; j++) {
if (equivalencyEnumerations[i][j] != 0) sumEquivalency++;
}
numClasses += sumEquivalency;
System.out.printf(" %-17s %15d %15d\n", handDescriptions[i], handEnumerations[i], sumEquivalency);
}
System.out.println(" -------------------------------------------------");
System.out.printf(" %-17s %15d %15d\n", "TOTAL", numHands, numClasses);
}

} // END class Evaluator



Post Extras: Print Post   Remind Me!   Notify Moderator  
404url
stranger


Reged: 03/29/07
Posts: 4
Re: 7 Card Hand Evaluators [Re: 404url]
      #9766376 - 03/30/07 02:37 AM

Forgot to mention. I stuck in everything statically so I could post it more easily. If you're going to use it it's probably not a bad idea to alter it so it adheres to OO design.

Post Extras: Print Post   Remind Me!   Notify Moderator  
404url
stranger


Reged: 03/29/07
Posts: 4
Re: 7 Card Hand Evaluators [Re: 404url]
      #9774228 - 03/30/07 05:47 PM

And the top should read:
Code:

2c = 1 2d = 2 2h = 3 2s = 4
3c = 5 3d = 6 3h = 7 3s = 8
4c = 9 4d = 10 4h = 11 4s = 12
5c = 13 5d = 14 5h = 15 5s = 16
6c = 17 6d = 18 6h = 19 6s = 20
7c = 21 7d = 22 7h = 23 7s = 24
8c = 25 8d = 26 8h = 27 8s = 28
9c = 29 9d = 30 9h = 31 9s = 32
Tc = 33 Td = 34 Th = 35 Ts = 36
Jc = 37 Jd = 38 Jh = 39 Js = 40
Qc = 41 Qd = 42 Qh = 43 Qs = 44
Kc = 45 Kd = 46 Kh = 47 Ks = 48
Ac = 49 Ad = 50 Ah = 51 As = 52



Edited by 404url (03/30/07 05:47 PM)


Post Extras: Print Post   Remind Me!   Notify Moderator  
BinaryMan
stranger


Reged: 05/09/07
Posts: 4
Loc: CA, USA
Re: 7 Card Hand Evaluators [Re: 404url]
      #10312098 - 05/10/07 01:01 AM

I have been trying to implement some of the suggestions found here in VB, but I realized that I can never achieve high performance because of the way VB handles array accesses (the SAFEARRAY type protects you, but it also slows the code a bit).

I've tried large tables and hash arrays, but the memory overhead is ridiculous. Using VB classes isn't a solution either because of memory and process overhead (I kind of anticipated this, but tried anyway). While VB makes most of the speed-insensitive coding tasks easier, I'm turning to assembly code to do the hand evaluations in an external library (sort of a compromise).

The way I'm doing it now seems relatively efficient and straightforward, though I'm sure there are more complicated (and faster) ways. Here is the outline for the process:

VB has preprocessed the cards into an array Card(1 to 7). VB passes a pointer to Card(1) to the external optimized library. Each card is represented by a 64-bit structure containing one 16-bit integer per suit as recommended by several people. Bits 0-12 are set for each suit to represent the card ranks contained in the hand. The library can easily address the card data using this format.

The evaluator first uses each 16-bit rank mask in an 8K lookup table to determine if a flush is present. It then combines all four rank masks using the OR operation. A different 8K table lookup determines if the hand is a straight or high card hand. The only hand types that remain undetected so far are rank-count-based (pair, 2 pair, trip, full house, quad).

This is where optimization is critical (at least in my case). I have seen methods of using boolean operations on the rank masks to figure out which ranks are paired, triped, etc. However, there is still additional processing to identify the true hand type and final hand rank. Rather than working with the rank masks in this way, I am trying to find assembly code methods that more directly lead to a table access or hand rank.

Idea: If assembly code can quickly sort the ranks from high-low or vice versa accross all four rank masks, then the table size for a 7-vector rank set lookup is reduced from 60M+ to around 50K. I am testing using the BSR (Bit Scan Reverse; finds the index of the high bit in a register) instruction followed by BTR (Bit Test with Reset) to quickly extract the sorted rank bits. The BSR/BTR instructions only take one cycle on modern processors, and so they seem relatively efficient for this purpose. There are two factors I see that may allow for optimization tricks: (1) Each rank mask contains at most 4 set bits (because a flush has been ruled out in an earlier test). (2) All rank masks together contain at most 6 set bits (because a high card hand was also ruled out).

Problem: The high bits may be in different rank masks and the bits are not implicitly ordered between the masks (only within each individual mask). I can extract all seven card ranks, but they would not always be in order.

Could someone with assembly knowledge tell me how they would solve this problem (or anyone who has an alternative)? It may require a specialized trick. I can get the population count of each rank mask using an 8K table lookup if it helps with the solution. Array memory lookups for Core processors probably aren't too bad. The IMUL instruction if needed for other purposes takes about three cycles. The CMOV instruction takes about one cycle and might avoid some JMPs. If seven bytes (the bit indexes) contained in registers can be sorted/swapped quickly, then it alleviates the problem quite a bit - the hand rank is simply the sorted bytes representing the card ranks (4 bits are needed per card index for the rank; all seven cards will fit in a 32-bit register). The problem might come with the fact that VB code could displace the data cache, so I will assume that the 8K tables will not be resident in the L1 cache, therefor I will not expect maximum performance attained by some people. However, I think I can do much better than 100K comparisons per second (should hit at least several million per second using optimized library code).

Once the hand ranking algorithm is fast enough, I can work on my simulation code trying to get 10 bots at a table to converge on optimal play (most people want maximal play, but in limit poker you can leverage time and probabilities to be profitable with lower risk and bankroll volatility; no-limit poker is different altogether as you have a risk of ruin at Sit-n-Go tables that can't be ignored - that's a separate problem I will tackle later).


Post Extras: Print Post   Remind Me!   Notify Moderator  
advis0r
journeyman


Reged: 04/02/07
Posts: 99
Loc: Southern Germany
Re: 7 Card Hand Evaluators [Re: BinaryMan]
      #10315695 - 05/10/07 10:14 AM

does anyone know a java implementation of an equity calculator for given hands and a board? optional features: allows Ranges like TT+ and AQs+, is fast

i know that there is a poker-eval project, but i have no clue how to compile a DLL that i can call out of java OR use the java native interface to call c functions directly (im too stupid to compile those ;D)

thx in advance


Post Extras: Print Post   Remind Me!   Notify Moderator  
jukofyork
Carpal \'Tunnel


Reged: 09/16/04
Posts: 2551
Loc: Leeds, UK.
Re: 7 Card Hand Evaluators [Re: advis0r]
      #10315732 - 05/10/07 10:19 AM

Quote:

i know that there is a poker-eval project, but i have no clue how to compile a DLL that i can call out of java OR use the java native interface to call c functions directly (im too stupid to compile those ;D)



The JNI is incredibly easy to use and has a very good tutorial to follow. It pretty much writes all the code stubs and creates the DLL project for you - all you have to do is fill in the functionality and then hit built and you're done. If if you are a Java programmer and not a C/C++ programmer, your knowledge of Java syntax and a quick read of some FAQ about the differences should be enough to interface some C/C++ code via the JNI.

Juk


Post Extras: Print Post   Remind Me!   Notify Moderator  
advis0r
journeyman


Reged: 04/02/07
Posts: 99
Loc: Southern Germany
Re: 7 Card Hand Evaluators [Re: jukofyork]
      #10315865 - 05/10/07 10:34 AM

thanks for the reply, i know the JNI isnt that difficult to use (tutorial etc) but the problem is the compilation itself (when i use functions defined in the poker-eval library).

i would spend some hours figuring it all out but first i was gonna ask if something that is ready-to-use is available already

http://svn.gna.org/viewcvs/pokersource/trunk/poker-eval/java/
here are some java to pokereval bindings via JNI but when i tried to get it running real quick i failed (not keen with make scripts).
the java code there also has support for hand ranges etc. which is pretty neat - so if nobody has something for me already, i might invest some time and make it work

(i think it could improve the AllinCalc performance a lot if i dont have to run pokenum.exe and parse its output )


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: advis0r]
      #10317302 - 05/10/07 12:26 PM

Quote:

does anyone know a java implementation of an equity calculator for given hands and a board? optional features: allows Ranges like TT+ and AQs+, is fast


Sorry, no optional features:
http://www.stevebrecher.com/Software/software.html


Post Extras: Print Post   Remind Me!   Notify Moderator  
advis0r
journeyman


Reged: 04/02/07
Posts: 99
Loc: Southern Germany
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #10318884 - 05/10/07 02:27 PM

Hello Steve,

Thanks for your Code
Problem: Your Enumerator Class has Package visibility and thus isnt directly usable for other java programs. I did copy your source into my own files for this little Test:

http://nopaste.byte-welt.de/view.php?id=357

output:
Quote:

WINS: [1140, 186]
TIED: [0, 0]
PaPo: [0.0, 0.0]




while pokerstove and pokenum give me other results:
Quote:

990 games 0.005 secs 198,000 games/sec

Board: 2c 7d Ts
Dead:

equity win tie pots won pots tied
Hand 0: 91.616% 91.62% 00.00% 907 0.00 { AcAs }
Hand 1: 08.384% 08.38% 00.00% 83 0.00 { KdKh }




Quote:

Holdem Hi: 990 enumerated boards containing Ts 2c 7d
cards win %win lose %lose tie %tie EV
As Ac 907 91.62 83 8.38 0 0.00 0.916
Kd Kh 83 8.38 907 91.62 0 0.00 0.084




did I do anything wrong in my little piece of code?
and why do the printed results change if I change the instance and number of thread parameters for Enumerator??


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: advis0r]
      #10319600 - 05/10/07 03:14 PM

Quote:

... I did copy your source into my own files for this little Test:

http://nopaste.byte-welt.de/view.php?id=357

output:
Quote:

WINS: [1140, 186]
TIED: [0, 0]
PaPo: [0.0, 0.0]




while pokerstove and pokenum give me other results:
[...]
did I do anything wrong in my little piece of code?


Sorry, I can't go over your code just now. Here's the output from the code downloaded from my site:
Code:

Known hole cards; two per player: acaskdkh
Number of players with unknown hole cards (0 to 2) [0]:
Known board cards [none]: 2c7dts
Dead/exposed cards [none]:
990 deals required. Start dealing? (y/n) [y]:

AcAs KdKh
% chance of outright win 91.616162 8.383838
% chance of win or split 91.616162 8.383838
expected return, % of pot 91.616162 8.383838
fair pot odds:1 0.091510 10.927711
pots won: 907.00 83.00

Quote:

and why do the printed results change if I change the instance and number of thread parameters for Enumerator??


Because your parameters are "lies"? The parameters must reflect the number of threads; each of n enumeration threads does 1/n of the work. For thread creation and use, see Showdown.java.

Post Extras: Print Post   Remind Me!   Notify Moderator  
advis0r
journeyman


Reged: 04/02/07
Posts: 99
Loc: Southern Germany
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #10322305 - 05/10/07 06:27 PM

thx for the reply...
if you would have taken a quick look at the 22 lines of code i posted, u could have told me earlier that i have to remove the used cards from the deck

nevermind it seems to work well now do you plan to upload a newer version with a public Enumerator class or am i allowed to "copy" this part of your sourcecode ?


Post Extras: Print Post   Remind Me!   Notify Moderator  
Steve Brecher
newbie


Reged: 09/06/04
Posts: 45
Re: 7 Card Hand Evaluators [Re: advis0r]
      #10322369 - 05/10/07 06:32 PM

Quote:

do you plan to upload a newer version with a public Enumerator class or am i allowed to "copy" this part of your sourcecode ?


Copy at will!

Post Extras: Print Post   Remind Me!   Notify Moderator  
advis0r
journeyman


Reged: 04/02/07
Posts: 99
Loc: Southern Germany
Re: 7 Card Hand Evaluators [Re: Steve Brecher]
      #10323682 - 05/10/07 08:22 PM

thank you my program is almost 2 times faster now

Post Extras: Print Post   Remind Me!   Notify Moderator  
StickGuy
stranger


Reged: 05/20/07
Posts: 1
Re: 7 Card Hand Evaluators [Re: advis0r]
      #10448680 - 05/20/07 03:07 PM

The number of unique 6 card IDs can be reduced from about 350,000 to about 280,000 by setting the rank of cards that won't factor into the final hand to 0. If the hand is already a flush, for example, any non-suited cards will be irrelevant. This is the single biggest reduction, but smaller ones can be made for four of a kind, full houses, flushes and straights.

Post Extras: Print Post   Remind Me!   Notify Moderator  
hanhikoski
stranger


Reged: 06/15/07
Posts: 3
Re: 7 Card Hand Evaluators [Re: StickGuy]
      #10837650 - 06/18/07 05:25 PM

The reason to register. Thank you. I'd like to thank the guys at the UofA, poker source developers and the contributors in this thread.

Ran Ray Wotton's nested loop hand type enumeration test (TestHandRank.cpp) on a dual-core 2.00 GHz windows laptop. Compiled as a console application and with Maximize speed and Favor Fast Code compiler options in VS.Net 2005. Got some weird results:

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

Validation seconds = 1.0940
Total HighPrecision Clocks = 3923457
HighPrecision clocks per lookup = 0.029327

This is quite far from what I should be getting. Ideas?


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: hanhikoski]
      #10869843 - 06/20/07 07:06 PM

Hi,
I see that you have the right answers, well that is a good start. Since you have the 2005 version, try to instrument the code and optimize it. That should speed it up. You might want to select the proper CPU and other optimizations in the project settings (I am using the AMD64 dual processor). Honestly getting the 130+ million hands evaled in a second is a great accomplishment in itself!

Ray


Post Extras: Print Post   Remind Me!   Notify Moderator  
bokeh
stranger


Reged: 08/05/07
Posts: 1
Re: 7 Card Hand Evaluators [Re: RayW]
      #11536274 - 08/05/07 04:43 AM

Hi friends, I'm new round here!

I've written an extremely simple and fast 7 card evaluator in PHP which can be seen in action at texas.holdem.poker.probability.cal.culator.org

The evaluator function takes an un-ordered hand array('AH', '5H', '4H', '9C', 'TC', '2D', '3H') runs five very simple lines of code and returns an integer ("1" Royal Flush thru "7414" Nine High "98754" which is the worst hand it is possible to form from 7 cards). There are a couple of simple look-up tables and the RAM footprint is in the region of 4.3 megabytes.

I personally don't know C/C++ but would be very interested in sharing my code with anyone that would like to port it into a CLI in C/C++ along with the simulate function.

My email


Post Extras: Print Post   Remind Me!   Notify Moderator  
Masterplan
newbie


Reged: 08/10/06
Posts: 46
Re: 7 Card Hand Evaluators [Re: RayW]
      #11566092 - 08/07/07 11:52 AM

been trying to get Ray's code to work as part of my Msc project (very useful!)
but am failing horribly.
wirth a few subtle modifications to get it to run on visual studio 2005 i got the HandRankSetup to work fine.

Getting Card IDs!
ID - 612976
Setting HandRanks!
ID - 612976
Number IDs = 612977
maxHR = 32487833
Training seconds = 61.54

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

Validation seconds = 0.6250
Total HighPrecision Clocks = 2225124
HighPrecision clocks per lookup = 0.016632


however, i cannot get TestHandRank to run properly.
i've converted:

FILE * fin = fopen("..\\HandRanks.dat", "rb");

to

FILE * fin;
fopen_s(&fin,"..\\HandRanks.dat", "rb");

but this just doesnt find the file (in the same directory)
so i change the ..\\ to .\\ and i get:


BAD!! = 133784560
High Card = 0
Pair = 0
Two Pair = 0
Three of a Kind = 0
Straight = 0
Flush = 0
Full House = 0
Four of a Kind = 0
Straight Flush = 0
Total Hands = 133784560

Validation seconds = 0.5000
Total HighPrecision Clocks = 1787357
HighPrecision clocks per lookup = 0.013360

which is just entirely confusing.
any ideas anyone?


Post Extras: Print Post   Remind Me!   Notify Moderator  
RayW
stranger


Reged: 03/28/05
Posts: 24
Re: 7 Card Hand Evaluators [Re: Masterplan]
      #11589297 - 08/08/07 06:55 PM