RandSeed as constant

Good day!
My question is about randomization in game.
I need a map generator for the creation of the big worlds. For this i need stable RandSeed variable. I want to use it both for procedural generation and for the game saving.

How do I make RandSeed as constant for all units, functions and etc in during the game process?
As i understand, i need a unit with this variable in uses of all another units, that use randomization?

unit SeedUnit
var WorldSeed: Integer;
begin
  WorldSeed := 0;

and next

unit GameStateMain
interface
uses SeedUnit;
...
begin
  RandSeed := WorldSeed;

For all units?..

And maybe some more recommendations for procedural generation, if you already used it or see realizations in another games codes.

Indeed, you can use RandSeed to store random seed for your world. This way if the logic of your world generation always results in the same number of calls to Random then you can rely on that. I highlight that because while it looks trivial it actually isn’t. I’m not in the best mental shape today, but I’ll try to explain it more or less step-by-step a little bit later, let’s just then look first from the technical standpoint.

First of all, you have a great random number generator in FreePascal. But it has one big flaw - it’s only one random number generator. In other words, if you need one number generator for world and another for gameplay (e.g. chance to hit an enemy or similar) then you’ll really need to be creative with predefined seed (that defines your world) and random seed (that defines in-game behavior). What is worse, even if you separate those there is still a chance they’ll be getting in the way of each other. But this is still solvable - you just have to store somewhere the last RandSeed you were using.

Another solution (maybe much simpler) - in Castle Engine we have a ready plugin with random number generator GitHub - castle-engine/castle-random: Implementation of quick random based on Xorshift algorithm. This solves some of the problem mentioned above: you can create multiple instances of TCastleRandom (for example, one for world, another for gameplay) and each will have its own persistent Seed. As of stable FPC build it was even faster than its random implementation, however, I see that in the latest unstable (TRUNK) FPC their native random can be faster than ours :slight_smile:

Now for the hard part - generating world from the seed.

There are several approaches to procedural generation. I personally split them into two ways: Generate only and Generate and save. They can be used for different purposes.

The first one has a huge advantage that you don’t have to save the world. Whatever happens you just generate the world anew and given the static seed, it will always generate exactly the same way as it did before. Implementing such generator is relatively trivial, however, it becomes trickier as the size of the world increases (especially to semi-infinity) and it has to be chunked to work with. Another problem of this method is that you will have problems generating non-static objects this way (like pickable items) - next time you generate the world, they’ll again appear in their places, which is not always desirable. So either you have to generate them once and save (which partially defeats the benefit of this world) or you have to have it in game logic that they respawn constantly (in some ancient games stuff happened this way).

Next option is when you generate a world (or a single chunk) once, and then just save the result in the save game. It has a lot of benefits, however, obviously such method takes more storage (as in save game size) and memory (you can’t just “forget” the chunks and regenerate them on the fly, you have to implement load-unload mechanisms, or store everything generated in the RAM which also has limits). But this way you are not limited, e.g. you can even make modifiable terrain.

Generating huge worlds (in chunks) can be done either by generating all the chunks at start (will be very slow, take a lot of memory) or generating them as the Player advances (imposes some limitations on generation methods, e.g. celluar automatona-like algorithms will have problems working at borders with ungenerated chunks; but in turn technically allows semi-infinite worlds). You can also try to “force” same seed for every hypothetical chunk (e.g. using a deterministic formula to convert from “world seed” into “chunk seed”) or, maybe just don’t bother and keep on using the same (or even random) seed for generating the chunks if you save the result anyway. This way for the same seed the world can generate differently if the player goes left or right, but often it’s more than enough for a single-player games and you can skip problems with seeds for individual chunks.

I guess I’ve written the “theory” above a bit chaotically. Hopefully it could help you answer your questions and you can make sense of it.

Now for the examples, I guess the closest to what you are trying to achieve is EugeneLoza / Kryftolike · GitLab - this game generates a semi-infinite world as the Player progresses through the map. It uses the “last” version of the algorithm - there is no strict “world seed” and the same seed can produce different dungeons depending on where the Player goes.

The generation happens in code/gamemap.pas · master · EugeneLoza / Kryftolike · GitLab, or rather in code/gamemap.pas · master · EugeneLoza / Kryftolike · GitLab . In short what it does: it finds 3x3 chunks around the Player and calls GenerateChunk for each of them. Chunk generates a maze inside of it until the passages lead outside of the chunk. When all generated passages lead outside of the chunk, next chunk gets generation queue. And so on until no unfinished passages remain inside of the chunk we were working with (all passages lead outside of the generation area of 3x3 chunks).

There is more stuff going on there - by Voronoi polygons we create areas with slightly different generation parameters which result in them looking differently on the map. E.g. this is how biomes look like on zoom-out:

2 Likes

Thanks, it is a good explanation.
I will use two levels of generation:

  1. Generation of world map with thousands of regions;
  2. Generation every region when player will visit it. For this part i want to use global seed modified by numbers of region coordinates.

If I will need to add something in special place, will do it after generation, I think. Maybe few precents of quest locations will be semi-randomized.

Algorithm of first step in my had is that for now:

  1. Player inputs name of world and seed is calculated with ASCII numbers of name symbols;
  2. On map puts few numbered points - it will be a continents and islands in global ocean. And now i think about algorithm to grow grounds from this points. I want to make “random” number of ground cells around this anchors and put it in the “random” positions in “random” radius… Or something better to make more reslistic contours of continents…
  3. And next - same operation with forest, mountain and cities regions in map.

Maybe I will test this on simple canvas in Lazarus with colored pixels for different cells types, for approve that program draws something nice.
I don’t need a endless world, so, I can generate all base map immediately, I think? And game save this map and seed for regions after starts.