Jump to content



0

Random Number Generation?


20 replies to this topic

#1 Ze_ro OFFLINE  

Ze_ro

    Quadrunner

  • 8,511 posts
  • Welcome Back!

Posted Sat Apr 24, 2004 4:40 PM

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

#2 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,458 posts
  • Location:The land of Gorch

Posted Sat Apr 24, 2004 5:00 PM

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

#3 DEBRO OFFLINE  

DEBRO

    Stargunner

  • 1,862 posts
  • Location:Atlanta, GA

Posted Sat Apr 24, 2004 5:44 PM

Ze_ro said:

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

Ze_ro said:

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.c...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).

#4 Ze_ro OFFLINE  

Ze_ro

    Quadrunner

  • 8,511 posts
  • Welcome Back!

Posted Sat Apr 24, 2004 9:42 PM

Nukey Shay said:

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).

DEBRO said:

: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.

Quote

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...

Quote

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

#5 Ze_ro OFFLINE  

Ze_ro

    Quadrunner

  • 8,511 posts
  • Welcome Back!

Posted Sat Apr 24, 2004 10:19 PM

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.

--Zero

#6 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,458 posts
  • Location:The land of Gorch

Posted Sat Apr 24, 2004 10:34 PM

Ze_ro said:

Nukey Shay said:

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).



Quote

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).

#7 Ze_ro OFFLINE  

Ze_ro

    Quadrunner

  • 8,511 posts
  • Welcome Back!

Posted Sun Apr 25, 2004 12:10 AM

Nukey Shay said:

Ze_ro said:

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

#8 Happy_Dude OFFLINE  

Happy_Dude

    River Patroller

  • 4,208 posts
  • Forum Slacker
  • Location:Sydney, Australia

Posted Sun Apr 25, 2004 8:36 AM

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


#9 Thomas Jentzsch OFFLINE  

Thomas Jentzsch

    Thrust, Jammed, SWOOPS!

  • 16,745 posts
  • Always left from right here!
  • Location:Düsseldorf, Germany

Posted Sun Apr 25, 2004 9:04 AM

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.

#10 Thomas Jentzsch OFFLINE  

Thomas Jentzsch

    Thrust, Jammed, SWOOPS!

  • 16,745 posts
  • Always left from right here!
  • Location:Düsseldorf, Germany

Posted Sun Apr 25, 2004 9:07 AM

Ze_ro said:

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.

#11 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,458 posts
  • Location:The land of Gorch

Posted Sun Apr 25, 2004 9:16 AM

Quote

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

#12 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,458 posts
  • Location:The land of Gorch

Posted Sun Apr 25, 2004 12:18 PM

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.

#13 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,458 posts
  • Location:The land of Gorch

Posted Sun Apr 25, 2004 12:46 PM

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.

#14 DEBRO OFFLINE  

DEBRO

    Stargunner

  • 1,862 posts
  • Location:Atlanta, GA

Posted Sun Apr 25, 2004 3:37 PM

Thomas Jentzsch said:

Ze_ro said:

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.

#15 Ze_ro OFFLINE  

Ze_ro

    Quadrunner

  • 8,511 posts
  • Welcome Back!

Posted Tue Apr 27, 2004 4:25 PM

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

#16 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 20,458 posts
  • Location:The land of Gorch

Posted Tue Apr 27, 2004 5:14 PM

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.

#17 EricBall OFFLINE  

EricBall

    Dragonstomper

  • 711 posts
  • Location:Markham, Ontario, Canada

Posted Tue Apr 27, 2004 8:08 PM

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.

#18 Ze_ro OFFLINE  

Ze_ro

    Quadrunner

  • 8,511 posts
  • Welcome Back!

Posted Tue Apr 27, 2004 11:57 PM

Nukey Shay said:

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

#19 Ze_ro OFFLINE  

Ze_ro

    Quadrunner

  • 8,511 posts
  • Welcome Back!

Posted Wed Apr 28, 2004 10:50 PM

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

Attached Thumbnails

  • repeater.jpg


#20 DEBRO OFFLINE  

DEBRO

    Stargunner

  • 1,862 posts
  • Location:Atlanta, GA

Posted Thu Apr 29, 2004 7:25 AM

:thumbsup:

#21 ErikM OFFLINE  

ErikM

    Space Invader

  • 21 posts
  • Location:New York

Posted Thu Jun 24, 2004 12:15 PM

Happy_Dude said:

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.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users