Jump to content
IGNORED

Random Number Generation?


Ze_ro

Recommended Posts

Before I try taking on a huge program that will likely be more than I can handle, I've decided to start off in the shallow end and write a simple 2600 version of the classic Simon game. I already have it to the point where it draws pretty much everything on the screen, and will actually light up the buttons when you wiggle the joystick... but I need some way to generate random numbers in order for this to actually act anything like the original Simon.

 

I noticed this bit of code that Eric Ball posted in another thread:

  ; simple 8 bit LFSR (RANDOM is a byte in Zero Page RAM)

RAND   LDA   RANDOM

  BEQ   XSEED

  LSR

  BCC   SRAND

XSEED   EOR   #$A9

SRAND   STA   RANDOM

  RTS

... which is a nice piece of code... but I think I'm going to need something a little more random than that. Otherwise, every time you start the game, you'll get the exact same sequence of button presses, which wouldn't be very fun at all.

 

So what's the best way to go about this? Would it be a decent idea to start a timer when the VCS turns on, and then when the reset button is pressed, read the timer and use it to seed this LFSR? Or what if I read one of the RAM addresses before they're initialized, and use that to seed the LFSR?

 

--Zero

Link to comment
Share on other sites

Yeah...using the frame counter to seed the generator might work well. When the switch is moved, it could be any 1 of 256 combinations.

 

BTW I hope that you just use 4-way joystick movement as opposed to diagonals. The player can just hold the stick diagonally :)

 

Look forward to more info on this...I used to play that thing 'till the lights burned out :D

Link to comment
Share on other sites

Before I try taking on a huge program that will likely be more than I can handle, I've decided to start off in the shallow end and write a simple 2600 version of the classic Simon game.

:idea: Video Simon

 

but I need some way to generate random numbers in order for this to actually act anything like the original Simon.

 

I noticed this bit of code that Eric Ball posted in another thread:

  ; simple 8 bit LFSR (RANDOM is a byte in Zero Page RAM)

RAND   LDA   RANDOM

  BEQ   XSEED

  LSR

  BCC   SRAND

XSEED   EOR   #$A9

SRAND   STA   RANDOM

  RTS

 

You can also use a random number generater posted to [stella] by Thomas here http://www.biglist.com/lists/stella/archiv...1/msg00222.html.

 

The idea is to seed the random seed with a non zero number. To get sort of random numbers I used a technique used in Berzerk. I stored the TIM64T value in the y-register at start up. After the initial cart zeroing routine I set the random seed from y. Also during the game I would call the random routine periodically to sort of give better results.

 

Other games would use user input (joystick values) to re-seed the random number (i.e. Donkey Kong).

Link to comment
Share on other sites

BTW I hope that you just use 4-way joystick movement as opposed to diagonals.  The player can just hold the stick diagonally :)

Damn straight. I didn't play all that Q*Bert for nothing! I'm still trying to think of a clever way to allow you to switch the joystick control on the fly (ie, switch between Up = Top right, or Up = Top left).

 

:idea: Video Simon

Yeah, I know... this isn't really a serious attempt at a marketable game or anything... it's mostly just to get my feet wet. I'd eventually like to try making a space trading game (think Elite, except with Space War type battle scenes instead of 3D wireframe graphics), but that's obviously far beyond my abilities at the moment.

 

You can also use a random number generater posted to [stella] by Thomas herehttp://www.biglist.com/lists/stella/archives/200401/msg00222.html.

Looks like the exact same thing, except that it doesn't check for zero...

 

To get sort of random numbers I used a technique used in Berzerk. I stored the TIM64T value in the y-register at start up. After the initial cart zeroing routine I set the random seed from y.

So loading a value from uninitialized memory will actually give you a usable value? I was worried that 90% of the time, it would just give zero, and that emulators might give you zero 100% of the time.

 

I'll be throwing out all but the first two bits of the number anyways, since all I really need is a number between 0 and 3.

 

--Zero

Link to comment
Share on other sites

BTW I hope that you just use 4-way joystick movement as opposed to diagonals.  The player can just hold the stick diagonally :)

Damn straight. I didn't play all that Q*Bert for nothing! I'm still trying to think of a clever way to allow you to switch the joystick control on the fly (ie, switch between Up = Top right, or Up = Top left).

Use a set of "masks" in a table. Grab the difficulty switch setting, game selection, whatever you use to do the option...and use that to set an offset value to the correct table of mask values. Then just match them up :)

(there's probably a easier way, but that's what I just guessed at).

 

 

 

So loading a value from uninitialized memory will actually give you a usable value? I was worried that 90% of the time, it would just give zero, and that emulators might give you zero 100% of the time.

 

There is no guarantee that the value is going to be anything for certian...that's how come the routine to begin with AFAIK. You might be right about emus tho. In either case, it would always be usable (the ram location always exists...and it always contains something - even if it's just a zero).

Link to comment
Share on other sites

I'm still trying to think of a clever way to allow you to switch the joystick control on the fly.

Use a set of "masks" in a table. Grab the difficulty switch setting, game selection, whatever you use to do the option...and use that to set an offset value to the correct table of mask values. Then just match them up :)

 

Yeah, I was thinking of doing it that way... but I was also trying to think of some way to transform the joystick values that would correspond with a 90 degree rotation... however, I can't seem to come up with any simple routine that can do that without taking up more memory, and taking more time.

 

Oh well, there's still a lot of work to do before I start to worry about niceties like that anyways.

 

By the way, anywhere know where I can find out what frequencies the original Simon toy used so that I can try to approximate them with the VCS? And while I'm talking about sound, is it just a matter of sticking the proper values into AUDCx, AUDFx and AUDVx, and then just waiting an appropriate length of time before muting AUDVx? Or is there something more to it than that? [Edit: Ah, I was right! Sound is refreshingly simple after dealing with all the asymmetrical playfield and sprite position nonsense]

 

--Zero

Link to comment
Share on other sites

Heres the code I'm using in Master Mind deluxe. It was posted to [stella]

It's called once per frame in the attract mode (title screen) until the fire button is pressed

Seems random enough ;)

; RANDOM NUMBER GENERATOR;Rand1, Rand2, Rand3, Rand4 are RAM locations, initialized to any nonzero;value at program start (I use #$6D);RandomBit generates one random bit.;RandomByte generates one random byte and returns it in the accumulator.

RandomBit SUBROUTINE
   lda Rand4
   asl
   asl
   asl
   eor Rand4;new bit is now in bit 6 of A
   asl
   asl       ;new bit is now in carry
   rol Rand1;shift new bit into bit 0 of register; bit 7 goes into carry
   rol Rand2;shift old bit 7 into bit 8, etc.
   rol Rand3
   rol Rand4
   rts

RandomByte SUBROUTINE
   ldx #8
RandomByte1
   jsr RandomBit
   dex
   bne RandomByte1
   lda Rand1
   rts

Link to comment
Share on other sites

Which routine you use depends on how random you need the results. Just make sure that the initial value isn't 0.

 

The simple code uses only one byte and produces a sequence repeating after 2^8-1 (=255) iterations which is usually random enough. This kind of random number generator is called LFSR (linear feedback shift register). Use Google for more informations and optimal values.

 

With 32 bits you get 2^32-1 (=4294967295!) values before the sequence repeats. This is probably more than required in most cases, so 16 bits might be a good compromise there.

Link to comment
Share on other sites

Alright, I tried initializing things with TIM64T, and although I DO get some nice, random sequences, they always end up starting with red. I guess I'll have to fiddle with things a bit.

:idea: You have to use INTIM instead.

 

BTW: Doesn't work when using a SC (or CC), since the register has been initialized by the according software. And e.g. Windows z26 doesn't support this trick (yet!), while the DOS version does.

Link to comment
Share on other sites

Yeah, I was thinking of doing it that way... but I was also trying to think of some way to transform the joystick values that would correspond with a 90 degree rotation... however, I can't seem to come up with any simple routine that can do that without taking up more memory, and taking more time.

 

Well, how about a "temp" approach? Just save the left/right delta to a temp, up/down delta to another temp, and have the program add them to the proper ram location on the routine exit.

 

I just woke up...sorry if this has some mistakes :P

 

JoyStick:

LDA #$00 ;2 clear the temps

STA Temp1 ;3

STA Temp2 ;3

LDA SWCHA ;4 load stick

LDX Player ;3 Check player #

BEQ CheckStick ;2 branch if left joystick

ASL ;2 otherwise, move the bits to the top

ASL ;2

ASL ;2

ASL ;2

 

CheckStick:

AND #$F0 ;2

CMP #$F0 ;2

BEQ End ;2 branch if no movement

ASL ;2 check right

BCS NoRight ;2

INC Temp1 ;5

 

NoRight:

ASL ;2 check left

BCS NoLeft ;2

DEC Temp1 ;5

 

NoLeft:

ASL ;2 check down

BCS NoDown ;2

INC Temp2 ;5

 

NoDown:

ASL ;2 check up

BCS NoUp ;2

DEC Temp2 ;5

 

NoUp:

BIT SWCHB ;2 check difficulty

BMI UpRight ;2 branch if selected

CLC ;2

LDA PlayerX,X ;4

ADC Temp1 ;3

STA PlayerX,X ;4

CLC ;2

LDA PlayerY,X ;4

ADC Temp2 ;3

STA PlayerY,X ;4

JMP End ;3

 

UpRight:

CLC ;2

LDA PlayerY,X ;4

ADC Temp1 ;3

STA PlayerY,X ;4

CLC ;2

LDA PlayerX,X ;4

ADC Temp2 ;3

STA PlayerX,X ;4

 

End:

RTS ;6

Link to comment
Share on other sites

Yeah...the above stores deltas more suited to actual movement. Since all you are interested in is a value from 0 to 3, you could probably do this...

 

JoyStick:

LDA #$FF ;2 load a value for non-movement

LDY Player ;3 Load the player's number

STA Selection,Y ;4 store the minus value

LDA SWCHA ;4 load stick

CPY #$00 ;3 Check player #

BEQ CheckStick ;2 branch if left joystick

ASL ;2 otherwise, move the bits to the top

ASL ;2

ASL ;2

ASL ;2

 

CheckStick:

AND #$F0 ;2

CMP #$F0 ;2

BEQ End ;2 branch if no movement

ASL ;2 check right (Y remains at zero)

 

ASL ;2 check left

BCS CheckDown ;2

INY ;2 (Y=1)

 

CheckDown:

ASL ;2 check down

BCS CheckUp ;2

LDY #$02 ;2

 

CheckUp:

ASL ;2 check up

BCS ChecksDone ;2

LDY #$03 ;2

 

ChecksDone:

TYA ;2

 

;I just used the left difficulty switch to hold the flag whether to flip

;the values...you could use anything...just branch off to skip the

;EOR afterward. You could even test the Player value using BIT to

;decide if you should be checking the right difficulty switch, for example.

 

BIT SWCHB ;2 check difficulty

BMI SaveStick ;2 branch if selected

EOR #$02 (left/right becomes up/down and visa-versa)

 

SaveStick:

LDY Player ;3 Load the player's number

STA Selection,Y ;4 store the direction

 

End:

RTS ;6

 

Selection and Selection+1 now contain the 0-3 value for each player (or $FF if the stick was not moved).

Variable "Player" holds the current player you are checking. You could just use a high bit from another ram location to do that rather than waste a full byte.

49 bytes used by the above routine. No temps used.

Link to comment
Share on other sites

The same routine using masks...

 

JoyStick:

LDA SWCHA ;4 load stick

BIT Player ;3 Check player #

BEQ CheckStick ;2 branch if left joystick

ASL ;2 otherwise, move the bits to the top

ASL ;2

ASL ;2

ASL ;2

 

CheckStick:

AND #$F0 ;2

CMP #$F0 ;2

BEQ SaveStick ;2 branch if no movement

 

LDY #$03 ;2 initialize a counter

 

MaskLoop:

CMP MaskValues-1,Y ;4 check if the mask matches

BEQ ChecksDone ;2 Yes...keep current Y

DEY ;2 otherwise reduce and try again

BNE MaskLoop ;2 (skips Y=0...default)

 

ChecksDone:

TYA ;2

 

;I just used the left difficulty switch to hold the flag whether to flip

;the values...you could use anything...just branch off to skip the

;EOR afterward. You could even test the Player value using BIT to

;decide if you should be checking the right difficulty switch, for example.

 

BIT SWCHB ;2 check difficulty

BMI SaveStick ;2 branch if selected

EOR #$02 (left/right becomes up/down and visa-versa)

 

SaveStick:

LDY Player ;3 Load the player's number

STA Selection,Y ;4 store the direction

RTS ;6

 

MaskValues:

.byte %11010000,%10110000,%01110000

 

 

Selection and Selection+1 now contain the 0-3 value for each player (or minus value $F0 if the stick was not moved).

Variable "Player" holds the current player you are checking. You could just use a high bit from another ram location to do that rather than waste a full byte.

43 bytes used by the above routine. No temps used.

Link to comment
Share on other sites

Alright, I tried initializing things with TIM64T, and although I DO get some nice, random sequences, they always end up starting with red. I guess I'll have to fiddle with things a bit.

:idea: You have to use INTIM instead.

Doh! Thanks for correcting me. Sorry about that Ze_ro.

 

BTW, there is also a post on [stella] about the values of RIOT at start up. Read the RIOT RAM test thread.

Link to comment
Share on other sites

Thanks for the help guys! The game now generates it's own sequences and plays them back perfectly.

 

My main problem now is latching the switches... as it is right now, I have it so that the select switch will rotate through three different moves: a demo sequence that just plays back a hardcoded sequence as an "attract mode" of sorts, an actual sequence generation where it continuously plays back a sequence over and over, adding a button each time it finishes, and a third mode that just lights up whatever button the user points at. The problem with this is that it reads the select switch every single frame, so it switches game modes 60 times a second! The same goes for my joystick handling, although it hasn't been a problem until now, since I have to come up with some way to compare the user's button presses against the queue.

 

What's the easiest way to go about this? Should I just keep track of SWCHA and SWCHB, and only act if there is a change? Or is there a better way of doing this that doesn't require me to give up two bytes of RAM?

 

--Zero

Link to comment
Share on other sites

Nope...I'm pretty sure you need to store the values (so the program can tell if there is any change). Fortunately for you, a game of this type should have plenty of ram left over :)

Plus it has the added bonus of making those checks use less space later on, since you'll be reading from the ram locations instead of the actual SWCHA/B addresses.

Link to comment
Share on other sites

This is the code from SpaceWar! 7800 for handling the fire button, which you should be able to adapt to handle SWCHB.

 


FIRE_A	

 LDA INPT4,X

 TAY  ; save result in unused register

 EOR FIRE_1,X; quick compare

 BPL FIRE_B	; same as last frame

 STY FIRE_1,X; save if different

 BNE FIRE_Z	; and exit

FIRE_B

 TYA  ; restore current value

 EOR FIRE_2,X; quick compare again

 BPL FIRE_Z	; no change

 STY FIRE_2,X; save if changed

 TYA  ; set flags

 BMI FIRE_Z	; up

 

(X is used because I use the same code for both players). For SWCHB you will need to mask off the desired bits and change the BPL/BMI to BEQ/BNE.

 

And I appologise to those programmers that prefer lowercase, but uppercase ASM is a habit I've had since I started on the CoCo/6809 where we didn't have no stinking lowercase.

Link to comment
Share on other sites

Nope...I'm pretty sure you need to store the values (so the program can tell if there is any change).  Fortunately for you, a game of this type should have plenty of ram left over :)

 

True enough... in fact, assuming I don't end up using any more ram than I'm already using, the sequences can get as long as 121 buttons before the program starts to overwrite important data (And in fact, using some better memory handling techniques, I could easily double or even quadruple that I think). I think that's long enough that bounds checking is probably unnecessary. However, I hate to write inefficient code regardless of the circumstances.

 

This is actually going along faster than I expected. I might have this whole thing wrapped up by the end of the week :)

 

--Zero

Link to comment
Share on other sites

Awesome! I have the entire game completely working and playable now! Not too bad for only 5 days worth of work :) I've attached a screenshot... it doesn't look like much, but it was never really meant to look like much.

 

All it needs are a few tweaks... some sprite changes... add difficulty modes... clean up some of the code... figure out why Z26 says I'm only display 254 scanlines... after that's done, I can move on to something else.

 

Again, thanks for your help guys!

 

--Zero

post-134-1083214201_thumb.jpg

Link to comment
Share on other sites

  • 1 month later...
Heres the code I'm using in Master Mind deluxe. It was posted to [stella]

 

Heh, that's my original code from way back when (1997), that I originally wrote for INV. Funny how things can come full circle. :)

 

The advantage of the 32-bit version over a shorter one is that it's less prone to artifacts in the distribution of the output. For example, if you pull an 8-bit random number from an 8-bit LFSR, you can never get zero.

 

One easy way to seed any shift-based RNG is to just simply call it once per frame whether or not you need a random number. Unless the user manages to press Game Reset on exactly the same frame after every bootup, you'll get a reasonably random initialization of the sequence.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...