BASIC ROLL command?
BASIC ROLL command?
Just kicking this idea around. Maybe it's too late to really be in the ROM BASIC, but maybe it's something USR() could be used for, or handled in some other way I have thought of.
If I have several randomized events coming up, I realize I was calling RND(1) a bunch of times. Each one has to get parsed, then do the work of involving floating point to determine and come up with the random values, which many times I am then eventually scaling/comparing to an integer value. So.....
What if I could request a bunch of numbers in an array all at once? like DIM A(5):ROLL(A) ?
where ROLL does something conceptually like:
FOR I = 1 TO 5:A=RND(1):NEXT
(but it would do it all in ROM assembly, not parsed)
Then even better, how about: ROLL(A,3,8)
second argument limits it to just the first 3 index instead of all (so if I want to roll 3 instead of 5 die), and third argument scales the result to an INT. I'm not sure if that could avoid floating point entirely. But anyhow, it would conceptually be like:
FOR I = 1 TO 3:A=INT(RND(1)*8)+1:NEXT
Just food for thought. I'm not a *huge* fan of adding a bunch of tailored commands to BASIC (that then have to be debugged, documented, explained, etc). Plus that BASIC was oriented more for turned-based stuff, not real-time action stuff (i.e. where performance isn't the main thing). Still, was curious of other opinions on it.
Context: I'm using RND(1) to semi-randomly decide individual facial features. Instead of RND(1) on each decision, would be nice to just ROLL all those at once. I think it turns out equally as random? (to decide apriori versus in-place)
If I have several randomized events coming up, I realize I was calling RND(1) a bunch of times. Each one has to get parsed, then do the work of involving floating point to determine and come up with the random values, which many times I am then eventually scaling/comparing to an integer value. So.....
What if I could request a bunch of numbers in an array all at once? like DIM A(5):ROLL(A) ?
where ROLL does something conceptually like:
FOR I = 1 TO 5:A=RND(1):NEXT
(but it would do it all in ROM assembly, not parsed)
Then even better, how about: ROLL(A,3,8)
second argument limits it to just the first 3 index instead of all (so if I want to roll 3 instead of 5 die), and third argument scales the result to an INT. I'm not sure if that could avoid floating point entirely. But anyhow, it would conceptually be like:
FOR I = 1 TO 3:A=INT(RND(1)*8)+1:NEXT
Just food for thought. I'm not a *huge* fan of adding a bunch of tailored commands to BASIC (that then have to be debugged, documented, explained, etc). Plus that BASIC was oriented more for turned-based stuff, not real-time action stuff (i.e. where performance isn't the main thing). Still, was curious of other opinions on it.
Context: I'm using RND(1) to semi-randomly decide individual facial features. Instead of RND(1) on each decision, would be nice to just ROLL all those at once. I think it turns out equally as random? (to decide apriori versus in-place)
Re: BASIC ROLL command?
What you're describing is not unlike other languages...
In C#, for example, you will initialize the Random object, then call the .Next() method over and over to get numbers.
You could probably do something in machine language, by generating random numbers and writing them to an array. I've gotten to know the variable tables a little bit, so I could probably help you some with that.
Really, though, the RND function is pretty fast, and I'm not sure you'd save any time by doing this. I did a quick run with this test program:
I generated 1000 RND numbers in 46 Jiffies. It took 38 Jiffies to read 1000 array elements. So if you add the time it would take to internally generate and store 1000 random numbers, the difference between the two is probably going to be nil.
In C#, for example, you will initialize the Random object, then call the .Next() method over and over to get numbers.
You could probably do something in machine language, by generating random numbers and writing them to an array. I've gotten to know the variable tables a little bit, so I could probably help you some with that.
Really, though, the RND function is pretty fast, and I'm not sure you'd save any time by doing this. I did a quick run with this test program:
Code: Select all
10 T1=TI
20 FOR I=1 TO 1000
30 X=RND(1)
40 NEXT
50 PRINT TI-T1
100 DIM A(1000)
110 T1=TI
130 FOR I=1 TO 1000
140 X=A(I)
150 NEXT
160 PRINT TI-T1
Re: BASIC ROLL command?
Ah, thanks, I should have set up a test of that first - I figured the parsing time (of figuring out you want to do RND) would have cost more than that. But now I recall even on the old IBM 5100, the RND call got tokenized down to a single byte.
I changed your line 30 to
30 X=INT(RND(1)*1000)+1
(i.e. scaling the resulting to a range over 255)
The first value ends up over 120 for me (so 3X slower).
Was thinking it mighta had application like psuedo randomly generating tiles or terrain type stuff, or more "organically" adjusting exponential decay rates (like modeling rain drops or wax melting {drips} kind of stuff -- which MooingLemur is right, pretty niche stuff to sacrifice a reserved word for, combined with your note that the performance improvement probably isn't there)
I changed your line 30 to
30 X=INT(RND(1)*1000)+1
(i.e. scaling the resulting to a range over 255)
The first value ends up over 120 for me (so 3X slower).
Was thinking it mighta had application like psuedo randomly generating tiles or terrain type stuff, or more "organically" adjusting exponential decay rates (like modeling rain drops or wax melting {drips} kind of stuff -- which MooingLemur is right, pretty niche stuff to sacrifice a reserved word for, combined with your note that the performance improvement probably isn't there)
-
- Posts: 20
- Joined: Sat Nov 26, 2022 6:24 pm
Re: BASIC ROLL command?
First, replacing the RND(1) with RND(.) will speed things up a little bit; the RND()x1000(+1) time dropped from 94 (108) to 75 (86) for me with that change. It drops even more if you put the 1000 into a variable first. This runs in 64 jiffies:
10 T1=TI:N=1000
20 FOR I=1 TO 1000
30 X=INT(RND(.)*N)+1
40 NEXT
50 PRINT TI-T1
Literal numbers don't get tokenized in BASIC; they're stored as text strings that have to be parsed every time they're encountered, i.e. every time through the loop. (The 1000 in the FOR header is only parsed once, fortunately.) So using variables where you can is faster. If you must use a literal with value 0, "." parses faster than any other string.
Second, I wonder if either of the synthesizer chips provide a noise source on the X16 that could be PEEKed to get a random value? On the C64 it was pretty common to set the SID up for noise generation and then sample its oscillator.
10 T1=TI:N=1000
20 FOR I=1 TO 1000
30 X=INT(RND(.)*N)+1
40 NEXT
50 PRINT TI-T1
Literal numbers don't get tokenized in BASIC; they're stored as text strings that have to be parsed every time they're encountered, i.e. every time through the loop. (The 1000 in the FOR header is only parsed once, fortunately.) So using variables where you can is faster. If you must use a literal with value 0, "." parses faster than any other string.
Second, I wonder if either of the synthesizer chips provide a noise source on the X16 that could be PEEKed to get a random value? On the C64 it was pretty common to set the SID up for noise generation and then sample its oscillator.
Re: BASIC ROLL command?
To speed up loops, use the variables that were defined the earliest in the program. So for example:
In this code, X is the fastest variable to access, followed by Y, then by Z.
For another speedup, in addition to RND(.) for RND(0), you can use the pi symbol for RND(1), which is just a hardcoded value greater than 0 that doesn't require the number parser nor a variable lookup.
Code: Select all
1 X=0:Y=0:Z=0
(code code code)
100 FOR X=0 TO 10
110 Y=RND(1)
120 NEXT
For another speedup, in addition to RND(.) for RND(0), you can use the pi symbol for RND(1), which is just a hardcoded value greater than 0 that doesn't require the number parser nor a variable lookup.
Re: BASIC ROLL command?
Well, I've never seen RND(.) before. And I'm not even seeing it documented in the old C64 BASIC RND descriptions (but I didn't look very far). Is this an extension or some C128 thing??
Re: BASIC ROLL command?
Just a dot by itself is interpreted as the number "0.0" but with the bonus of only needing to parse one character. It's worth noting, RND() doesn't care about the actual value of the number you give it, only that it's negative, zero, or positive, which is why dot and pi are acceptable equivalents to RND(0) and RND(1) respectively.
Re: BASIC ROLL command?
It's a quirk of BASIC 2. A decimal point (period) is actually parsed as a number, and since there are no digits, BASIC just treats at as the number zero. Also, since there are no digits, no ASCII to Float routines need to be run, so it's the fastest way to write Zero in BASIC 2.
That's not documented in the manual, because it's not an official language feature, but BASIC code tweakers know about this one. I guess this is a thing called "tribal knowledge."
-
- Posts: 20
- Joined: Sat Nov 26, 2022 6:24 pm
Re: BASIC ROLL command?
Good point. RND() behaves differently based on the sign – well, actually, SGN() – of its argument. If it's <0, the value is used as a seed to start a new sequence of PRNG values. If it's >0, the next value in that sequence is returned. But if it's =0, the sequence is reseeded based on the system timer. RND(.) is RND(0) and gets you the =0 behavior, which is often fine, but if you need the >0 behavior, RND(π) is a similarly quick-to-parse way to get that.
Re: BASIC ROLL command?
Thx explanation, makes sense. I'm not too big into "optimizing" BASIC (as far as the string sequence) since the hope is we just toss everything into Blitz eventually!
BTW, BASLOAD lets us violate the 80-col thing. I've found I can put lengthy rows in BASLOAD - and the tokenizer will take it. I can't edit the resulting line, but it does run ok (at least seemed so for me)
BTW, BASLOAD lets us violate the 80-col thing. I've found I can put lengthy rows in BASLOAD - and the tokenizer will take it. I can't edit the resulting line, but it does run ok (at least seemed so for me)