Log In  


Cart #travwomoving2-0 | 2022-11-24 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
4

(v00 11-23-22)
TO LOAD THIS PICO-8 CART, in immediate mode, type: load #travwomoving2

Run this cart. Now before you view the source-code, THINK of what it might be. Do you think it's using an array to keep track of the stars ? OK. Perhaps the acceleration speed is being recorded from another floating point array. Fine. How about the color of each star to make sure they stay the same for each row in the display. That would be the regular way to do it, yes.

So that's 3-arrays we need to keep track of, right ? Let's list them out:

  1. The Star's X-position.
  2. The Star's acceleration speed.
  3. and the Star's constant color.

Now let's consider something else.

. . .

Here is a scene inside the movie, "DUNE" where Paul, son of Duke Leto of the House Atreides is commenting to himself about what is to follow. They must travel a great distance, farther than any conventional starship can reach, and it is done by the Navigators, fantastic beings with vast powers who have ingested the gas of the spice Melange for many years.

Leto speaks to his son, "Soon they will begin to fold space."

Paul thinks to himself, "Far off in the control rooms of spice gas. Traveling without moving."

. . .

That is much what is happening here. Traveling without moving ...

Like being able to enter any universe at any given time. Like being able to travel through time in the past.

"As long as we know what the results have BEEN, we know what the results will be NOW."

Now let's get back to the code. Do you still think it is 3-arrays controlling the stars ?

No, would you be surprised if I told you NO arrays are controlling the stars ? None whatsoever.

Instead the stars' position, acceleration, and color are all being controlled by one very powerful and often underestimated ability.

That ability is called SEED RANDOM. NOW look at the source-code to see how it was done. I'll wait for you here ...

So in definition, just what does is happening ?

First off the program does not rely on RND() or SRAND() from Pico-8, instead it has my own random number generator based upon a true random number from the beginning that is within the range of -32768.9999 to 32767.9999.

In this in calling the RNND() function, a 'random' number is generated based upon 'shuffling' these values and returning one that matches what we need.

This can be quite useful if you want to have a random set of events occur yet you want those random items to be the same every time. For instance, try this code:

cls()
seed=1234.5678
for i=1,12 do
  ?(rnnd(6)+1)\1
end
-- my nifty random function!
function rnnd(a)
  seed=seed*16372.1
  return seed%a
end

Well even before you run it you know there will be 12-random numbers each a value of 1-6 that will be printed. Yet if you run this program OVER and OVER again, you will get the exact same results every time.

NOW you can see how powerful seeding is ! Not just for saving you from arrays but being able to build procedurally generated maps and worlds that can be repeated and reproduced on the fly with a chosen seed value leading the first random value and that follows to it all.

And using zero variable space in the process. Being able and capable to build whole planets and galaxies of definition and not being forced to use any arrays besides.

Or as above I am repeating the seeded value so the stars will appear in their correct position and are all only being adjusted by a single variable.

Try including my function within your code. See if it doesn't inspire you in your cart and game design and open your mind to new ways of programming and coding ...

HOPE THIS HELPS !


If you are looking for the earlier version which uses true RND() and true SRAND(), that can be found HERE:

Cart #travwomoving-0 | 2022-11-23 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
4

4


how do i post a thread? i'm confused

-edit thank you


1

Hi @my_name_is_doof:

And welcome to Lexaloffle, home of Pico-8 !

If you just want to introduce yourself, go HERE:
https://www.lexaloffle.com/bbs/edit.php?cat=7&sub=1&new_post=1

To post a cart, go HERE:

https://www.lexaloffle.com/bbs/edit.php?cat=7&sub=2&new_post=1


Interesting. This makes me wonder though, is there a way to use this but still have other non-deterministic random elements in the same cart? For example, say I wanted to use this as background in a game, but at the start of a level I wanted a random power up to pop up that is not the same every time. Would there be a way to do that?

Put another way: say I put ? rnd() at the very end of your update function. As expected, it always outputs the same number. Could I get it to change without also making the stars change? In another programming language, I would create a different instance of the random number generator, but I don't know of a way to do that in pico8.


I have a suggestion, @mattu82.

Let me think for a second how to code this ...

Right ! Try this out:

function _init()
  cls()
  r=rnd(-1)
  p=0
end

function _update()
  line(0,0,127,0,0)
  srand(1234.5678)
  pset((rnd(128)+p)%128,0,7)
  srand(r)
  for i=1,127,2 do
    for j=0,127,2 do
      pset(j,i,rnd(15)+1)
    end
  end
  r=rnd(-1)
  p=p+1
end

@mattu82

Once you're done with your "fixed" elements you can reseed with the current time. Or any other value that's likely to change/be different each time the cart is run.

function _draw()
 cls()
 srand(10)
 print('same:'..rnd())
 srand(t())
 print('different:'..rnd())
end

Hi @jasondelaat:

I'm not sure if you get "great" random number seeding this way. The problem with time() is it always ends the last 2-decimals in 00, 33, or 67.

Now I can't say if mine is better yet it does get both positive and negative values in the range of -32768.9999 to 32767.9999 with random digits on the end and not ending in 00, 33, or 67.

It's a good idea, as a purist though we want to seed true random, and it is quite possible my method above is not as it relies jumping from one seeded value to another.

Yet isn't there some memory location that always has a 4-byte random number ?


2

Here, try this, @jasondelaat. This is something I wrote about a year ago. Messing with numbers mostly. Started at half of 32767, 16383, and began counting down from there until it worked.

Cart #gunewewoki-0 | 2022-11-23 | Code ▽ | Embed ▽ | No License
2


So, yes, I removed the RND() in favor of my own RNND() function (see top post), freeing RND() and SRAND() for independent use.


1

What @dw817 gave initially gives a different number every cart run, which could also be useful but wasn't what quite what i was looking for.

@jasondelaat's idea of seeding with time does, but there is the problem that t() could always be the same too, so you may get repeated behavior. Time of day would be better, but we can only get that down to the second.

I tweaked @dw817's rnnd() and that seems to work best, though I'm not sure why.

Here is a cart I made to experiment with all these:

Cart #randomtest-0 | 2022-11-24 | Code ▽ | Embed ▽ | No License
1


Wow ! I am loving what you did here, @mattu82 !

Since you're quite good at this, how about this ?

Is there a way to determine if my magic number of 16372.1 is really good for random numbers compared to the default RND() provided by Pico-8 ?


A few thoughts came to mind reading this thread:

  1. There's no way to get true random with computers, and it's theoretically possible that true random doesn't exist at all. If you're not satisfied with using time() to seed random functions, you can always seed them with one of the results of random functions after seeding with time().

2.I wouldn't have used quite so many arrays for the initial prompt. You can do the array method with only 1. You can also use camera movement if you have a repeat in the star pattern.

I have a game sitting around from a game jam that I've been considering porting to pico-8 (if I can figure out how to compress and scale it enough to fit) that uses a similar seeded random effect. For my use, though, it's for populating a few scenes in a high school (using dots for people on a black and white blue print style map) with bystanders without having to write code to add each one. As such, I think this type of technique is more useful when combined with arrays rather than getting rid of them.


Hi @kimiyoribaka.

Well yeah there is one. No real random numbers. Still enough, to confuzzle most people. :)

  1. I have never really been content to shove all those single array things into a smarter multi-array thing. I think I am and always will be old-school and make an array for every single object.

In some ways I think that's a benefit, in others - probably not.

It was a grand effort for me to write this alone:

https://www.lexaloffle.com/bbs/?tid=27795


Cart #gikigabiya-0 | 2022-11-24 | Code ▽ | Embed ▽ | No License

Here is a test. If you press 🅾️ then it uses RND() and SRAND() with a fixed seed.

If you press ❎ it uses my rnnd() with the same fixed seed.

Watch the pattern which will be the same each time you press either button. They appear to be alike to me. No difference.

If that is the case then it is possible that the method I am using here is in fact @zep's very own for developing random numbers. :)

To reset the seed to a new random number, press CTRL+R to rerun.


@dw817 To clarify, I didn't mean an array of objects. I meant manually simulating an array of structs. For my main projects I do a lot of direct memory management and other low-level stuff just cause I find it easier than the normal abstractions and object-oriented stuff. As such, I'm rather partial to things like storing 2d maps in 1d sequences and just using different pitches and strides to step through them, though I'm also inconsistent about methods. For the situation in your first post, here's what I would do (if I felt like it):

function _init()
  srand(33) -- some number, whatever
  local n = 1
  stars = {}
  for i=1,100 do
    stars[n] = rnd()*128
    stars[n+1] = rnd()*128
    stars[n+2] = (rnd()*4)\1+1
    stars[n+3] = (rnd()*16)\1
    n+=4
  end
end

function _update()
  for i=1,#stars,4 do
    stars[i]-=stars[i+2]
    stars[i]%=128
  end
end

function _draw()
  cls()
  for i=1,#stars,4 do
    pset(stars[i],stars[i+1],stars[i+3])
  end
end

1

@dw817
Seeding with time() shouldn't really be a problem. Since we're talking pseudo-random numbers here we basically just have a great big sequence on numbers and each time you call rnd() you're just getting the next number in the sequence. The sequence is always the same. The seed just changes where in the sequence we start taking numbers from. So even if the last two digits of the number are always the same, as long as some of the digits are different you'll start from a different place in the sequence and have "different" random numbers.

Actually, I take that back. Using time() isn't a problem if some time is allowed to pass between calls. If you're calling it multiple times in _update (for instance) then it might not have updated and you end up seeding with the same value multiple times. Which obviously isn't ideal. Maybe that's what you meant?

@mattu82
You mentioned spinning up new instances of random number generators so I threw this together. See if it does what you're after.

rnd_funcs = {
   reset=function(self)
      self.i = 0
   end
}

rnd_meta = {
   origin=rnd(),
   i=0,
   __index=rnd_funcs,
   __call=function(self)
      srand(self.seed + self.i)
      self.i += 1
      return rnd()
   end
}

function new_rnd_gen()
   srand(rnd_meta.origin + rnd_meta.i)
   rnd_meta.i = 1+rnd()
   return setmetatable({seed=rnd(), i=0}, rnd_meta)
end

You can use them as independent generators each doing their own thing and you can optionally reset each one to its original state at any time. And You can lock one into a particular sequence by setting its seed attribute to some number.

r1 = new_rnd_gen()
r1.seed = 10

Here it is in action. It creates two generators r1 and r2. After generating a few numbers with each, r1 is reset and repeats while r2 just continues giving new values.

Cart #jonamamoju-0 | 2022-11-24 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
1


Oh I see what you're doing, @kimiyoribaka. Yeah I had to do that in 6502 machine-language but I try to avoid that with basic coding.

Doing that modulos to grab each element and you have to memorize each division. Nope. I prefer using up new-name arrays.

starsx[i] ... stars x-position
starxa[i] ... stars x-acceleration speed
starsc[i] ... stars color

@jasondelaat:

Yep. It is possible to call two random numbers with time() and wind-up getting the same time. It's a neat idea cause it changes as the code runs. I mean I know some early machine-language games would always show their logo and enter a loop that depending upon your keystroke would derive a random seed on that.

But for what I want which is instant seeding, I think using my own rnnd() function is best. Especially since I can't find a chink in its armor now with that test above.


By the way dw817, if you're not already aware, your rnnd function is essentially what's called a linear congruential generator. (see: https://en.wikipedia.org/wiki/Random_number_generation#Computational_methods)

Typically all the numbers would be integers but since Pico-8's numbers are fixed point, 16372.1 is, in this context and for all intents and purposes, an integer. In your case the shift value (b on the wiki page I linked) is zero. These type of RNGs are simple and fast but, as you discovered, the quality of the generated numbers depends on the factors you choose. There's nothing too special about 16372.1, plenty of other numbers should work just as well. But there will also be a bunch which produce garbage.

Which was a very long winded way of saying, yeah, for a wide range of purposes your function should do the trick.


3

Aww man ! I thought I came up with something completely original and cool !

Story of my life, @jasondelaat and guys ...

I know how it goes and it has gone. I remember Dad telling me, "Now I know you're excited right now, son. I can see that." =he laughs as I give him the look=

Dad continues, "However, what you've discovered here. I know you're proud of it. I can see that. However what you have here was something that was actually discovered over 100-years ago and it was called -" and he tells me the technical name for it.

And then I would start wailing, frustrated I didn't discover something unique ! :)

And he would try to coddle me saying it was very good for me to think up something like that as young as I was. And I would take some comfort in those words.

I feel a little like that now. Maybe not as much though. Nonetheless, as you stated, it certainly should work well enough for what it does, and that is generating a random number without using rnd() and random seeding without interfering with SRAND().

One thing I can definitely see with it, not now but later I will post a program to handle encryption. You can enter in the KEY for it and decrypt stuff.

This will be useful for data files I want to experiment with in Pico-8 later - when data files are sent through Twitter and other means.

Still no worries. No CMD.EXE or anything tricky like that. Just straight out 6-bit data.

Thanks ... for letting me know what it is.

"Linear Congruential Generator." LCG. Or in other words:

"Loss for a Computer Guru ..."

For now ...


@jasondelaat Your cart looks like it's doing the trick. So you could use R1 to get the stars in the initial example and R2 to keep getting other random (or, should I say, pseudorandom) values you want to keep unpredictable.

I hadn't seen metatables until just now, so I'm still wrapping my head around it. I skimmed at the wiki for it and looks like there's some interesting things there to play around with when I get the chance.


1

@dw817
I always like finding out the actual names of things because it usually leads me on to other interesting topics to learn about so I tend to assume other people like learning about them too. Not my intention to knock the wind out of your sails though. Sorry about that.

@mattu82
Yeah absolutely and you can create as many generators as you might want or need. Another neat thing I forgot to mention is that since the seed for each generator is created from both a base seed and an incremented value i the whole thing can be indexed at any point by setting i to whatever you want. Essentially it's an array of "random" values which you can index into so you can recreate subsets starting from wherever you want.

For instance, say you're generating 20 planets for a game and each planet has say 20 different things you want to be able to generate: description, climate, whatever. But you don't necessarily need them all at the same time. You have a few options:

  1. Create a generator for each planet, generate everything at once and then extract what you need when you need it. This way though you end up spending a lot of time generating info you're just going to throw away without using.

  2. Create a separate generator for each individual thing you want to generate. For 20 planets with 20 attributes each that would mean juggling 400 generators.

  3. Create a generator for each planet and use indices for each of the attributes. All planets use the same indices—description starts at index 100, climate at index 200, etc.—but on a different generator. Now you only need to keep track of 20 generators and 20 indices but you can still generate only the information you want when you want it.

An easy way to do it would be to change the definition of reset to this:

rnd_funcs = {
   reset=function(self, index)
      self.i = index or 0
   end
}

rnd1 = new_rnd_gen()
rnd1:reset(100) -- sets index to 100
rnd1:reset() -- resets back to 0 just like before


[Please log in to post a comment]