![]() |
![]() |
![]() |
![]() |
|
||||||
Random Number GeneratorOf necessity, the random number generator in One Chip Poker is very simple. It's the old standard Linear Congruential Random Number Generator (LCRNG) described in Numerical Recipes in C, and which can be found all over the place online. There's a good brief discussion of it at Wikipedia. |
One Chip Video Poker Project |
|||||
|
Basically, how it works is like this:
In general, the higher-order bits of LCRNG samples are more random than the low-order bits, so I use the high-order bits in the deal/draw code. |
LCRNGs are a good, old-fashioned technique that is suitable for tiny systems like PICs but not for any flashy modern applications like cryptography. Nearly every mention of them online has a “no cryptography” disclaimer, in varying degrees of disdain. Here I'll add my own: Don't try to use this RNG for cryptography. |
|||||
StirringOne big drawback with LCRNGs is that they are just a repeating series of numbers which look sort of random - even if it is a long series, more than 4 billion numbers in this case. There's a good old video-game-biz trick for helping out with this, which is to throw in a real-world randomizing factor: user response time. How does that work? Well, in human terms, this LCRNG implementation is pretty fast. I estimate that it takes about 400 instruction cycles to run. If you're using the internal 16F628 oscillator at 4MHz (1MHz instruction cycle), it takes about 0.4 milliseconds per iteration. Human reaction times in button pressing tend to be in the tenth-of-a-second range, so even if a user is twiddling a button as fast as they can this thing can step through a whole bunch of iterations - even calling it 50 times a second is fast enough, which is the same rate as the button-debouncing interval used in One Chip Poker's button input routine. If you just keep calling the RNG while waiting for user button presses, it "stirs" the RNG nicely by more randomly sampling the LCRNG sequence. However: Looking ahead to what we'll really have in the game, it's going to go something like this.
So, that didn't give us much of a chance to stir; if the seed is hardcoded to start at the same value on reset, and the RNG is stirred once per debouncing interval, chances are that fewer than ten stirs will have happened - resulting in only about ten initial hands. We can't stir and sample the buttons much faster or the debouncing might not work. Calling the LCRNG a fixed but larger number of times per button sampling period doesn't help either because there will still only be about ten different RNG states once the game starts. One solution is to avoid having the same seed every time at startup. Thus, it can't be hardcoded; it has to be stored somewhere. Happily, the PIC 16F628 has a built-in mechanism for just such an application - the Data EEPROM. Like the program Flash-ROM, the Data EEPROM is memory that will retain its contents even while the circuit's power is off. Think of it as a 128-byte hard drive. So, all we have to do is write the current seed into Data EEPROM once in a while, and read it out of there on powerup. |
||||||
LCRNG Unit TestHere's a screenshot of a little test that shows the first 8 numbers output by the PIC version, starting with the seed 0x1337D00D, and the expected values as printed out by a little PC console program implementing the LCRNG:
Pressing a button advances to the next 8 numbers, which can be compared to the next 8 from the PC, for as long as it takes to convince you that the PIC version is working.
|
||||||
|
Page Top
|