Jump to content
IGNORED

on ... gosub issue


abaudrand

Recommended Posts

I'm trying to use the on gosub function but I only got Syntax error on the first line of gosub. Is it coming from my old XP version of batari basic?

rem ' Rolling Dice1
a=rand(&5)
on a gosub Data1 Data2 Data3 Data4 Data5 Data6
var15=c:var16=d

rem ' Rolling Dice2 if equal to Dice1, cast Dice again
RollingDice2
b=rand(&5)
on b gosub Data1 Data2 Data3 Data4 Data5 Data6
var17=c:var18=d
if var17=var15 && var18=var16 then goto RollingDice2
Data1
  c=%11101000
  d=%01101000
  return
Data2
  c=%01010000
  d=%00001001
  return
Data3
  c=%00101100
  d=%00001100
  return
Data4
  c=%00111000
  d=%00110100
  return
Data5
  c=%01110000
  d=%01110100
  return
Data6
  c=%00100000
  d=%00000100
  return

Link to comment
Share on other sites

I got this to compile in bB 1.0 in Vista 32-bit

 rem ' Rolling Dice1
    a=(rand&5)
    on a gosub Data1 Data2 Data3 Data4 Data5 Data6
    var15=c:var16=d

    rem ' Rolling Dice2 if equal to Dice1, cast Dice again
RollingDice2
    b=(rand&5)
    on b gosub Data1 Data2 Data3 Data4 Data5 Data6
    var17=c:var18=d
    if var17=var15 && var18=var16 then goto RollingDice2
Data1
  c=%11101000
  d=%01101000
  return
Data2
  c=%01010000
  d=%00001001
  return
Data3
  c=%00101100
  d=%00001100
  return
Data4
  c=%00111000
  d=%00110100
  return
Data5
  c=%01110000
  d=%01110100
  return
Data6
  c=%00100000
  d=%00000100
  return

Link to comment
Share on other sites

I don't think rand&5 is going to give you what you think; it can never be 2 or 3. You might want to use a loop, like this:

 

  a = rand
reduce_a
  if a > 5 then a = a - 6 : goto reduce_a

 

That isn't as fast as using &, but it will give you a random number from 0 to 5 with a pretty good distribution-- the likelihood of rolling 0, 4, or 5 will be 42 out of 255, and the likelihood of rolling 1, 2, or 3 will be 43 out of 255.

 

If on...gosub doesn't work with the version of bB you're using, you can use on...goto to accomplish the same thing, like this:

 

  rem ' Rolling Dice1
  a=rand
reduce_a
  if a>5 then a=a-6:goto reduce_a
  gosub check_a
  var15=c:var16=d
  rem ' Rolling Dice2 if equal to Dice1, cast Dice again
RollingDice2
  b=rand
reduce_b
  if b>5 then b=b-6:goto reduce_b
  gosub check_b
  if b=a then goto RollingDice2
  var17=c:var18=d

check_a
  on a goto Data1 Data2 Data3 Data4 Data5 Data6
check_b
  on b goto Data1 Data2 Data3 Data4 Data5 Data6
Data1
  c=%11101000
  d=%01101000
  return
Data2
  c=%01010000
  d=%00001001
  return
Data3
  c=%00101100
  d=%00001100
  return
Data4
  c=%00111000
  d=%00110100
  return
Data5
  c=%01110000
  d=%01110100
  return
Data6
  c=%00100000
  d=%00000100
  return

Edited by SeaGtGruff
Link to comment
Share on other sites

thanks SeaGtGruff... I didnt knew the trouble with random yet I ve noticed some result barely go out but didnt knew why :). I'm far from computer till next week so I could not try it for the moment but yes, I was going to a goto solution yet not as good as yours. :)

Link to comment
Share on other sites

The AND (&) function is a bit mask. What it does is only output a 1, if both the numbers involved also contain a 1, otherwise it outputs 0.

 

AND is good for powers of two:

 

1, 11, 111, 1111, etc...

 

When used this way, it will mask off all the extra digits, clipping a larger number to a smaller set of values.

 

If you want just a single digit, 0 or 1, then you use & 1

 

If you want the numbers 0 to 3, then you use & 11

 

For the numbers 0 to 7, you use & 111

 

The numbers 0 - 15 are done with & 1111

 

Take each power of 2, and subtract one from it. 2, 4, 8, 16 become 1, 2, 7, 15, etc...

 

Any other numbers will not produce all the values, because not all the digits contain a 1. Using & 4, for example would produce the numbers 0 and 4, that's it, because the binary is 100, leaving only two choices.

 

If you want the numbers 1 through 4, you can use & 3, then add one.

 

In this program, the numbers 0 through 5 are needed for the on gosub. Use the next highest power of two, which is 8, subtract one, arriving at & 7. Now you have the numbers 0 through 7. Use an "if, then" statement to quickly check for the number being larger than 5, and if so, loop back and pick another number.

 


rand_loop
 a = (rand & 7)
 if (a > 5) goto rand_loop

 

Or...

 

You can stack a couple of random number ranges together. Say you want a range from 0 to 13. You could take (rand&7)+(rand&3)+(rand&3) and get there too. Smallest number is 0, largest is (7+3+3) 13.

 

Different methods bias the numbers in various ways. Adding several smaller random numbers together to get a larger one will produce a different kind of random than picking from the next largest power of two number and looping until one is obtained in the range will.

Link to comment
Share on other sites

I've been thinking about the random number problem-- specifically, a random number from 1 to 6 (i.e., rolling a 6-sided die). The challenge is to come up with a method that always takes the same amount of time-- preferably as short as possible-- and gives an equal distribution for each possible value.

 

I don't especially care for the bit-masking method, because bB's rand statement returns a value from 1 to 255, so (if I'm not mistaken) masking the bits can't give an equal distribution-- plus, you're limited to values from 0 to 2^n-1, which doesn't work for 1 to 6 anyway.

 

I think the best method for generating a number from 1 to 6 is to reduce the result so it falls within 1 to 6-- but a repeated subtraction loop will take a variable amount of time, plus you have three values left over (253 reduces to 1, 254 reduces to 2, and 255 reduces to 3). I've come up with a solution that uses a lookup table instead of a loop so the reduction always takes the same amount of time. And since there are three extra values, and 6 is a multiple of 3, it uses two tables instead of one. Thus, the first table reduces rand to a number from 1 to 6, with an extra chance of getting 1, 2, or 3; and the second table also reduces rand to a number from 1 to 6, but with an extra chance of getting 4, 5, or 6. This is more or less a lookup table with 510 values (255+255=510), which is a multiple of 6 (510/6=85), so each value occurs exactly 85 times. But since we can't have a lookup table greater than 256 bytes, the 510 values are split into two separate lookup tables, and you have to alternate between them.

 

This has the disadvantage of requiring 512 bytes for the two lookup tables, plus extra variables for keeping track of which table to use; but it has the advantage of always taking the same amount of time and giving an equal distribution for the six possible values.

 

Here's a sample program to demonstrate the idea. It simulates rolling two 6-sided dice, and shows the results in the score. Since either die could have the same value as its previous value, the score alternates between yellow and blue each time the dice are rolled, just to indicate that the dice were rolled. And since rand gives 255 values-- an odd number-- and is used twice when rolling the dice, each die should cycle through all 255 possible values before the combinations start to repeat in the same sequence. But since the program alternates between two lookup tables, this should actually come out to 510 iterations before the combinations of reduced values start to repeat the same sequence over again.

 

This method could be used with other ranges, but the number of lookup tables might be different, since any remainders would need to be multipled to get equal distributions. And it goes without saying that this method is specifically for a number of values that isn't a power of 2.

 

  rem * generate a random number from 1 to 6 with equal chances

  dim die1 = a
  dim die2 = b
  dim roll1 = c
  dim roll2 = d
  dim counter = e
  rem * roll1 and roll2 are used to track whether to use table0 or
  rem * table1 when looking up the mod 6 value for die1 and die2

  scorecolor=$14

loop

  if counter = 0 then gosub rolldice

  drawscreen

  counter=counter+1:counter=counter&63

  goto loop

rolldice

  rem * roll the first die
  temp1=rand
  roll1=roll1^1
  if roll1=0 then die1=table0[temp1] else die1=table1[temp1]

  rem * roll the second die
  temp1=rand
  roll2=roll2^1
  if roll2=0 then die2=table0[temp1] else die2=table1[temp1]

  score=0
  for temp1=1 to die1:score=score+1000:next
  for temp1=1 to die2:score=score+1:next

  scorecolor=scorecolor+128

  return

  data table0
  0,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1
  2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3
  4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5
  6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1
  2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3
  4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5
  6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1
  2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3
end

  data table1
  0,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4
  5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
  1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2
  3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4
  5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
  1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2
  3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4
  5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
end

Link to comment
Share on other sites

So a = (rand/64) + (rand/64) isn't good enough?

Well, rand/64 will give 0, 1, 2, or 3, so (rand/64)+(rand/64) will give 0 through 6-- not 1 through 6. Not only are you calling rand twice to get one number, but if you actually want 1 through 6 then you'll need to loop back and try again, so that means you might be calling rand four times, or six times, or eight times, etc.-- plus, the loop will take a variable amount of time.

 

I was trying to figure out a way to keep the amount of time constant and ensure an absolutely equal distribution of results, and that was the best I could come up with. I'm not saying it's the best way, or that anyone should do it that way-- it's just the best way I could think of so far.

 

Note, you could also randomize the two lookup tables, rather than just listing 1 through 6 over and over as I did-- just make sure you have 85 instances of each digit. Or if you actually wanted 0 through 5, then change the values to 0 through 5.

 

Also, keep in mind that the first value in the two lookup tables is just a garbage value, since rand will never equal 0-- unless you *set* it to 0 and "break" the rand function. You need it to make the tables 256 bytes, but you could also overlap the two tables so the 0th value of the second table is the 255th value of the first table. On the other hand, it's probably best to keep each table 256 bytes to avoid page-crossing situations. I didn't align the tables to page boundaries-- I assumed bB would do it automatically.

Link to comment
Share on other sites

So a = (rand/64) + (rand/64) isn't good enough?

 

Well, rand/64 will give 0, 1, 2, or 3, so (rand/64)+(rand/64) will give 0 through 6-- not 1 through 6. Not only are you calling rand twice to get one number, but if you actually want 1 through 6 then you'll need to loop back and try again, so that means you might be calling rand four times, or six times, or eight times, etc.-- plus, the loop will take a variable amount of time.

 

Oops. I forgot something. In his first example, he needed 0 through 5 for an on-gosub (6 numbers). What I posted gives 0 through 6 (7 numbers).

 

I'd never loop back to get another number, so what about this:

 

a = (rand/64) + (rand/128) + (rand/128)

 

That should give a random number between 0 and 5 (6 numbers). Does rand and division use too many cycles?

Link to comment
Share on other sites

Oops. I forgot something. In his first example, he needed 0 through 5 for an on-gosub (6 numbers). What I posted gives 0 through 6 (7 numbers).

 

I'd never loop back to get another number, so what about this:

 

a = (rand/64) + (rand/128) + (rand/128)

 

That should give a random number between 0 and 5 (6 numbers). Does rand and division use too many cycles?

That would work, but division uses a loop so it takes a variable amount of time, plus I'm not sure how the distribution will be, although I'm pretty sure it won't be even.

 

I checked, and bB doesn't align data statements to page boundaries, plus it adds a JMP before each data set, so the number of bytes used is actually the length of the table plus 3. As long as you don't care about page crossings, my previous example can be made more compact by overlapping table0 with the return of the subroutine, and then overlapping table1 with most of table0, as follows, thus reducing the total table size to just 258 bytes:

 

  rem * generate a random number from 1 to 6 with equal chances
  dim die1 = a
  dim die2 = b
  dim roll1 = c
  dim roll2 = d
  dim counter = e
  rem * roll1 and roll2 are used to track whether to use table0 or
  rem * table1 when looking up the mod 6 value for die1 and die2
  scorecolor=$14
loop
  if counter = 0 then gosub rolldice
  drawscreen
  counter=counter+1:counter=counter&63
  goto loop
rolldice
  rem * roll the first die
  temp1=rand
  roll1=roll1^1
  if roll1=0 then die1=table0[temp1] else die1=table1[temp1]
  rem * roll the second die
  temp1=rand
  roll2=roll2^1
  if roll2=0 then die2=table0[temp1] else die2=table1[temp1]
  score=0
  for temp1=1 to die1:score=score+1000:next
  for temp1=1 to die2:score=score+1:next
  scorecolor=scorecolor+128
  asm
table0
  rts
  byte 1,2
table1
  byte 3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
  byte 1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4
  byte 5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2
  byte 3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
  byte 1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4
  byte 5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2
  byte 3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
  byte 1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4
  byte 5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2
  byte 3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
  byte 1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4
  byte 5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2
  byte 3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
  byte 1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4
  byte 5,6,1,2,3,4,5,6,1,2,3,4,5,6,1,2
  byte 3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6
end

 

I tried using rand16 as well, but got a result of 96 for one roll, which is the return opcode, so I guess using rand16 can give a 0 return value at times?

Link to comment
Share on other sites

You know, that division could be sped up by using inline ASM.

 

Since it's powers of two....

 


a = rand
ASM
  lda  a
  lsr
  lsr
  lsr

REM continue program...

 

Shifts are cheap. lsr = divide by 2, asl multiply by 2, and there are the bitmasks.

 

Bitmasks do bias things, but one does have choices in that bias. Instead of doing the bitmask for the lower digits, do it in the middle, or the higher digits, etc...?

a = rand
ASM
  lda a
  and #%00111100
  lsr
  lsr

REM continue program

 

A couple of shifts and you can very easily move the number to the range you want.

Link to comment
Share on other sites

I've been thinking about the random number problem-- specifically, a random number from 1 to 6 (i.e., rolling a 6-sided die). The challenge is to come up with a method that always takes the same amount of time-- preferably as short as possible-- and gives an equal distribution for each possible value.

 

I don't especially care for the bit-masking method, because bB's rand statement returns a value from 1 to 255, so (if I'm not mistaken) masking the bits can't give an equal distribution-- plus, you're limited to values from 0 to 2^n-1, which doesn't work for 1 to 6 anyway.

 

 

Hi there,

 

1 to 6 is a small number, so how about adding up bits? I'm not sure what the distribution would look like, but the time would always be the same (40 cycles of assembly, plus whatever rand takes).

 

Something like this, where you use rand to get a number between 1-255 (I think rand never returns 0, but that doesn't matter for this code anyway):

 

rem * I do not no BB syntax, but do rand function for a value 1-255
 a = (rand)

  ASM
;now,"a" is actually located at zero page register address??? (lets assume it is $80 for this example)
lda	#1  
asl	$80
adc	#0   ; 1 to 2 possible
asl	$80
adc	#0   ; 1 to 3 possible
asl	$80
adc	#0   ; 1 to 4 possible
asl	$80
adc	#0   ; 1 to 5 possible
asl	$80
adc	#0   ; 1 to 6 possible
sta	$80  ; place the value back in "a"
 
END

 

 

Edit: If you wanted to have a random number between 0 and 5 you would just change "lda #1" to "lda #0" in the code above.

Edited by Omegamatrix
Link to comment
Share on other sites

1 to 6 is a small number, so how about adding up bits?

I had considered that solution, but I wasn't sure about the distribution. For a sequence of "random" numbers where each value occurs exactly once and all values between 0 and 255 occur, each bit can be on 50% of the time or off 50% of the time. But when you add the bits up, you won't get an even distribution.

Link to comment
Share on other sites

. . . you won't get an even distribution.

 

Speaking of that:

 

http://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#rand

 

I noticed when I used AND with rand instead of dividing, the distribution was very uneven. When I switched to dividing or a mix of AND and dividing, the distribution became more even.

 

For example, instead of using something like a = (rand&3) + (rand&1) + (rand&1), I had to use something like a = (rand/64) + (rand&1) + (rand/128).

Link to comment
Share on other sites

1 to 6 is a small number, so how about adding up bits?

I had considered that solution, but I wasn't sure about the distribution. For a sequence of "random" numbers where each value occurs exactly once and all values between 0 and 255 occur, each bit can be on 50% of the time or off 50% of the time. But when you add the bits up, you won't get an even distribution.

 

 

I see. You are right the spread is not good. I took a look in excel and it wasn't even close.

 

 

There is another approach you might try. rand MOD 6 is a fairly good distribution. I wrote some code to find this using divison by 6 to get the whole number, then multiplication by 6 followed by subtraction to get the remainder. I then increased it by 1 to get a number of 1 to 6.

 

 

It is slower than using a look up table of course, but it saves bytes and is faster than the worse time case of looped division. So it is a medium length time/bytes approach to try and get the most bang for the buck. Try it out. :)

 

 

 a = rand

 ASM
tempOne  = $80	  ; $80 is just for this demonstration, you must determine a correct address for a tempvariable
 lda  #$08	  ; one bit is pre-set to save a SEC instruction later
 sta  tempOne

 lda  a  ; holds value from rand, currently 1 to 255 possible
;do unsigned division by six, result is the whole number (no remainder)
 cmp  #192
 bcc  .do96
 sbc  #192  ; carry is set!
.do96
 rol  tempOne
 cmp  #96
 bcc  .do48
 sbc  #96
.do48
 rol  tempOne
 cmp  #48
 bcc  .do24
 sbc  #48
.do24
 rol  tempOne
 cmp  #24
 bcc  .do12
 sbc  #24
.do12
 rol  tempOne
 cmp  #12
 bcc  .do6
 sbc  #12
.do6
; carry gets set due to the pre-load of #$08
 rol  tempOne
 sbc  #6
 rol  tempOne

; at this point "tempOne" holds the whole number of the division of six,
; lets do multiplication by six now
 lda  tempOne
 asl
 adc  tempOne
 sta  tempOne
 adc  tempOne
 sta  tempOne

;tempOne is now multiplied by six, find the remainder
 dec  tempOne  ; instead of 0-5, lets get a number 1-6
 lda  a  ; rand value 1 to 255
 sec
 sbc  tempOne
 sta  a  ; now holds a number 1 to 6

END

Edited by Omegamatrix
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...