Recipes
This page shows practical, production-style patterns built on the current public API.
Recipe: Stable Chunk RNG from World Seed
#include <grimoire-pcg/random.h>
static hash_t hash_chunk_seed(hash_t worldSeed, int32_t chunkX, int32_t chunkY)
{
/* Simple deterministic combiner for example purposes. */
uint32_t h = (uint32_t)worldSeed;
h ^= (uint32_t)chunkX * 0x9E3779B9u;
h ^= (uint32_t)chunkY * 0x85EBCA6Bu;
h ^= h >> 16;
return (hash_t)h;
}
int generate_chunk_loot(hash_t worldSeed, int32_t chunkX, int32_t chunkY)
{
hash_t chunkSeed = hash_chunk_seed(worldSeed, chunkX, chunkY);
GrimoireRandom rng = GrimoireRandom_CreateSeed(chunkSeed);
if (!rng) return -1;
int lootTier = GrimoireRandom_NextRange(rng, 0, 5);
GrimoireRandom_Destroy(rng);
return lootTier;
}
Use this when each spatial chunk should be reproducible independently.
Recipe: Save/Load RNG Stream Position with Metadata
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <grimoire-pcg/random.h>
enum { RNG_STATE_SIZE = 228 };
enum { RNG_CHECKPOINT_VERSION = 1 };
typedef struct RngCheckpoint
{
uint32_t version;
uint32_t size;
uint8_t state[RNG_STATE_SIZE];
} RngCheckpoint;
bool save_rng(GrimoireRandom rng, RngCheckpoint* outCp)
{
if (!rng || !outCp) return false;
outCp->version = RNG_CHECKPOINT_VERSION;
outCp->size = RNG_STATE_SIZE;
GrimoireRandom_Serialize(rng, outCp->state);
return true;
}
GrimoireRandom load_rng(const RngCheckpoint* inCp)
{
if (!inCp) return NULL;
if (inCp->version != RNG_CHECKPOINT_VERSION) return NULL;
if (inCp->size != RNG_STATE_SIZE) return NULL;
return GrimoireRandom_Deserialize(inCp->state);
}
This lets save files fail safely if the checkpoint shape is not what you expect.
Recipe: 2D Height + Moisture Field for Biome Selection
#include <grimoire-pcg/noise.h>
typedef struct BiomeSample
{
float height;
float moisture;
} BiomeSample;
BiomeSample sample_biome_fields(float x, float y, hash_t worldSeed)
{
GrimoireFractalSettings heightSet;
heightSet.frequency = 0.008f;
heightSet.octaves = 6;
heightSet.lacunarity = 2.0f;
heightSet.persistence = 0.5f;
heightSet.staticSeed = true;
GrimoireFractalSettings moistSet;
moistSet.frequency = 0.014f;
moistSet.octaves = 4;
moistSet.lacunarity = 2.0f;
moistSet.persistence = 0.55f;
moistSet.staticSeed = true;
BiomeSample out;
out.height = Grimoire_Fbm(Grimoire_Perlin2D, x, y, 0.0f, worldSeed, &heightSet);
out.moisture = Grimoire_Fbm(Grimoire_ValueSmooth2D, x, y, 0.0f, worldSeed + 101, &moistSet);
return out;
}
Use separate frequencies and seeds to avoid strongly correlated biome axes.
Recipe: Animated Cloud Mask (Time as Z)
#include <grimoire-pcg/noise.h>
float cloud_mask(float x, float y, float tSeconds, hash_t seed)
{
GrimoireFractalSettings s;
s.frequency = 0.015f;
s.octaves = 5;
s.lacunarity = 2.0f;
s.persistence = 0.5f;
s.staticSeed = true;
float z = tSeconds * 0.08f;
return Grimoire_Billow(Grimoire_Perlin3D, x, y, z, seed, &s);
}
Sampling 3D noise with time in z gives smooth animation without frame-to-frame pops.
Recipe: Deterministic Byte Buffer Fill
#include <stdint.h>
#include <grimoire-pcg/random.h>
void fill_bytes(hash_t seed, uint8_t* buffer, size_t length)
{
GrimoireRandom rng = GrimoireRandom_CreateSeed(seed);
if (!rng) return;
GrimoireRandom_NextBytes(rng, buffer, length);
GrimoireRandom_Destroy(rng);
}
Use this for procedural data blobs, not security-sensitive entropy.
Recipe: Replay-Safe RNG Branching
#include <grimoire-pcg/random.h>
void generate_encounter_and_loot(hash_t seed)
{
GrimoireRandom master = GrimoireRandom_CreateSeed(seed);
if (!master) return;
GrimoireRandom encounterRng = GrimoireRandom_Clone(master);
GrimoireRandom lootRng = GrimoireRandom_Clone(master);
if (encounterRng) {
int enemyCount = GrimoireRandom_NextRange(encounterRng, 2, 8);
(void)enemyCount;
}
if (lootRng) {
int rarity = GrimoireRandom_NextRange(lootRng, 0, 1000);
(void)rarity;
}
GrimoireRandom_Destroy(lootRng);
GrimoireRandom_Destroy(encounterRng);
GrimoireRandom_Destroy(master);
}
Branching from a shared snapshot keeps subsystems deterministic while allowing independent call counts.