![]() |
![]() |
![]() |
![]() |
|
|||||||||||||||||||||||||||||||||||
DealingCard dealing is a task commonly addressed online; it's a good beginner-level programming assignment, not to mention the cornerstone of all card games. There are many approaches to card dealing. The one used in One Chip Video Poker is not a very elegant one - I chose it because it's suitable for use where code and data space are at an extreme premium, and because video poker has a couple of properties that accomodate it. |
One Chip Video Poker Project |
Card RepresentationCards are easily represented in a single byte each – the intuitive way to do it is just use the numbers from 0 to 51. Intuitive for assembly or C programmers, anyway, who like to think of numbering things starting at zero. One very happy property of cards is that there are four suits, and four is one of those useful powers of two.
If you shift the suit bits off, you're effectively dividing by four. From 0..51, dividing by four – and ignoring any decimal remainder – gets you a number from 0 to 12. Handily enough, those correspond to the thirteen ranks. |
|||||||||||||||||||||||||||||||||
|
Ranks:
Suits:
Not quite arbitrarily chosen – as it happens, if you add 3 to the suit values, you get the character codes for the heart, diamond, club, and spade on the PC console. That's how I get the nifty printouts like the one in the table at the bottom of the page. |
In brief: given a number “card” from 0..51: rank = card / 4, which is the same as card >> 2 suit = card % 4, which is the same as card & 3 where the % operator is modulo, & is bitwise-AND, / is integer divide, and >> is shift right. |
||||||||||||||||||||||||||||||||||
Examples:
|
|||||||||||||||||||||||||||||||||||
Hand RepresentationAgain, simplicity is the guiding light. At the beginning of a game, the PIC uses the current LCRNG seed (hopefully well stirred) and starts from there to generate the first ten cards out of the deck. Why ten cards? Five of them form the initial (pat) hand, and the other five are the cards that can potentially be drawn. A great deal more variety could conceivably be introduced to the game by dealing only five cards, then stirring the LCRNG while the player chose cards to hold, then dealing the drawn cards from the newly stirred seed. I may modify the game to work that way – but it's simpler to keep the dealing all in one place, and 4 billion hands is enough variety to be getting along with. Also, it would be very difficult to exhaustively test that kind of arrangement with a PC simulation. |
|||||||||||||||||||||||||||||||||||
The Dealing AlgorithmWith all that in mind, here is the actual dealing algorithm. Start with an empty hand, i.e. no cards dealt. Then, for each of n cards you want to deal:
|
|||||||||||||||||||||||||||||||||||
|
The astute reader will have osberved that this algorithm has the potential to run forever. In theory, the random number generator could be called trillions of times before a number from 0..51 could be gleaned from the top 6 bits, or it may spend years choosing cards it's already chosen. As it turns out, that isn't a problem, and we owe it all to the fact that the “random” number generator is so predictable. As shown on the previous page, the LCRNG can only be in 232 states, and the deal-10-cards strategy means there are only 232 possible pat-plus-draw hands. 232, or about four billion, isn't too bad a number of things to simulate exhaustively on a modern PC, assuming that each instance only needs a teeny thing done. I wrote a little PC C++ program that, for every possible seed, dealt the corresponding 10-card hand the same way the One Chip Poker program would do it. While it did that it kept track of how many times it had to call the LCRNG in order to generate the hand, and how many times it generated cards that were already present and therefore had to be redone. It concluded this: 39 calls to the LCRNG to generate 10 cards seems pretty bad, but this is all still blink-of-an-eye performance. Good enough! Dealing cards this way turns out to need only around 45 machine instructions and around 16 bytes of variables including the 10 bytes for the hand, but not including the LCRNG's code & variables. (The LCRNG sits on 73 machine instructions and 8 bytes of RAM). |
Big Caveat:This algorithm's performance would degrade terribly if many more than 10 cards were needed – if it were to be asked to deal the whole deck, it would likely take a very long time. So, it's really only usable because video poker shuffles the deck between games and doesn't need to deal very many cards per game. Keep that in mind if you're thinking of using the dealing code for a different game. |
||||||||||||||||||||||||||||||||||
Verifying PIC implementationThe unit test for dealing is a little test program to deal out 10-card hands and show them on the LCD. Then, a corresponding PC program does the LCRNG / hand generation the same way and prints out expected results. Here's an example of the simulation:
|
|||||||||||||||||||||||||||||||||||
|
Page Top
|