7413d107ae
Co-authored-by: Martin Griffin <martinrgriffin@gmail.com>
227 lines
6.4 KiB
C
227 lines
6.4 KiB
C
#ifndef GUARD_RANDOM_H
|
|
#define GUARD_RANDOM_H
|
|
|
|
// The number 1103515245 comes from the example implementation of rand and srand
|
|
// in the ISO C standard.
|
|
#define ISO_RANDOMIZE1(val) (1103515245 * (val) + 24691)
|
|
#define ISO_RANDOMIZE2(val) (1103515245 * (val) + 12345)
|
|
|
|
/* Some functions have been added to support Expansion's RNG implementation.
|
|
*
|
|
* LocalRandom(*val) provides a higher-quality replacement for uses of
|
|
* ISO_RANDOMIZE in vanilla Emerald. You can use LocalRandomSeed(u32) to
|
|
* create a local state.
|
|
*
|
|
* It is no longer possible to call Random() in interrupt handlers safely.
|
|
* AdvanceRandom() is provided to handle burning numbers in VBlank handlers.
|
|
* If you need to use random numbers in the VBlank handler, a local state
|
|
* should be used instead.
|
|
*
|
|
* Random2_32() was added, even though it is not used directly, because the
|
|
* underlying RNG always outputs 32 bits.
|
|
*/
|
|
|
|
struct Sfc32State {
|
|
u32 a;
|
|
u32 b;
|
|
u32 c;
|
|
u32 ctr;
|
|
};
|
|
|
|
typedef struct Sfc32State rng_value_t;
|
|
|
|
#define RNG_VALUE_EMPTY {}
|
|
|
|
// Calling this function directly is discouraged.
|
|
// Use LocalRandom() instead.
|
|
static inline u32 _SFC32_Next(struct Sfc32State *state)
|
|
{
|
|
const u32 result = state->a + state->b + state->ctr++;
|
|
state->a = state->b ^ (state->b >> 9);
|
|
state->b = state->c * 9;
|
|
state->c = result + ((state->c << 21) | (state->c >> 11));
|
|
return result;
|
|
}
|
|
|
|
static inline u16 LocalRandom(rng_value_t *val)
|
|
{
|
|
return _SFC32_Next(val) >> 16;
|
|
}
|
|
|
|
u32 Random32(void);
|
|
u32 Random2_32(void);
|
|
|
|
static inline u16 Random(void)
|
|
{
|
|
return Random32() >> 16;
|
|
}
|
|
|
|
void SeedRng(u32 seed);
|
|
void SeedRng2(u32 seed);
|
|
rng_value_t LocalRandomSeed(u32 seed);
|
|
|
|
static inline u16 Random2(void)
|
|
{
|
|
return Random2_32() >> 16;
|
|
}
|
|
|
|
void AdvanceRandom(void);
|
|
|
|
extern rng_value_t gRngValue;
|
|
extern rng_value_t gRng2Value;
|
|
|
|
void Shuffle8(void *data, size_t n);
|
|
void Shuffle16(void *data, size_t n);
|
|
void Shuffle32(void *data, size_t n);
|
|
void ShuffleN(void *data, size_t n, size_t size);
|
|
|
|
static inline void Shuffle(void *data, size_t n, size_t size)
|
|
{
|
|
switch (size)
|
|
{
|
|
case 1: Shuffle8(data, n); break;
|
|
case 2: Shuffle16(data, n); break;
|
|
case 4: Shuffle32(data, n); break;
|
|
default: ShuffleN(data, n, size); break;
|
|
}
|
|
}
|
|
|
|
/* Structured random number generator.
|
|
* Instead of the caller converting bits from Random() to a meaningful
|
|
* value, the caller provides metadata that is used to return the
|
|
* meaningful value directly. This allows code to interpret the random
|
|
* call, for example, battle tests know what the domain of a random call
|
|
* is, and can exhaustively test it.
|
|
*
|
|
* RandomTag identifies the purpose of the value.
|
|
*
|
|
* RandomUniform(tag, lo, hi) returns a number from lo to hi inclusive
|
|
* with uniform probability.
|
|
*
|
|
* RandomUniformExcept(tag, lo, hi, reject) returns a number from lo to
|
|
* hi inclusive with uniform probability, excluding those for which
|
|
* reject returns TRUE.
|
|
*
|
|
* RandomElement(tag, array) returns an element in array with uniform
|
|
* probability. The array must be known at compile-time (e.g. a global
|
|
* const array).
|
|
*
|
|
* RandomPercentage(tag, t) returns FALSE with probability 1-t/100,
|
|
* and TRUE with probability t/100.
|
|
*
|
|
* RandomWeighted(tag, w0, w1, ... wN) returns a number from 0 to N
|
|
* inclusive. The return value is proportional to the weights, e.g.
|
|
* RandomWeighted(..., 1, 1) returns 50% 0s and 50% 1s.
|
|
* RandomWeighted(..., 2, 1) returns 2/3 0s and 1/3 1s.
|
|
*
|
|
* RandomChance(tag, successes, total) returns FALSE with probability
|
|
* 1-successes/total, and TRUE with probability successes/total. */
|
|
|
|
enum RandomTag
|
|
{
|
|
RNG_NONE,
|
|
RNG_ACCURACY,
|
|
RNG_CONFUSION,
|
|
RNG_CRITICAL_HIT,
|
|
RNG_CURSED_BODY,
|
|
RNG_CUTE_CHARM,
|
|
RNG_DAMAGE_MODIFIER,
|
|
RNG_DIRE_CLAW,
|
|
RNG_EFFECT_SPORE,
|
|
RNG_FLAME_BODY,
|
|
RNG_FORCE_RANDOM_SWITCH,
|
|
RNG_FROZEN,
|
|
RNG_G_MAX_STUN_SHOCK,
|
|
RNG_G_MAX_BEFUDDLE,
|
|
RNG_G_MAX_REPLENISH,
|
|
RNG_G_MAX_SNOOZE,
|
|
RNG_HARVEST,
|
|
RNG_HITS,
|
|
RNG_HOLD_EFFECT_FLINCH,
|
|
RNG_INFATUATION,
|
|
RNG_LOADED_DICE,
|
|
RNG_METRONOME,
|
|
RNG_PARALYSIS,
|
|
RNG_PICKUP,
|
|
RNG_POISON_POINT,
|
|
RNG_POISON_TOUCH,
|
|
RNG_RAMPAGE_TURNS,
|
|
RNG_SECONDARY_EFFECT,
|
|
RNG_SECONDARY_EFFECT_2,
|
|
RNG_SECONDARY_EFFECT_3,
|
|
RNG_SHED_SKIN,
|
|
RNG_SLEEP_TURNS,
|
|
RNG_SPEED_TIE,
|
|
RNG_STATIC,
|
|
RNG_STENCH,
|
|
RNG_TOXIC_CHAIN,
|
|
RNG_TRI_ATTACK,
|
|
RNG_QUICK_DRAW,
|
|
RNG_QUICK_CLAW,
|
|
RNG_TRACE,
|
|
RNG_FICKLE_BEAM,
|
|
RNG_AI_ABILITY,
|
|
RNG_AI_SWITCH_HASBADODDS,
|
|
RNG_AI_SWITCH_BADLY_POISONED,
|
|
RNG_AI_SWITCH_CURSED,
|
|
RNG_AI_SWITCH_NIGHTMARE,
|
|
RNG_AI_SWITCH_SEEDED,
|
|
RNG_AI_SWITCH_ABSORBING,
|
|
RNG_AI_SWITCH_NATURAL_CURE,
|
|
RNG_AI_SWITCH_REGENERATOR,
|
|
RNG_AI_SWITCH_ENCORE,
|
|
RNG_AI_SWITCH_STATS_LOWERED,
|
|
RNG_AI_SWITCH_SE_DEFENSIVE,
|
|
RNG_SHELL_SIDE_ARM,
|
|
RNG_RANDOM_TARGET,
|
|
RNG_HEALER,
|
|
};
|
|
|
|
#define RandomWeighted(tag, ...) \
|
|
({ \
|
|
const u8 weights[] = { __VA_ARGS__ }; \
|
|
u32 sum, i; \
|
|
for (i = 0, sum = 0; i < ARRAY_COUNT(weights); i++) \
|
|
sum += weights[i]; \
|
|
RandomWeightedArray(tag, sum, ARRAY_COUNT(weights), weights); \
|
|
})
|
|
|
|
#define RandomChance(tag, successes, total) (RandomWeighted(tag, total - successes, successes))
|
|
|
|
#define RandomPercentage(tag, t) \
|
|
({ \
|
|
u32 r; \
|
|
if (t <= 0) \
|
|
{ \
|
|
r = FALSE; \
|
|
} \
|
|
else if (t >= 100) \
|
|
{ \
|
|
r = TRUE; \
|
|
} \
|
|
else \
|
|
{ \
|
|
const u8 weights[] = { 100 - t, t }; \
|
|
r = RandomWeightedArray(tag, 100, ARRAY_COUNT(weights), weights); \
|
|
} \
|
|
r; \
|
|
})
|
|
|
|
#define RandomElement(tag, array) \
|
|
({ \
|
|
*(typeof((array)[0]) *)RandomElementArray(tag, array, sizeof((array)[0]), ARRAY_COUNT(array)); \
|
|
})
|
|
|
|
u32 RandomUniform(enum RandomTag, u32 lo, u32 hi);
|
|
u32 RandomUniformExcept(enum RandomTag, u32 lo, u32 hi, bool32 (*reject)(u32));
|
|
u32 RandomWeightedArray(enum RandomTag, u32 sum, u32 n, const u8 *weights);
|
|
const void *RandomElementArray(enum RandomTag, const void *array, size_t size, size_t count);
|
|
|
|
u32 RandomUniformDefault(enum RandomTag, u32 lo, u32 hi);
|
|
u32 RandomUniformExceptDefault(enum RandomTag, u32 lo, u32 hi, bool32 (*reject)(u32));
|
|
u32 RandomWeightedArrayDefault(enum RandomTag, u32 sum, u32 n, const u8 *weights);
|
|
const void *RandomElementArrayDefault(enum RandomTag, const void *array, size_t size, size_t count);
|
|
|
|
u8 RandomWeightedIndex(u8 *weights, u8 length);
|
|
|
|
#endif // GUARD_RANDOM_H
|