Files
balatro-gba/source/random.c
Geralt 0eb7042468
Some checks failed
Build and Deploy Doxygen Docs / docs (push) Has been cancelled
Deploy Nightly / Run Build ROM Workflow (push) Has been cancelled
Deploy Nightly / Deploy nightly (push) Has been cancelled
[Bug] Implement independent RNG sequences to avoid interferences (#599)
* Implement our own Xorshift32 RNG algo to manage independent number sequences

* Fix issues with seed 0

* adress @MeirGavish's review

* Apply suggestions from @MeirGavish's code review

Co-authored-by: MeirGavish <meir.gavish@gmail.com>

* adress the rest of @MeirGavish's review

* adress @MeirGavish's review

* Fix last issues

---------

Co-authored-by: MathisMartin31 <mathis.martin31@gmail.com>
Co-authored-by: MeirGavish <meir.gavish@gmail.com>
Co-authored-by: MonteCrysto <monte.crysto31@gmail.com>
2026-08-05 12:37:06 +03:00

81 lines
2.0 KiB
C

#include "random.h"
#include "game_variables.h"
#include "util.h"
#include <stdlib.h>
#include <tonc.h>
static void s_init_rng_states(void);
static void s_xorshift32(u32* state);
// Accumulate timer 1 into a bigger variable so we can generate more diverse seeds
static u32 s_timer_acc = 0;
// Timers usage docs: https://gbadev.net/tonc/timers.html
void rng_init(void)
{
REG_TM1D = 0;
REG_TM1CNT = TM_FREQ_1 | TM_ENABLE; // using timer with x1 prescale
}
void rng_update(void)
{
s_timer_acc += (u32)REG_TM1D;
}
void rng_shuffle_seed(void)
{
srand(s_timer_acc);
rng_set_seed(rand());
}
void rng_set_seed(u32 seed)
{
// We store the seed to display it at the end of the run, but here it's only used to generate
// the independent rng sequences' initial states. We also avoid the seed 0 as the Xorshift32
// method used will stay stuck.
u32 capped_seed = seed % (MAX_BASE36 + 1);
g_game_vars.rng_info.seed = (capped_seed == 0) ? MAX_BASE36 : capped_seed;
s_init_rng_states();
}
/**
* @brief Reset all independent RNG sequences to their initial states using the
* global custom seed.
*/
static inline void s_init_rng_states(void)
{
srand(g_game_vars.rng_info.seed);
for (enum RngSequence key = 0; key < RNG_SEQ_MAX; key++)
{
g_game_vars.rng_info.states[key] = rand();
}
}
u32 rng_get_u32(enum RngSequence key)
{
s_xorshift32(&g_game_vars.rng_info.states[key]);
return g_game_vars.rng_info.states[key];
}
/**
* @brief Transforms a given RNG state according to the Xorshift32 algorithm.
*
* Custom RNG had to be implemented to be able to manage several independent sequences, since
* `initstate` and `setstate` are POSIX and not available on GBA via devkitpro.
*
* @param state pointer to a 32-bit RNG state
*/
static inline void s_xorshift32(u32* state)
{
*(state) ^= *(state) << 13;
*(state) ^= *(state) >> 17;
*(state) ^= *(state) << 5;
}
void rng_restore(RngInfo info)
{
g_game_vars.rng_info = info;
}