Poker hand evaluator
|
|
Thread rating:  |
Johannes Baagoe - 09 Mar 2010 15:22 GMT Has anybody written a fast Texas hold'em hand evaluator in ECMAScript?
What is wanted is a function, say handEval(hand), which, given a suitable representation of a hand of 7 cards, returns a value that accurately represents the strength of the hand. That is, handEval(hand0) > handEval(hand1) iff hand0 beats hand1, handEval(hand0) == handEval(hand1) iff hand0 and hand1 tie, and handEval(hand0) < handEval(hand1) iff hand1 beats hand0.
It would be nice, too, if the return value could easily indicate what kind of hand it is - high card, pair, two pairs, etc, preferably with further information to break ties within each kind.
And it would be a further bonus if the evaluator could retain most of its state when only a few cards change from one hand to another, in order to gain time when looping through all the possible hands with a given, fixed subset.
The problem has received a lot of attention in other languages, e.g., http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup But the best solutions can hardly be implemented in ECMAScript - a 123 megabyte lookup table is unpractical. On the other hand, Objects or sparse Arrays should be reasonably fast and space efficient, so smaller lookup tables could be fine. How fast can one get?
Any ideas?
(NB: this is Texas hold'em, with 7 cards. Naive assumptions from 5 card poker do not necessarily apply. There may be a flush and a straight, but no straight flush. Also, there may be both 4 of a kind and 3 of a kind, etc. What is wanted is the value of the best 5 cards subset.)
 Signature Johannes
Stevo - 09 Mar 2010 15:26 GMT > Has anybody written a fast Texas hold'em hand evaluator in ECMAScript? So you want people here to help you cheat and take people's money?
Ken Snyder - 09 Mar 2010 20:15 GMT > So you want people here to help you cheat and take people's money? No, he just wants to evaluate which hand wins at the end of the round. e.g. determine that a hand is a flush or 2 pair and that a flush beats 2 pair.
I would write a routine that sorts the cards numerically then checks for each possible hand type. Start with the highest (straight flush) then go down to the lowest (1 pair). Make sure that you count Aces as 14, not 1.
So, you might have a hand that starts out ["2H","4H","9H","10H","14D,"14H","14C"].
Transform that into two arrays: [2,4,9,10,14,14,14], ["H","H","H","H","D","H","C"].
If you have a total of 5 H's, then you have a flush. If you have 5 numbers in sequence, you have a straight, etc.
That would be a good start.
Johannes Baagoe - 10 Mar 2010 02:27 GMT Ken Snyder :
> I would write a routine that sorts the cards numerically Fast solutions tend to avoid sorting - it takes time, and needs to be redone even if only one card changes.
> then checks for each possible hand type. Start with the highest > (straight flush) then go down to the lowest (1 pair). Again, this is hardly optimal, except in terms of being the most natural to code.
> Make sure that you count Aces as 14, not 1. What is usually done is to number the cards from 0 to 51, like this :
2c -> 0 2d -> 1 2h -> 2 2s -> 3 3c -> 4 ... Ah -> 50 As -> 51
That makes extracting the suit and the denomination easy :
suit = card & 3; // 0 -> clubs, 1 -> diamonds, 2 -> hearts, 3 -> spades
(Opinions differ as to the order of suits, which is irrelevant in poker. Old bridge habits dictate my choice.)
denomination = card >> 2; // 0 -> deuce, ..., 12 -> ace
> So, you might have a hand that starts out > ["2H","4H","9H","10H","14D,"14H","14C"]. Or [2, 10, 30, 34, 49, 50, 48].
> Transform that into two arrays: [2,4,9,10,14,14,14], > ["H","H","H","H","D","H","C"]. If one doesn't mind making new Arrays on the fly (I am not sure they come cheap), I think they would be more useful in counting :
[1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 3]
(counts the denominations : deuces, treys, ..., aces)
[1, 1, 5, 0]
(counts the suits : clubs, diamonds, hearts and spades)
> If you have a total of 5 H's, then you have a flush. Actually, I think it would be more efficient to implement the counters as 4 bit wide portions of the 32 bits available for bit manipulation in ES Numbers. I suspect (correct me if I am wrong) that
suitCounts += (1 << ((card & 3) << 2)); // suitCounts is a Number used // as a bitmap and split in // four 4-bit counters
may be faster than
suitCounts[card & 3]++; // suitCounts is an Array
and I am pretty sure that
isFlush = Boolean((suitCounts + 0x00003333) & 0x00008888);
is faster than
isFlush = false; for (i = 0; i < 4; i++) { if (suitCounts[i] >= 5) { isFlush = true; break; } }
> If you have 5 numbers in sequence, you have a straight, etc. Table lookup is probably *much* faster.
It seems, rather surprisingly, that there are "only" 49205 different possible denomination counts for 7 cards, from '0000000000034' (four deuces and a trey - the two other treys get thrown out) to '4300000000000' (four aces and a king - three kings, actually, but only one counts) via things like '0021000001021' (two pairs, queens and treys, fifth card jack). It is quite easy to construct such counts in the same way we counted cards within each suit.
Now, ES's internal hashing mechanism is supposed to be good. Why not simply initialise an Object with 49205 properties, each of which is associated with a number that indicates the hand's strength ?
Something like this :
var handRanks = { '0000000000034' => 0x700001, '0000000000043' => 0x711110, '0000000000124' => 0x700002, '0000000000133' => 0x611100, '0000000000142' => 0x711112, ... '4201000000000' => 0x7ccccb, '4210000000000' => 0x7ccccb, '4300000000000' => 0x7ccccb };
The code would weight about 1.5 MB, uncompressed, but is that prohibitive nowadays ?
A note on the rank format :
The first 4 bits indicate what kind of hand :
0 => high card, 1 => one pair, 2 => two pairs, 3 => three of a kind, 4 => straight, 5 => flush, 6 => full house, 7 => four of a kind, 8 => straight flush
The next 20 bits represent the 5 most significant cards denominations, 4 bits per card, from 0 (deuce) to 0xc (ace).
Altogether, this ensures 1. that stronger hand will always get a higher value than a weaker hand, and 2. that the information can easily be translated into something more palatable to human users.
> That would be a good start. Indeed. I still have to construct the lookup table (by the way, I am not going to do that in ES) and to figure out what to do with flushes, whether straight or not. And, of course, test and time it all, and put it on my site when it is ready.
In the meantime, comments, suggestions and criticism are welcome.
 Signature Johannes
Johannes Baagoe - 10 Mar 2010 04:37 GMT Johannes Baagoe :
> var handRanks = { > '0000000000034' => 0x700001, [quoted text clipped - 9 lines] > '4300000000000' => 0x7ccccb > }; Oops, sorry, it isn't Perl. s/ =>/:/g
 Signature Johannes
Dr J R Stockton - 10 Mar 2010 22:48 GMT In comp.lang.javascript message <O6Sdnaz_cZjrngrWnZ2dnUVZ8rti4p2d@gigane ws.com>, Tue, 9 Mar 2010 20:27:02, Johannes Baagoe <baagoe@baagoe.com> posted:
>Ken Snyder : > >> I would write a routine that sorts the cards numerically > >Fast solutions tend to avoid sorting - it takes time, and needs to >be redone even if only one card changes. Full sorting of N entries takes > o(N) time; removal and correct insertion could need o(log2 N) time.
> ...
>Now, ES's internal hashing mechanism is supposed to be good. Why >not simply initialise an Object with 49205 properties, each of which [quoted text clipped - 4 lines] > var handRanks = { > '0000000000034' => 0x700001,
> ...
> '4300000000000' => 0x7ccccb > }; > >The code would weight about 1.5 MB, uncompressed, but is that >prohibitive nowadays ? Can the entries be computed by under 1.5 MB of deliverable JavaScript that runs in a reasonable time?
It is easy enough to code a page so that some or all of the script on the page will not run when loaded from YOUR, or from any, server. Users will then take, and retain, a copy to use from their local disc, rather than reloading frequently.
Once the table is stable, it can be supplied separately, say as an include file. That should largely remain in cache while you titivate the rest of it all.
 Signature (c) John Stockton, Surrey, UK. ?@merlyn.demon.co.uk Turnpike v6.05 MIME. Web <URL:http://www.merlyn.demon.co.uk/> - FAQish topics, acronyms, & links. Proper <= 4-line sig. separator as above, a line exactly "-- " (RFCs 5536/7) Do not Mail News to me. Before a reply, quote with ">" or "> " (RFCs 5536/7)
Johannes Baagoe - 11 Mar 2010 08:36 GMT Dr J R Stockton :
[Object with 49205 properties; I assumed it had to be computed separately and delivered as a literal]
> Can the entries be computed by under 1.5 MB of deliverable JavaScript > that runs in a reasonable time? Actually, yes, thanks for making me reconsider the point.
The problem is to generate a representation of the 49205 different ways 7 cards can be distributed according to rank - how many deuces, treys, fours, ..., aces.
I had found the answer by a brute-force exploration in C of all possible hands, followed by a sort and elimination of doubles. But there is a much better way: to realise that it is equivalent to figuring out all the ways to accommodate 7 persons in 13 rooms, with the limitation that no more that 4 persons can be in the same.
The following code does that :
function place(n, current, capacity, rooms) { if (n <= 0) { print('[' + rooms + '] ' + ++count); return; }
for (var i = current; i < rooms.length ; i++) { if (rooms[i] < capacity) { rooms[i]++; place(n - 1, i, capacity, rooms); rooms[i]--; } } }
count = 0; place (7, 0, 4, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
This promptly produces the desired sequence, from
[4,3,0,0,0,0,0,0,0,0,0,0,0] 1 [4,2,1,0,0,0,0,0,0,0,0,0,0] 2 [4,2,0,1,0,0,0,0,0,0,0,0,0] 3 [4,2,0,0,1,0,0,0,0,0,0,0,0] 4 [4,2,0,0,0,1,0,0,0,0,0,0,0] 5
to
[0,0,0,0,0,0,0,0,0,0,2,1,4] 49200 [0,0,0,0,0,0,0,0,0,0,1,4,2] 49201 [0,0,0,0,0,0,0,0,0,0,1,3,3] 49202 [0,0,0,0,0,0,0,0,0,0,1,2,4] 49203 [0,0,0,0,0,0,0,0,0,0,0,4,3] 49204 [0,0,0,0,0,0,0,0,0,0,0,3,4] 49205
(If you want to try it in a browser, change the "print" function to something else, say, some text that is incremented and inserted into a <pre> element. I use the standalone console shells js and v8.)
There only remains to compute the hand value for each, assuming than there are no flushes. Even the most naive evaluator should be able to do that in less than a second.
 Signature Johannes
Dr J R Stockton - 11 Mar 2010 23:08 GMT In comp.lang.javascript message <BtadnVz9LeU0NgXWnZ2dnUVZ8lti4p2d@gigane ws.com>, Thu, 11 Mar 2010 02:36:57, Johannes Baagoe <baagoe@baagoe.com> posted:
> ... >The following code does that : [quoted text clipped - 16 lines] > count = 0; > place (7, 0, 4, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); Function place calls itself recursively. Integers n & current keep changing, but have a small range. Argument capacity is a constant. Argument rooms is a multiple answer.
That reminds me of the code for the number of ways of giving change for, say, £5.00 given the available lower denominations, which has a simple recursive expression. That runs very much faster if a cache of the answers to sub-problems is used, as demonstrated in <URL:http://www.merlyn.demon.co.uk/js-misc0.HTM#JAD>, in which array Cash is the cache.
It seems possible, but no more, that a similar trick might be used in your case, if the code needs speeding up. The cached items, indexed by n & current, would be like a new Array(13) (?).
If the displayed code is not compact for you, View Source may be preferred.
 Signature (c) John Stockton, nr London, UK. ?@merlyn.demon.co.uk Turnpike v6.05. Web <URL:http://www.merlyn.demon.co.uk/> - w. FAQish topics, links, acronyms PAS EXE etc : <URL:http://www.merlyn.demon.co.uk/programs/> - see 00index.htm Dates - miscdate.htm estrdate.htm js-dates.htm pas-time.htm critdate.htm etc.
Johannes Baagoe - 12 Mar 2010 12:12 GMT Dr J R Stockton :
> Johannes Baagoe :
>> function place(n, current, capacity, rooms) { >> if (n <= 0) { [quoted text clipped - 10 lines] > } > }
> That reminds me of the code for the number of ways of giving change > for, say, £5.00 given the available lower denominations, which > has a simple recursive expression. Aha ! that sounds like a nice problem.
> That runs very much faster if a cache of the answers to sub-problems > is used, as demonstrated in > <URL:http://www.merlyn.demon.co.uk/js-misc0.HTM#JAD>, in which array > Cash is the cache. Alas, Error 404.
> It seems possible, but no more, that a similar trick might be used > in your case, if the code needs speeding up. The cached items, > indexed by n & current, would be like a new Array(13) (?). It would hardly be worth the trouble, unless I feel the urge to turn my ad hoc place function into something more generally useful. Here, it is just a quick hack to do something I needed. But, as it happens, Scott Sauyet applied it to a slightly different problem, and I applied it to yet another, and that added up to an explanation of a well-known result by "Cactus Kev" - the number of equivalence classes of 7-card poker hands - which looked like magic to me before.
 Signature Johannes
Johannes Baagoe - 12 Mar 2010 12:17 GMT Johannes Baagoe :
> a well-known result by "Cactus Kev" - the number of equivalence classes > of 7-card poker hands Oops - of 5-card poker hands, or simply of poker hands. It is here : http://www.suffecool.net/poker/evaluator.html and here : http://www.suffecool.net/poker/7462.html
 Signature Johannes
Dr J R Stockton - 13 Mar 2010 17:17 GMT In comp.lang.javascript message <w5GdnfwvJY4rsgfWnZ2dnUVZ7oRi4p2d@gigane ws.com>, Fri, 12 Mar 2010 06:12:38, Johannes Baagoe <baagoe@baagoe.com> posted:
>Dr J R Stockton :
>> That reminds me of the code for the number of ways of giving change >> for, say, £5.00 given the available lower denominations, which >> has a simple recursive expression. > >Aha ! that sounds like a nice problem. I mean the number of combinations of coins that may be given, regardless of the order in which they are handed over. I think the modification for the latter would be trivial in my code.
>> That runs very much faster if a cache of the answers to sub-problems >> is used, as demonstrated in >> <URL:http://www.merlyn.demon.co.uk/js-misc0.HTM#JAD>, in which array >> Cash is the cache. > >Alas, Error 404. Typo : HTM too big. Apologies. This works : <http://www.merlyn.demon.co.uk/js-misc0.htm#JAD>. But the 404 text does link to the site index, in which all local links are tested in at least one way, commonly two.
 Signature (c) John Stockton, Surrey, UK. ?@merlyn.demon.co.uk Turnpike v6.05 MIME. Web <URL:http://www.merlyn.demon.co.uk/> - FAQish topics, acronyms, & links. Proper <= 4-line sig. separator as above, a line exactly "-- " (RFCs 5536/7) Do not Mail News to me. Before a reply, quote with ">" or "> " (RFCs 5536/7)
Johannes Baagoe - 15 Mar 2010 21:17 GMT Johannes Baagoe :
[Generating a rather large lookup table for fast poker hand evaluations]
> Even the most naive evaluator should be able to do that in less than > a second. Actually, not quite. With efficient ES implementations like Chrome's v8, it takes a bit more than a second on my laptop with a dual T3200 @ 2.00GHz. With SpiderMonkey / Firefox, it takes more than ten seconds. Opera takes about 5.
But speed gain is spectacular. With v8, I get close to 4 million evaluations per second. Pokersource Poker-Eval's fish program (in heavily macro'ed C) runs at about 16.5 million per second on the same laptop.
The optimised evaluator is like this :
var handValues = computeHandValues(); // lookup table function fastHandEval(card0, card1, card2, card3, card4, card5, card6) { var suitCounts = 0x3333; // four 4-bit counters, one for each suit // all initialised to 3
var key; // a 13-digit number in base 5 to serve as lookup key.
var powersOf5 = [1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, 48828125, 244140625]; // table lookup is faster than Math.pow(5, n)
suitCounts += (1 << ((card0 & 3) << 2)); suitCounts += (1 << ((card1 & 3) << 2)); // ... suitCounts += (1 << ((card6 & 3) << 2));
if (suitCounts & 0x8888) { // there is a flush var flushSuit; // suit of the flush
if (suitCounts & 0x88) { if (suitCounts & 0x8) { flushSuit = 0; } else { flushSuit = 1; } } else { if (suitCounts & 0x800) { flushSuit = 2; } else { flushSuit = 3; } }
key = (card0 & 3) === flushSuit ? powersOf5[(card0 >> 2)] << 1 : 0; // ... key += (card6 & 3) === flushSuit ? powersOf5[(card6 >> 2)] << 1 : 0;
return handValues[key]; }
// no flush key = powersOf5[card0 >> 2]; // ... key += powersOf5[card6 >> 2];
return handValues[key]; }
 Signature Johannes
Lasse Reichstein Nielsen - 16 Mar 2010 07:12 GMT > The optimised evaluator is like this : It does look very finely tuned. I have only a few small suggestions.
> var handValues = computeHandValues(); // lookup table > > function fastHandEval(card0, card1, card2, card3, card4, card5, card6) {
> var powersOf5 = [1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, > 1953125, 9765625, 48828125, 244140625]; You create a new array on each call, and throw it away at the end of the call. If you could create it once and pass it as a parameter, you could save a lot of memory grinding. (Or, you could have it in a global variable, and then read it into a local variable at this point to avoid repeatedly accessing global variables).
> // table lookup is faster than Math.pow(5, n) > if (suitCounts & 0x8888) { // there is a flush > var flushSuit; // suit of the flush > > if (suitCounts & 0x88) { Ingenious :)
> key = (card0 & 3) === flushSuit ? powersOf5[(card0 >> 2)] << 1 : 0; > // ... > key += (card6 & 3) === flushSuit ? powersOf5[(card6 >> 2)] << 1 : 0; Here you could do the << 1 at the end, instead of doing it for each card. I.e., drop it from above and do return handValues[key << 1];
And while we are at it, you could pass handValues as a parameter too, to avoid having it as a global variable. Global variables suck. Alternatively, you could create this function inside the scope of another function that declars handValues and powersOf5, so that the variables are still looked up, but not on the global object.
It might not do much, though, since handValues is only used once per call - but it's worth a try.
/L
 Signature Lasse Reichstein Holst Nielsen 'Javascript frameworks is a disruptive technology'
Johannes Baagoe - 16 Mar 2010 17:58 GMT Lasse Reichstein Nielsen :
> Johannes Baagoe :
>> The optimised evaluator is like this :
> I have only a few small suggestions. Excellent ones, as the tests show. What I use is a rather standard exploration of all the 133 784 560 possible 7-card hands, like this :
function allHands() { var count = 0; var types = [0, 0, 0, 0, 0, 0, 0, 0, 0]; for (var c0 = 0; c0 < 52; c0++) { for (var c1 = c0 + 1; c1 < 52; c1++) { / ... var type = fastHandEval(c0, c1, c2, c3, c4, c5, c6); types[type >> 20]++; count++; /... } } for (var type = 0; type < 9; type++) { print(types[type]); } print(count); }
var now = new Date(); allHands() ; print(new Date() - now + ' ms');
All are with Chrome's v8 engine on a dual T3200 @ 2.00GHz. It is IMHO rather amazing that it is possible to run it at all in a reasonable time in ES. I wouldn't attempt it with Firefox / SpiderMonkey or Opera, though, and I have nothing else. I don't know if anybody is interested enough in trying other platforms to justify that I put the entire code on my website before I have finalised a version that actually does something useful.
Before your suggestions, the test ran in 37343 ms.
>> var powersOf5 = [1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, >> 1953125, 9765625, 48828125, 244140625];
> You create a new array on each call, and throw it away at the end of the > call. If you could create it once and pass it as a parameter, you could > save a lot of memory grinding.
> (Or, you could have it in a global variable, and then read it into a > local variable at this point to avoid repeatedly accessing global > variables). Making it global makes the time drop to 29165 ms. No surprise - I could kick myself for that oversight.
>> key = (card0 & 3) === flushSuit ? powersOf5[(card0 >> 2)] << 1 : 0; >> // ... >> key += (card6 & 3) === flushSuit ? powersOf5[(card6 >> 2)] << 1 : 0;
> Here you could do the << 1 at the end, instead of doing it for each > card. I.e., drop it from above and do > return handValues[key << 1]; Indeed. And what amazes me is how much that improves performance - the time drops to 27499 ms, that is by nearly 6 %. I am at a loss to explain it, since that branch is only supposed to be taken about 3 % of the time, and this factoring out of a shift affects less that half of instructions; I would have expected a marginal improvement of about 1 %, not 6. I repeated the tests a couple of times with both versions, and the ~ 6 % improvement was consistent, but it may still have been a fluke. Do you have a better explanation ?
> Global variables suck. Sure, but avoiding them by the means of closures seems to degrade performance. If I wrap the whole thing in a
var texasHoldemHandEval = (function () { ... return fastHandEval; })();
the time of the test increases again to 32412 ms. I shall try to figure out something better, suggestions are welcome.
On the other hand, the time drops to 26377 ms if I copy powersOf5 on entry. (Copying handValues rather degrades performance a bit, no surprise, since is only called once.) Altogether, your suggestions have improved speed by about 30 %, and the evaluator now reaches more than 5 million evaluations per second ! Not bad for a supposedly slow and inefficient language...
 Signature Johannes
Lasse Reichstein Nielsen - 16 Mar 2010 21:14 GMT ...
> All are with Chrome's v8 engine on a dual T3200 @ 2.00GHz. It is IMHO > rather amazing that it is possible to run it at all in a reasonable > time in ES. I wouldn't attempt it with Firefox / SpiderMonkey or > Opera, though, and I have nothing else. If you have the new Opera 10.50 (or even 10.51rc), you should try it there. They seem to have both good memory management and fast arithmetic, so they should do well.
> Before your suggestions, the test ran in 37343 ms. ...
>>> key = (card0 & 3) === flushSuit ? powersOf5[(card0 >> 2)] << 1 : 0; >>> // ... [quoted text clipped - 12 lines] > versions, and the ~ 6 % improvement was consistent, but it may still > have been a fluke. Do you have a better explanation ? My best guess is code density. You do conditional jumps. If they skip less, they are more likely to hit something that's already in the instruction look-ahead or code cache.
I just noticed that you are adding zeros (although in at most two out of seven cases, since you know you have a flush). How about, for every card except the first, to do: if ((card1 & 3) == flushSuit) key += powersOf5[card6 >> 2]; ... It seems like it should be slightly shorter and faster.
>> Global variables suck. > [quoted text clipped - 7 lines] > > the time of the test increases again to 32412 ms. Probably because the function call must introduce a non-trivial scope.
> I shall try to figure out something better, suggestions are welcome. Have you tried keeping handValues and powersOf5 in the calling function, and passing them as parameters to fastHandEval. I don't know if passing two extra parameters will cost more than reading them from the global scope, but it's worth a try.
Best of luck. /L
 Signature Lasse Reichstein Holst Nielsen 'Javascript frameworks is a disruptive technology'
Johannes Baagoe - 09 Mar 2010 20:23 GMT Stevo :
>> Has anybody written a fast Texas hold'em hand evaluator in >> ECMAScript?
> So you want people here to help you cheat and take people's money? That remark is both rude and stupid. If you knew anything about the subject, you would realise that one cannot cheat people out of their money merely by using a hand evaluator, let alone one in ECMAScript. So, in this particular case, I can actually prove my absence of nefarious motives - but usually one cannot, which is why the presumption of innocence was invented, and why it is appallingly bad manners to question motives instead of discussing the facts.
No, I want to share experiences in ECMAScript programming, which I understand is the purpose of this newsgroup. As it happens, there are quite a few specific problems that arise. Just one example:
Most of the time, in order to evaluate a hand, it suffices to keep track of the number of cards in each suit, and of the number of cards of each denomination - how many aces, kings, etc.
But in one relatively rare case - when there are 5 cards or more in one suit - this is not enough; one has to know which denominations are in that suit. So, one has to keep track of the exact cards, e.g. by pushing them on a stack, or by setting a bit for each in a 52-bit mask, which may be faster, and which is what languages with 64-bit integers usually do.
Now, ECMAScript has no 64-bit integers, but it has Numbers with a 52-bit mantissa, which happens to be just enough. Using it could well be the fastest way of *saving* the information as it comes, and while *retrieving* it later may be something of a PINA, it would only occur in about 3 % of the cases. (4089228 times out of 133784560, to be exact.) It seems to be worth exploring. Nobody in his right mind would do that in C, but ECMAScript is different.
 Signature Johannes
Dr J R Stockton - 10 Mar 2010 18:45 GMT In comp.lang.javascript message <O6Sdna3_cZimMwvWnZ2dnUVZ8rti4p2d@gigane ws.com>, Tue, 9 Mar 2010 14:23:23, Johannes Baagoe <baagoe@baagoe.com> posted:
> ...
>Now, ECMAScript has no 64-bit integers, but it has Numbers with a >52-bit mantissa, which happens to be just enough. Using it could [quoted text clipped - 3 lines] >to be exact.) It seems to be worth exploring. Nobody in his right >mind would do that in C, but ECMAScript is different. FWIW, the mantissa can be considered as 53 bits (all integer values from 0 to 2^53-1 store exactly), and there is a sign bit too.
As an alternative to using Number, a four-character String cam be considered, with some handling difficulties, as a 4 element array of 16-bit unsigned integers (4 suits of 13 cards); an 8-character String can store 8 bytes, etc. Probably slower, though.
I have code in <URL:http://www.merlyn.demon.co.uk/js-misc0.htm> for converting a Number to its 64 bits and /vice versa/.
 Signature (c) John Stockton, nr London UK. ?@merlyn.demon.co.uk BP7, Delphi 3 & 2006. <URL:http://www.merlyn.demon.co.uk/> TP/BP/Delphi/&c., FAQqy topics & links; <URL:http://www.bancoems.com/CompLangPascalDelphiMisc-MiniFAQ.htm> clpdmFAQ; NOT <URL:http://support.codegear.com/newsgroups/>: news:borland.* Guidelines
Scott Sauyet - 09 Mar 2010 21:45 GMT > Has anybody written a fast Texas hold'em hand evaluator in ECMAScript? I never have, and would not choose ES to code one without good reason.
If issues of speed are not that important, this would not be hard to code, either in the compare-two-hands version or the score-a-hand version.
But it sounds like you're looking for something optimized for speed. I don't think many ES implementation are optimized for numeric calculations of that sort.
> (NB: this is Texas hold'em, with 7 cards. Naive assumptions from 5 card > poker do not necessarily apply. There may be a flush and a straight, > but no straight flush. Also, there may be both 4 of a kind and 3 of > a kind, etc. What is wanted is the value of the best 5 cards subset.) There are only twenty-one 5-card hands that can be made from a 7-card hand, so it would be reasonable to simply do a maximum score on those sub-hands to make a 7-card hand evaluator out of a 5-card hand one. But again, this means 21 runs of the 5-card hand evaluator, clearly not a technique optimized for speed.
-- Scott
Johannes Baagoe - 10 Mar 2010 05:26 GMT Scott Sauyet :
> Johannes Baagoe :
>> Has anybody written a fast Texas hold'em hand evaluator in >> ECMAScript?
> I never have, and would not choose ES to code one without good reason. I have no better reason than a taste for challenges.
> If issues of speed are not that important, this would not be hard > to code, either in the compare-two-hands version or the score-a-hand > version. I thought so, too, until I tried :) It is a lot trickier with 7 cards than with 5. While my present focus is on speed, I would still love to see a short and truly elegant solution, even if it is slow. I would love to write one, for that matter, but I am not satisfied by any of my efforts so far.
> But it sounds like you're looking for something optimized for > speed. I don't think many ES implementation are optimized for numeric > calculations of that sort. Quite, but how much speed can we still tweak our of them ?
> There are only twenty-one 5-card hands that can be made from a > 7-card hand, so it would be reasonable to simply do a maximum score > on those sub-hands to make a 7-card hand evaluator out of a 5-card > hand one. But again, this means 21 runs of the 5-card hand evaluator, > clearly not a technique optimized for speed. No, and besides, I would consider *that* to be cheating.
 Signature Johannes
Scott Sauyet - 10 Mar 2010 20:12 GMT > Scott Sauyet : >> Johannes Baagoe : [quoted text clipped - 3 lines] > > I have no better reason than a taste for challenges. That's a good enough reason for me! There are definitely some interesting questions about appropriate representation in JS, where 52- bit structures are not easy to manage and where bit manipulations are generally not at all optimized.
Maybe if I find some time over the weekend, I'll see what I can create.
>> If issues of speed are not that important, this would not be hard >> to code, either in the compare-two-hands version or the score-a-hand [quoted text clipped - 5 lines] > love to write one, for that matter, but I am not satisfied by any of > my efforts so far. It seems to me that any obvious solution will end up doing one of two things. Either it will score each 5-card sub-hand of the 7 cards you have, or it will successively search for 5-card hands for each score type starting with straight-flush and working down to 7-high.
>> But it sounds like you're looking for something optimized for >> speed. I don't think many ES implementation are optimized for numeric >> calculations of that sort. > > Quite, but how much speed can we still tweak our of them ? I don't know. Let's see, shall we?
>> There are only twenty-one 5-card hands that can be made from a >> 7-card hand, so it would be reasonable to simply do a maximum score [quoted text clipped - 3 lines] > > No, and besides, I would consider *that* to be cheating. Why is that? Granted, these two are distinct tasks:
(1) Find the value of the best 5-card hand that can be made from these seven cards. (2) Find the best 5-card hand that can be made from these seven cards.
But one of the most straightforward ways to do the former involves the latter as an intermediate step. And I think most solutions will have an abstract version of the latter embedded in them. I'd love to see a purely mathematical version which doesn't do this in practice, but I'm not holding my breath. What sorts of systems are you imagining that don't in some sense find a five-card hand to compare?
-- Scott
Johannes Baagoe - 11 Mar 2010 09:51 GMT Scott Sauyet :
> There are definitely some interesting questions about appropriate > representation in JS, where 52- bit structures are not easy to manage > and where bit manipulations are generally not at all optimized. I agree.
> Maybe if I find some time over the weekend, I'll see what I can > create. That would be nice.
>> how much speed can we still tweak our of them ?
> I don't know. Let's see, shall we? I'm all in :)
> these two are distinct tasks:
> (1) Find the value of the best 5-card hand that can be made from > these seven cards. (2) Find the best 5-card hand that can be made > from these seven cards.
> But one of the most straightforward ways to do the former involves the > latter as an intermediate step. And I think most solutions will have > an abstract version of the latter embedded in them. I'd love to see > a purely mathematical version which doesn't do this in practice, but > I'm not holding my breath. What sorts of systems are you imagining > that don't in some sense find a five-card hand to compare? They do obviously find a five-card hand, the best one of the 21 possibilities, but not simply by generating the 21 permutations and evaluating them all. A rough outline:
First, a pass over the seven cards count the number of cards in each rank and each suit, and keeps a track of the exact seven cards in case one suit has five cards or more, which means a flush.
If there is a flush, all cards of other suits are meaningless - with only seven cards, there is no way there could also be a quad or a full house, and a flush beats anything else. So the only thing to do is to find the 5 highest cards in that suit, and to check whether there is an uninterrupted run of five consecutive ranks, making a straight flush. All that takes is pass through the rank counts, starting from the top, that is, the aces.
If there is no flush (about 97% of all cases), the only thing that matters is the number of cards by rank. A provisional version of the algorithm steps through the rank counts starting from the aces and keeps count of all the information that is needed - similar to what is done for the flushes, but rather more involved. The final, optimised version simply looks in a big table which has been built by the provisional version and returns the result.
 Signature Johannes
Scott Sauyet - 11 Mar 2010 15:23 GMT > Scott Sauyet : >> these two are distinct tasks: [quoted text clipped - 11 lines] > possibilities, but not simply by generating the 21 permutations and > evaluating them all. A rough outline: I love how quickly I can be proven wrong! :-)
> First, a pass over the seven cards count the number of cards in each > rank and each suit, and keeps a track of the exact seven cards in [quoted text clipped - 3 lines] > only seven cards, there is no way there could also be a quad or a full > house, and a flush beats anything else. Ahh, this is a point I had missed.
> So the only thing to do is > to find the 5 highest cards in that suit, and to check whether there > is an uninterrupted run of five consecutive ranks, making a straight > flush. All that takes is pass through the rank counts, starting from > the top, that is, the aces. Well, you'd have to check for the straight before selecting the highest ones, and you'd have to have already removed non-flush cards from the rank count, but yes, this makes sense.
> If there is no flush (about 97% of all cases), the only thing that > matters is the number of cards by rank. A provisional version of [quoted text clipped - 3 lines] > optimised version simply looks in a big table which has been built > by the provisional version and returns the result. And presumably each rating would be fast enough that the startup time to calculate the table (if it's not actually loaded as code or simple data) would be fast enough to not matter in any reasonable system. Then the per-hand calculation would amount to the flush-check, and the creation of the lookup key -- a list of rank counts. It's an interesting approach. I'd love to see how it compares to the naive check-all-combinations approach (with of course a similar table creation for the 6175 different rank counts on five cards.)
I hope I have time to play with this over the weekend. Thanks for posing the problem.
-- Scott
Johannes Baagoe - 11 Mar 2010 17:17 GMT Scott Sauyet :
> Johannes Baagoe : [Determining the ranks of the five most significant cards in a flush, and whether it is a straight flush]
>> So the only thing to do is to find the 5 highest cards in that >> suit, and to check whether there is an uninterrupted run of five >> consecutive ranks, making a straight flush. All that takes is pass >> through the rank counts, starting from the top, that is, the aces.
> Well, you'd have to check for the straight before selecting the > highest ones, and you'd have to have already removed non-flush cards > from the rank counter Not necessarily.
Instead of removing non-flush cards first, I check for each rank with a non-zero count whether the card with that rank and the right suit is present in the hand. If not, I treat that rank as if it had no cards at all.
And instead of checking for straights first, I do it while pushing cards on an array in case I don't find one - chances are that they will end up being the relevant information. If I find a straight, it is easy enough to take its (consecutive) ranks instead.
The same kind of reasoning also applies to non-flush hands in the provisional evaluator. While I walk down the ranks, I push single cards, pairs (there may be three of them !), trips (could be two) and quads on separate arrays - and I also keep count of run lengths. At the end of the walk, it is a simple matter of choosing from which arrays to unshift a total of five card ranks.
Mind you, this is mainly for the sake of elegance. That evaluator is only going to be used about 55,000 times at startup, in order to build the lookup table. It needs not be blazingly fast.
> the per-hand calculation would amount to the flush-check, Not even that - I found since that flushes could also be incorporated in the lookup table without making it too big. Actually, when I have finished the ES version, I shall have a go at a C version with a hash table - it could be small enough to reside entirely in the cache memory of many recent processors, which should make it *very* fast.
> It's an interesting approach. I'd love to see how it compares to > the naive check-all-combinations approach I should be very surprised if it were not at least ten times faster. But it depends a lot on how efficient the internal hash functions of ES implementations are.
> (with of course a similar table creation for the 6175 different rank > counts on five cards.) 6175 ? http://www.suffecool.net/poker/evaluator.html says 7462, but I haven't checked. The same source says 4824 for 7-card hands, choosing best five.
> I hope I have time to play with this over the weekend. I'll try to stop spoiling till then :)
> Thanks for posing the problem. You're welcome.
 Signature Johannes
Scott Sauyet - 11 Mar 2010 17:55 GMT > Scott Sauyet : > [quoted text clipped - 29 lines] > the end of the walk, it is a simple matter of choosing from which > arrays to unshift a total of five card ranks. So if you're walking [K, 8, 7, 6, 5, 4, 2], you're tracking the run length and hit a run of 5 when you reach the 4. Do you stop there? Obviously you don't need to know any more, except about the flush. But have you pushed the initial cards onto a another array? Or do you reuse this original one if you end up with nothing better than King- high?
> Mind you, this is mainly for the sake of elegance. That evaluator > is only going to be used about 55,000 times at startup, in order to > build the lookup table. It needs not be blazingly fast.
>> the per-hand calculation would amount to the flush-check, > [quoted text clipped - 3 lines] > hash table - it could be small enough to reside entirely in the cache > memory of many recent processors, which should make it *very* fast. How do you include them in the table? I was assuming that there would be an initial check for a flush, then a isFlush boolean would be passed to the lookup. Do you have a technique that skips that initial check?
>> It's an interesting approach. I'd love to see how it compares to >> the naive check-all-combinations approach > > I should be very surprised if it were not at least ten times faster. > But it depends a lot on how efficient the internal hash functions > of ES implementations are. Since the entire object design of ES is based upon hash lookups, I'm guessing that they are relatively optimized.
>> (with of course a similar table creation for the 6175 different rank >> counts on five cards.) > > 6175 ?http://www.suffecool.net/poker/evaluator.htmlsays 7462, > but I haven't checked. The same source says 4824 for 7-card hands, > choosing best five. Cactus Kev's numbers are for the number of distinct hand value classes, from. four of the two million+ possible hands have the highest values of royal flushes.down to 1020 possible non-flush [7, 5, 4, 3, 2]-hands. I'm discussing only the counts of the ranks, i.e. the results of
place (5, 0, 4, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
with the function you supplied earlier.
>> I hope I have time to play with this over the weekend. > > I'll try to stop spoiling till then :) Well my thinking has so far been in an entirely different direction, so...
Johannes Baagoe - 12 Mar 2010 03:59 GMT Scott Sauyet :
> So if you're walking [K, 8, 7, 6, 5, 4, 2], you're tracking the > run length and hit a run of 5 when you reach the 4. Do you stop > there? Obviously you don't need to know any more, except about the > flush. But have you pushed the initial cards onto a another array?
> Or do you reuse this original one if you end up with nothing better > than King- high? Actually, what I am walking through is the *rank counts*, O ace, 1 king, 0 queen, etc: [0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1]. Not the 7 cards.
And while I walk through them, I maintain an array of five arrays: one for runs (it is reset to [] whenever I encounter a gap unless it has a length of five, otherwise, the current rank is pushed on it), one for high cards (whenever the count is 1, the current rank is pushed on it), one for pairs (whenever the count is 2, the current rank is pushed on it *twice*, etc.
Here, we are talking of the first, somewhat naive version of the evaluator. It will only be executed 53,924 times - speed matters less than clarity, especially since its overall structure helps to explain how the second version it is used to create, the one with the table lookup, is going to work. In both versions, we first determine if there is a flush, and then, we exploit the rank counts in two different ways according to whether there is one or not. The complexity of those treatments in the first version shows the merit of replacing them by a simple table lookup in the second.
But that argument is worthless if the first version is complicated and messy. Therefore, I aim to make it as simple and elegant as I can. So, instead of complicated ad hoc mechanisms for each of the cases, I systematically use my array of arrays - it simplifies the code. At the end of the walk, I shall use the 5 arrays as FIFOs and shift from them, in the appropriate order according to which ones have a non-zero length, until I have the 5 most significant ranks.
Of course, I could stop the walk before reaching the end in many cases, like the one you mention. Or at least stop pushing on some of the arrays. But I believe the rather complicated tests it would entail would do more harm than good - certainly from the point of view of clarity, and perhaps from the point of view of performance as well. Such tests often cost more than the slight gain they give in comparatively rare cases. Premature optimisation, and all that.
>> I found since that flushes could also be incorporated >> in the lookup table without making it too big.
> How do you include them in the table? I was assuming that there > would be an initial check for a flush, then a isFlush boolean would > be passed to the lookup. Do you have a technique that skips that > initial check? No, it does not skip the initial check, even if I use a more efficient means in the optimised version. But after that check, I construct a 31-bit "signature" of the 13 rank counts that is slightly different in the two cases. If there is no flush, I simply code the counts as a 13-digit number in base 5. If there is a flush, the only thing that matters is the presence or absence of a card in the right suit for each of the ranks. So, I code the ranks where it is present with a digit of 2, and those where it is not with 0, still giving a 13-digit number in base 5. Since there are at least 5 places where it is present in the case of a flush, the sum of the digits is at least 10, whereas in the case of no flush, that sum is always 7. Therefore, there can be no collisions. The table doesn't care about the meaning of the signature, it is just an access key. Incorporating the flushes increases the number of entries by less than 10 %, from 49205 to 53924 - probably well worth the simplification in logic.
>>> It's an interesting approach. I'd love to see how it compares >>> to the naive check-all-combinations approach
>> I should be very surprised if it were not at least ten times faster. >> But it depends a lot on how efficient the internal hash functions >> of ES implementations are.
> Since the entire object design of ES is based upon hash lookups, > I'm guessing that they are relatively optimized. That is what I rely on. We shall see.
>>> (with of course a similar table creation for the 6175 different >>> rank counts on five cards.)
>> 6175 ? http://www.suffecool.net/poker/evaluator.html says 7462, >> but I haven't checked. The same source says 4824 for 7-card hands, >> choosing best five.
> Cactus Kev's numbers are for the number of distinct hand value > classes, from. four of the two million+ possible hands have the > highest values of royal flushes.down to 1020 possible non-flush [7, > 5, 4, 3, 2]-hands. I'm discussing only the counts of the ranks, > i.e. the results of
> place (5, 0, 4, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
> with the function you supplied earlier. Aha ! And place(5, 0, 1, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) which is appropriate for flushes yields 1287, and 6175 + 1287 = 7462, giving Cactus Kev's result. It makes sense :)
 Signature Johannes
Lasse Reichstein Nielsen - 11 Mar 2010 17:42 GMT > They do obviously find a five-card hand, the best one of the 21 > possibilities, but not simply by generating the 21 permutations and [quoted text clipped - 3 lines] > rank and each suit, and keeps a track of the exact seven cards in > case one suit has five cards or more, which means a flush. I have wrapped up a quick attempt at a value function at <URL:http://www.infimum.dk/private/pokerhand.js> Not tested that well yet (removeCard in particular), but seems to correctly evaluate the simple cases I have manually thrown at it.
I keep track of the card values in each suit, as a bitmap, and the count of cards in each suit (to detect a flush).
> If there is a flush, all cards of other suits are meaningless - with > only seven cards, there is no way there could also be a quad or a full [quoted text clipped - 3 lines] > flush. All that takes is pass through the rank counts, starting from > the top, that is, the aces. If you have a bit-vector of cards in the flsuh suit (e.g., by keeping one for each suit), it's only a 269 element lookup table to see if there is a straight. That's fairly easy (otherwise, you can do a little bit fiddling to find the straight). I haven't used the table.
> If there is no flush (about 97% of all cases), the only thing that > matters is the number of cards by rank. A provisional version of [quoted text clipped - 3 lines] > optimised version simply looks in a big table which has been built > by the provisional version and returns the result. It seems that the ~49k entries can be seen as numbers in base 5 as well as strings. All the values are actually valid 32-bit (signed) numbers (four aces and two or more kings is sadly not a valid signed 31 bit integer).
I have kept a sorted list of counts of each rank present, sorted by count then rank. It's at most seven entries, so I haven't done anything fancy to keep them in order (like a red/black binary tree or similar).
/L
 Signature Lasse Reichstein Holst Nielsen 'Javascript frameworks is a disruptive technology'
Scott Sauyet - 11 Mar 2010 18:37 GMT > I have wrapped up a quick attempt at a value function at > <URL:http://www.infimum.dk/private/pokerhand.js> I get a 404 on that script.
-- Scott
Lasse Reichstein Nielsen - 11 Mar 2010 19:50 GMT >> I have wrapped up a quick attempt at a value function at >> <URL:http://www.infimum.dk/private/pokerhand.js> > > I get a 404 on that script. Argh, typical typo for me: <URL:http://www.infimum.dk/privat/pokerhand.js> Not the first time that particular error has been made.
/L
 Signature Lasse Reichstein Holst Nielsen 'Javascript frameworks is a disruptive technology'
Johannes Baagoe - 12 Mar 2010 05:55 GMT Lasse Reichstein Nielsen :
> I have wrapped up a quick attempt at a value function at > <URL:http://www.infimum.dk/private/pokerhand.js> For the benefit of other readers: <URL:http://www.infimum.dk/privat/pokerhand.js>, as you corrected, .
> Not tested that well yet (removeCard in particular), but seems to > correctly evaluate the simple cases I have manually thrown at it. I like the way you wrap everything nicely in a constructor, which is clearly the way to go.
I also like the idea of a removeCard function, if it can be made significantly faster than simply starting over again. The big problem I encounter is when removing a card (or replacing it) changes the status from a flush being present to no flush being present.
> I keep track of the card values in each suit, as a bitmap, Same for me in the optimised version of the evaluator. In the naive version I use an Object: isPresent[card] == true iff card (a number in 0 .. 51) is in the hand. (The naive version is used to build the table that drives the optimised version. It can also serve to measure just how much is gained - or not - by table lookup.)
> and the count of cards in each suit (to detect a flush). That appears to be the first thing to check in any reasonable algorithm.
I put four 4-bit counters on the 16 lower bits of an integer, say suitCounts, and test like this (with my encoding of suits) :
switch ((suitCounts + 0x00003333) & 0x00008888) { case 0: print('No flush'); flushSuit = -1; // no flush break; case 0x00000008: flushSuit = 0; // flush in clubs break; case 0x00000080: flushSuit = 1; // flush in diamonds break; case 0x00000800: flushSuit = 2; // flush in hearts break; case 0x00008000: flushSuit = 3; // flush in spades break; }
I wonder how efficient that is in ES, though. Maybe if statements are faster. Perhaps it depends on the implementations. Any information on that, anyone ?
> If you have a bit-vector of cards in the flsuh suit (e.g., by keeping > one for each suit), it's only a 269 element lookup table to see if > there is a straight. That's fairly easy (otherwise, you can do a > little bit fiddling to find the straight). I haven't used the table. I don't think I will use that either. In the naive version I avoid anything fancy. In the optimised version, one single table lookup takes care of everything.
> It seems that the ~49k entries can be seen as numbers in base 5 as > well as strings. That is indeed my first choice - 13 multiply-adds should be about as fast a way to code 13 chunks of information as one can get. Not to mention that the multiplications are by 5 - increment by self shifted 2 bits up, but perhaps that kind of tricks don't make sense any longer.
I expect integer indexes into a sparse Array to be more efficient than Objects with strings as access keys, since converting between numbers and strings take time. But perhaps one should also check an Object with e.g. toString(36) for minimum key length, and see how that compares.
It is no longer ~49k entries, by the way, rather 55 - I have added the flushes. They get a count of 2 (could be 3 or 4) for each rank in the suit, and O for any rank without a card in the suit. Since there is no way a real rank distribution can have 5 pairs or more, these entries won't collide with the "normal" ones. There are 4719 such entries - 1287 for 5 cards in the flush, and 1716 for each of 6 or 7 cards.
> All the values are actually valid 32-bit (signed) numbers (four aces > and two or more kings is sadly not a valid signed 31 bit integer). What is wrong with 32 bit unsigned integers ?
Anyway, Math.pow(5, 13) is 1 220 703 125, and parseInt('4300000000000', 5) is 1 123 046 875. 31 bits are enough.
> I have kept a sorted list of counts of each rank present, sorted > by count then rank. It's at most seven entries, so I haven't done > anything fancy to keep them in order (like a red/black binary tree > or similar). That would indeed seem to be overkill, but why do you need that list ?
 Signature Johannes
Johannes Baagoe - 13 Mar 2010 17:40 GMT Johannes Baagoe :
> Has anybody written a fast Texas hold'em hand evaluator in ECMAScript?
> What is wanted is a function, say handEval(hand), which, given a > suitable representation of a hand of 7 cards, returns a value that > accurately represents the strength of the hand. That is, handEval(hand0) > > handEval(hand1) iff hand0 beats hand1, handEval(hand0) == > handEval(hand1) iff hand0 and hand1 tie, and handEval(hand0) < > handEval(hand1) iff hand1 beats hand0.
> It would be nice, too, if the return value could easily indicate what > kind of hand it is - high card, pair, two pairs, etc, preferably with > further information to break ties within each kind. My preliminary, somewhat naive version is here : file:///home/baagoe/html/baagoe.com/en/ES/Poker/pokerDemo.xhtml
As indicated, it will serve to build a lookup table which a second, hopefully much faster version is going to use.
 Signature Johannes
Johannes Baagoe - 13 Mar 2010 17:51 GMT Johannes Baagoe :
> My preliminary, somewhat naive version is here : > file:///home/baagoe/html/baagoe.com/en/ES/Poker/pokerDemo.xhtml Oops, copied the wrong tab.
http://baagoe.com/en/ES/Poker/pokerDemo.xhtml
 Signature Johannes
Scott Sauyet - 14 Mar 2010 02:53 GMT > Johannes Baagoe : > [quoted text clipped - 4 lines] > > http://baagoe.com/en/ES/Poker/pokerDemo.xhtml I haven't yet looked at the code, but there is some bug in it right now. A random test gave this hand:
Jd, 6s, 3s, Ah, 4s, 5d, 10c
and this result: 0x44321c, Straight; top card: 6.
-- Scott
Johannes Baagoe - 14 Mar 2010 04:30 GMT Scott Sauyet :
> Johannes Baagoe :
>> http://baagoe.com/en/ES/Poker/pokerDemo.xhtml
> I haven't yet looked at the code, but there is some bug in it right now. > A random test gave this hand: > > Jd, 6s, 3s, Ah, 4s, 5d, 10c > > and this result: 0x44321c, Straight; top card: 6. Yes, indeed. My test for "wheels" was flawed - it showed a straight on 6543A if there was no 2 present. It even showed a straight flush if the 6543A was in the same suit.
It has been corrected. Thanks a lot for pointing it out.
 Signature Johannes
|
|
|