a93e5d4cdc
Plain-C, dependency-free engine that produces a personalized daily Celtic Cross tarot spread and natal-chart/transit astrology reading, plus a standalone CLI (dist/deck-engine) and run.sh wrapper. Includes English/German output via a simple key=value translation format, and docs/diagrams describing the architecture and I/O.
39 lines
1.4 KiB
C
39 lines
1.4 KiB
C
#include "rng.h"
|
|
|
|
uint64_t rng_seed_from_string(const char *seed) {
|
|
uint64_t hash = 0xcbf29ce484222325ULL; /* FNV offset basis */
|
|
for (const unsigned char *p = (const unsigned char *)seed; *p; p++) {
|
|
hash ^= (uint64_t)*p;
|
|
hash *= 0x100000001b3ULL; /* FNV prime */
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
void rng_init(RngState *rng, uint64_t seed) {
|
|
/* splitmix64 requires a nonzero-friendly state; any seed works, but
|
|
* avoid the degenerate all-zero stream for the empty string. */
|
|
rng->state = seed ? seed : 0x9e3779b97f4a7c15ULL;
|
|
}
|
|
|
|
uint64_t rng_next_u64(RngState *rng) {
|
|
uint64_t z = (rng->state += 0x9e3779b97f4a7c15ULL);
|
|
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
|
|
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
|
|
return z ^ (z >> 31);
|
|
}
|
|
|
|
uint32_t rng_next_bounded(RngState *rng, uint32_t bound) {
|
|
/* Rejection sampling against the largest multiple of `bound` that fits
|
|
* in 32 bits, so every outcome in [0, bound) is equally likely. `limit`
|
|
* must stay 64-bit: when `bound` evenly divides 2^32 (e.g. bound == 2,
|
|
* used for every reversed-card coin flip), the true limit is 2^32
|
|
* itself, which truncates to 0 in a uint32_t and turns the loop below
|
|
* into an infinite one. */
|
|
uint64_t limit = 0x100000000ULL - (0x100000000ULL % bound);
|
|
uint64_t value;
|
|
do {
|
|
value = rng_next_u64(rng) & 0xffffffffULL;
|
|
} while (value >= limit);
|
|
return (uint32_t)(value % bound);
|
|
}
|