Log In  


Cart #procedural_map_demo-0 | 2025-03-25 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
3


I had an idea to use a fast counter-based pseudo-random number generator (CBRNG) to try to procedurally generate information for an unnecessarily large game world that then does not have to be held in memory (or at least the default state does not, if one were to allow modifications those would still need to be stored in RAM)

I thought to do this largely because the PICO-8 platform is quite rich in available compute resource and limited in RAM.

So in this demo, you control a reticle and moving glides you and the screen in lockstep around the large world.

Holding down a direction key will accelerate you linearly.. if you build up speed for N seconds it ought to take another N seconds after you let go before all that speed bleeds off again.

World coordinates are printed in upper left (currently the coordinates of the tile drawn at upper left corner of screen) and you ought to be able to explore 2^32 tiles square (so ~4.3billion tiles wide and tall). Each tile is 8x8 pixels, and player position is tracked to the pixel within this environment.

I'm not currently making use of the CBRNG output beyond coloring each tile green or black with an (pseudo-)uncorrelated 50% probability, but the way I have this set up the CBRNG ought to cough up 32 bits of unique data per tile in this world that can then be used to craft procedurally generated content where and however one wishes.

I haven't used enough rigor to make strong guarantees about whether or how the world data might repeat in different locations, but if I'm very lucky it might not show any strong repeating anywhere. I am also posting this in advance of having tried to explore the edges of the coordinate space, but I am hoping that it just wraps around.

But to exemplify what's being done, find an interesting looking shape. Screenshot it if you need to, and try to remember the rough coordinates for the screen containing it (or let the sceenshot do that).

Now, try to leave that screen behind and encounter that shape anywhere else.
Now try to roam some good distance in any direction, then come back and return to those same coordinates and confirm that the shape is still there. 🙂

3


Even though I've seen this sort of thing before, it never ceases to amaze me just how much ridonculous stuff we can do with a humble RNG. Grat work!


Spent a lot of time wandering the map : if you allow yourself only to move from green to green orthogonally, you can find island that can span over a few screens, but it seems to always be bound.
If you move like a chess queen, it seems you can always find a way in any direction you want, with small detours here and there...

Now, I'm wondering, on an infinite random grid with 50%/50%, is the probability to have an infinite orthogonal island 0% and an infinite "queen" island 100% ?
What if we changed the 50%/50% ?
Would we get a chance to have infinite orthogonal island with 99% green, for example...


Based on https://www.youtube.com/watch?v=ZZY9YE7rZJw

This routine should be a little bit smaller:

_randomseed=0
function myrandom()
 _randomseed += 0xe120.fc15
 local tmp= _randomseed * 0x4a39.b70d
 local m1 = (tmp >> 16) ~ tmp
 tmp= m1 * 0x12fa.d5c9
 return (tmp >> 16) ~ tmp
end

seed=0xc8e4.fd15

function _update()
end

function _draw()
 for y=0,127 do
   for x=0,127 do
     -- a good random routine should here create a complete random chaotic picture
     _randomseed=seed+x+y*128
     pset(x,y,myrandom()%16)

   end
 end

end

@GPI perhaps that's a faster random number generator, but so is using the built-in one. The Squares CBRNG I've used is meant to be counter-based, so that I can offer potentially both a 32-bit X and 32-bit Y value as input and get a full 32-bit pseudo-random number as repeatable output.

It's not just about quickly filling the screen with noise, but about filling a large world with noise that the screen can traverse freely, ideally without repeats over any moderate distance in any direction.

For example your combination of "x+y" in the seed means that 10+3 and 3+10 are guaranteed to give you the same output.


it is not x+y, it is x+y*128 - for this example. But when you want 16-Bit coordinates try x|y>>>16. Of course, 32-bit-coordinates are a lot bigger, but the question is, how big is too big?

And oh, I forgot, that srand exists, lets test all methods

seed=0xc8e4.fd15
format=1

function _update()
 if btnp(❎) then
   format=1-format
 end
end

function squares_cbrng(key, counter)
 local t, x, y, z

 x = counter * key
 y = x
 z = y + key

 x = x*x + y
 x = bor((x>>>16), (x<<16)) -- round 1

 x = x*x + z
 x = bor((x>>>16), (x<<16)) -- round 2

 x = x*x + y
 x = bor((x>>>16), (x<<16)) -- round 3

 x = x*x + z
 t = x
 x = bor((x>>>16), (x<<16)) -- round 4

 return(bxor(t, ((x*x + y) >>> 16))) -- round 5
end

--lehmer
_randomseed=0
function myrandom()
 _randomseed += 0xe120.fc15
 local tmp= _randomseed * 0x4a39.b70d
 local m1 = (tmp >> 16) ~ tmp
 tmp= m1 * 0x12fa.d5c9
 return (tmp >> 16) ~ tmp
end

function _draw()
 size={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[0]=0}
 maxnb=0
 cls()

 for y=0,127 do
   for x=0,127 do
     srand(seed+(x|y>>>16))
     r=rnd(16)\1

     --_randomseed=seed+(x|y>>>16)
     --r=myrandom()%16\1

     --r=squares_cbrng(seed,x|y>>>16)%16\1

     size[r]+=1
     maxnb=max(maxnb,size[r])
     if format==0 then
       pset(x,y,r)
     end
   end
 end

 if format==1 then
  for i=0,15 do
   rectfill(i*8,0,i*8+7,100/maxnb*size[i],i%15+1)
   print(size[i],i*8,120-(i%3)*8,5)
  end
 end

 s=stat(1)
 print(s,1,0,0)
 print(s,1,2,0)
 print(s,0,1,0)
 print(s,2,1,0)
 print(s,1,1,7)
end

And the result is useable. The program shows first the result how often a number between 0 and 15 was choosen. with X-Button shows the noise-picture. Also the cpu-usage is displayed.

the build-in rnd generates a nice noise and use every number nearly equal.

build in routine: cpu usage is 4.1905 and 4.6417

lehmer random: is faster - 3.7222 and 4.1729

squares_cbrng: is slower - 5.3628 and 5.8136

All three have benefits and disadvantages.

And in the video from above - he tested some buildin c++ random generator and they generate stripes in his tests...


@GPI: Ah, my perception of your code is foiled by order of operations. 😁

And I'll certainly be glad to avoid stripes.

I'll post more updates if/when I'm able to muster the brainpower to begin to harness said noise to create potentially interesting terrain and gameplay options (as well as more intuitive player movement, which is perennially one of the things I have more difficulty sorting out. Heh heh!)



[Please log in to post a comment]