Optional high-quality RNG (#3780)
High-quality RNG, behind the HQ_RANDOM flag, enabled by default Makes the shuffle test error a bit more tolerant
This commit is contained in:
parent
63316daf30
commit
fb28ce50ae
20 changed files with 324 additions and 50 deletions
|
@ -15,6 +15,7 @@
|
|||
#include "pokeball.h"
|
||||
#include "battle_debug.h"
|
||||
#include "battle_dynamax.h"
|
||||
#include "random.h" // for rng_value_t
|
||||
|
||||
// Used to exclude moves learned temporarily by Transform or Mimic
|
||||
#define MOVE_IS_PERMANENT(battler, moveSlot) \
|
||||
|
@ -580,6 +581,13 @@ struct LostItem
|
|||
u16 stolen:1;
|
||||
};
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
struct BattleVideo {
|
||||
u32 battleTypeFlags;
|
||||
rng_value_t rngSeed;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct BattleStruct
|
||||
{
|
||||
u8 turnEffectsTracker;
|
||||
|
@ -651,7 +659,12 @@ struct BattleStruct
|
|||
u16 lastTakenMoveFrom[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT]; // a 2-D array [target][attacker]
|
||||
union {
|
||||
struct LinkBattlerHeader linkBattlerHeader;
|
||||
|
||||
#if HQ_RANDOM == FALSE
|
||||
u32 battleVideo[2];
|
||||
#else
|
||||
struct BattleVideo battleVideo;
|
||||
#endif
|
||||
} multiBuffer;
|
||||
u8 wishPerishSongState;
|
||||
u8 wishPerishSongBattlerId;
|
||||
|
|
|
@ -79,5 +79,6 @@
|
|||
#define EXPANSION_INTRO TRUE // If TRUE, a custom RHH intro will play after the vanilla copyright screen.
|
||||
#define POKEDEX_PLUS_HGSS FALSE // If TRUE, enables the custom HGSS style Pokedex.
|
||||
#define SUMMARY_SCREEN_NATURE_COLORS TRUE // If TRUE, nature-based stat boosts and reductions will be red and blue in the summary screen.
|
||||
#define HQ_RANDOM TRUE // If TRUE, replaces the default RNG with an implementation of SFC32 RNG. May break code that relies on RNG.
|
||||
|
||||
#endif // GUARD_CONFIG_H
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "palette.h"
|
||||
#include "constants/contest.h"
|
||||
#include "random.h" // for rng_value_t
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -327,7 +328,7 @@ extern struct ContestResources *gContestResources;
|
|||
extern struct ContestWinner gCurContestWinner;
|
||||
extern u8 gCurContestWinnerIsForArtist;
|
||||
extern u8 gCurContestWinnerSaveIdx;
|
||||
extern u32 gContestRngValue;
|
||||
extern rng_value_t gContestRngValue;
|
||||
|
||||
// contest.c
|
||||
void ResetLinkContestBoolean(void);
|
||||
|
|
|
@ -1,8 +1,74 @@
|
|||
#ifndef GUARD_RANDOM_H
|
||||
#define GUARD_RANDOM_H
|
||||
|
||||
extern u32 gRngValue;
|
||||
extern u32 gRng2Value;
|
||||
// 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 HQ_RANDOM.
|
||||
*
|
||||
* If using HQ_RANDOM, you cannot call Random() in interrupt handlers safely.
|
||||
* AdvanceRandom() is provided to handle burning numbers in the VBlank handler
|
||||
* if you choose to do that, and can be used regardless of HQ_RANDOM setting.
|
||||
* If you need to use random numbers in the VBlank handler, a local state
|
||||
* should be used instead.
|
||||
*
|
||||
* LocalRandom(*val) allows you to have local random states that are the same
|
||||
* type as the global states regardless of HQ_RANDOM setting, which is useful
|
||||
* if you want to be able to set them from or assign them to gRngValue.
|
||||
*
|
||||
* Random2_32() was added to HQ_RANDOM because the output of the generator is
|
||||
* always 32 bits and Random()/Random2() are just wrappers in that mode. It is
|
||||
* also available in non-HQ mode for consistency.
|
||||
*/
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
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;
|
||||
}
|
||||
|
||||
static inline u16 Random2(void)
|
||||
{
|
||||
return Random2_32() >> 16;
|
||||
}
|
||||
|
||||
void AdvanceRandom(void);
|
||||
#else
|
||||
typedef u32 rng_value_t;
|
||||
|
||||
#define RNG_VALUE_EMPTY 0
|
||||
|
||||
//Returns a 16-bit pseudorandom number
|
||||
u16 Random(void);
|
||||
|
@ -10,11 +76,23 @@ u16 Random2(void);
|
|||
|
||||
//Returns a 32-bit pseudorandom number
|
||||
#define Random32() (Random() | (Random() << 16))
|
||||
#define Random2_32() (Random2() | (Random2() << 16))
|
||||
|
||||
// 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)
|
||||
static inline u16 LocalRandom(rng_value_t *val)
|
||||
{
|
||||
*val = ISO_RANDOMIZE1(*val);
|
||||
return *val >> 16;
|
||||
}
|
||||
|
||||
static inline void AdvanceRandom(void)
|
||||
{
|
||||
Random();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern rng_value_t gRngValue;
|
||||
extern rng_value_t gRng2Value;
|
||||
|
||||
//Sets the initial seed value of the pseudorandom number generator
|
||||
void SeedRng(u16 seed);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define GUARD_RECORDED_BATTLE_H
|
||||
|
||||
#include "constants/battle.h"
|
||||
#include "random.h"
|
||||
|
||||
#define BATTLER_RECORD_SIZE 664
|
||||
|
||||
|
@ -13,7 +14,7 @@ struct RecordedBattleSave
|
|||
u8 playersGender[MAX_BATTLERS_COUNT];
|
||||
u32 playersTrainerId[MAX_BATTLERS_COUNT];
|
||||
u8 playersLanguage[MAX_BATTLERS_COUNT];
|
||||
u32 rngSeed;
|
||||
rng_value_t rngSeed;
|
||||
u32 battleFlags;
|
||||
u8 playersBattlers[MAX_BATTLERS_COUNT];
|
||||
u16 opponentA;
|
||||
|
@ -49,8 +50,8 @@ enum
|
|||
RECORDED_ITEM_MOVE,
|
||||
};
|
||||
|
||||
extern u32 gRecordedBattleRngSeed;
|
||||
extern u32 gBattlePalaceMoveSelectionRngValue;
|
||||
extern rng_value_t gRecordedBattleRngSeed;
|
||||
extern rng_value_t gBattlePalaceMoveSelectionRngValue;
|
||||
extern u8 gRecordedBattleMultiplayerId;
|
||||
|
||||
#define B_RECORD_MODE_RECORDING 1
|
||||
|
|
|
@ -855,7 +855,7 @@ void ClearFlagAfterTest(void);
|
|||
void OpenPokemon(u32 sourceLine, u32 side, u32 species);
|
||||
void ClosePokemon(u32 sourceLine);
|
||||
|
||||
void RNGSeed_(u32 sourceLine, u32 seed);
|
||||
void RNGSeed_(u32 sourceLine, rng_value_t seed);
|
||||
void AIFlags_(u32 sourceLine, u32 flags);
|
||||
void AILogScores(u32 sourceLine);
|
||||
void Gender_(u32 sourceLine, u32 gender);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "constants/songs.h"
|
||||
#include "constants/trainers.h"
|
||||
#include "recorded_battle.h"
|
||||
#include "random.h"
|
||||
|
||||
static void LinkOpponentHandleLoadMonSprite(u32 battler);
|
||||
static void LinkOpponentHandleSwitchInAnim(u32 battler);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "constants/songs.h"
|
||||
#include "constants/trainers.h"
|
||||
#include "recorded_battle.h"
|
||||
#include "random.h"
|
||||
|
||||
static void LinkPartnerHandleLoadMonSprite(u32 battler);
|
||||
static void LinkPartnerHandleSwitchInAnim(u32 battler);
|
||||
|
|
|
@ -1713,9 +1713,16 @@ static void CB2_HandleStartMultiBattle(void)
|
|||
case 8:
|
||||
if (IsLinkTaskFinished())
|
||||
{
|
||||
#if HQ_RANDOM == TRUE
|
||||
struct BattleVideo *ptr = &gBattleStruct->multiBuffer.battleVideo;
|
||||
ptr->battleTypeFlags = gBattleTypeFlags;
|
||||
ptr->rngSeed = gRecordedBattleRngSeed;
|
||||
#else
|
||||
u32 *ptr = gBattleStruct->multiBuffer.battleVideo;
|
||||
ptr[0] = gBattleTypeFlags;
|
||||
ptr[1] = gRecordedBattleRngSeed; // UB: overwrites berry data
|
||||
#endif
|
||||
|
||||
SendBlock(BitmaskAllOtherLinkPlayers(), ptr, sizeof(gBattleStruct->multiBuffer.battleVideo));
|
||||
gBattleCommunication[MULTIUSE_STATE]++;
|
||||
}
|
||||
|
@ -2053,7 +2060,7 @@ void VBlankCB_Battle(void)
|
|||
{
|
||||
// Change gRngSeed every vblank unless the battle could be recorded.
|
||||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_RECORDED)))
|
||||
Random();
|
||||
AdvanceRandom();
|
||||
|
||||
SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_X);
|
||||
SetGpuReg(REG_OFFSET_BG0VOFS, gBattle_BG0_Y);
|
||||
|
|
|
@ -358,7 +358,7 @@ EWRAM_DATA bool8 gCurContestWinnerIsForArtist = 0;
|
|||
EWRAM_DATA u8 gCurContestWinnerSaveIdx = 0;
|
||||
|
||||
// IWRAM common vars.
|
||||
u32 gContestRngValue;
|
||||
rng_value_t gContestRngValue;
|
||||
|
||||
extern const u8 gText_LinkStandby4[];
|
||||
extern const u8 gText_BDot[];
|
||||
|
@ -1709,7 +1709,7 @@ static void Task_AppealSetup(u8 taskId)
|
|||
if (++gTasks[taskId].data[0] > 19)
|
||||
{
|
||||
eContest.turnNumber = 0;
|
||||
eContest.unusedRng = gRngValue;
|
||||
eContest.unusedRng = 0;
|
||||
if ((gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK) && IsPlayerLinkLeader())
|
||||
{
|
||||
s32 i;
|
||||
|
@ -6109,4 +6109,3 @@ void StripPlayerAndMonNamesForLinkContest(struct ContestPokemon *mon, s32 langua
|
|||
name[PLAYER_NAME_LENGTH] = EOS;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2655,8 +2655,7 @@ void GenerateContestRand(void)
|
|||
|
||||
if (gLinkContestFlags & LINK_CONTEST_FLAG_IS_LINK)
|
||||
{
|
||||
gContestRngValue = ISO_RANDOMIZE1(gContestRngValue);
|
||||
random = gContestRngValue >> 16;
|
||||
random = LocalRandom(&gContestRngValue);
|
||||
result = &gSpecialVar_Result;
|
||||
}
|
||||
else
|
||||
|
@ -2669,8 +2668,7 @@ void GenerateContestRand(void)
|
|||
|
||||
u16 GetContestRand(void)
|
||||
{
|
||||
gContestRngValue = ISO_RANDOMIZE1(gContestRngValue);
|
||||
return gContestRngValue >> 16;
|
||||
return LocalRandom(&gContestRngValue);
|
||||
}
|
||||
|
||||
bool8 LinkContestWaitForConnection(void)
|
||||
|
|
|
@ -2144,7 +2144,7 @@ static void DebugAction_Util_Player_Gender(u8 taskId)
|
|||
|
||||
static void DebugAction_Util_Player_Id(u8 taskId)
|
||||
{
|
||||
u32 trainerId = ((Random() << 16) | Random());
|
||||
u32 trainerId = Random32();
|
||||
SetTrainerId(trainerId, gSaveBlock2Ptr->playerTrainerId);
|
||||
Debug_DestroyMenu_Full(taskId);
|
||||
ScriptContext_Enable();
|
||||
|
|
|
@ -127,7 +127,7 @@ void MoveSaveBlocks_ResetHeap(void)
|
|||
gMain.vblankCallback = vblankCB;
|
||||
|
||||
// create a new encryption key
|
||||
encryptionKey = (Random() << 16) + (Random());
|
||||
encryptionKey = Random32();
|
||||
ApplyNewEncryptionKeyToAllEncryptedData(encryptionKey);
|
||||
gSaveBlock2Ptr->encryptionKey = encryptionKey;
|
||||
}
|
||||
|
|
|
@ -23,9 +23,7 @@ static u8 GetMatchingDigits(u16, u16);
|
|||
|
||||
void ResetLotteryCorner(void)
|
||||
{
|
||||
u16 rand = Random();
|
||||
|
||||
SetLotteryNumber((Random() << 16) | rand);
|
||||
SetLotteryNumber(Random32());
|
||||
VarSet(VAR_POKELOT_PRIZE_ITEM, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -373,7 +373,7 @@ static void VBlankIntr(void)
|
|||
TryReceiveLinkBattleData();
|
||||
|
||||
if (!gMain.inBattle || !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_RECORDED)))
|
||||
Random();
|
||||
AdvanceRandom();
|
||||
|
||||
UpdateWirelessStatusIndicatorSprite();
|
||||
|
||||
|
|
130
src/random.c
130
src/random.c
|
@ -4,12 +4,108 @@
|
|||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
EWRAM_DATA static u8 sUnknown = 0;
|
||||
EWRAM_DATA static u32 sRandCount = 0;
|
||||
|
||||
// IWRAM common
|
||||
u32 gRngValue;
|
||||
u32 gRng2Value;
|
||||
rng_value_t gRngValue;
|
||||
rng_value_t gRng2Value;
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
|
||||
EWRAM_DATA static volatile bool8 sRngLoopUnlocked;
|
||||
|
||||
// Streams allow generators seeded the same to have separate outputs.
|
||||
#define STREAM1 1
|
||||
#define STREAM2 29
|
||||
|
||||
// A variant of SFC32 that lets you change the stream.
|
||||
// stream can be any odd number.
|
||||
static inline u32 _SFC32_Next_Stream(struct Sfc32State *state, const u8 stream)
|
||||
{
|
||||
const u32 result = state->a + state->b + state->ctr;
|
||||
state->ctr += stream;
|
||||
state->a = state->b ^ (state->b >> 9);
|
||||
state->b = state->c * 9;
|
||||
state->c = result + ((state->c << 21) | (state->c >> 11));
|
||||
return result;
|
||||
}
|
||||
|
||||
static void SFC32_Seed(struct Sfc32State *state, u16 seed, u8 stream)
|
||||
{
|
||||
u32 i;
|
||||
state->a = state->b = 0;
|
||||
state->c = seed;
|
||||
state->ctr = stream;
|
||||
for(i = 0; i < 16; i++)
|
||||
{
|
||||
_SFC32_Next_Stream(state, stream);
|
||||
}
|
||||
}
|
||||
|
||||
/*This ASM implementation uses some shortcuts and is generally faster on the GBA.
|
||||
* It's not necessarily faster if inlined, or on other platforms. */
|
||||
u32 NAKED Random32(void)
|
||||
{
|
||||
asm(".thumb\n\
|
||||
push {r4, r5, r6}\n\
|
||||
mov r6, #11\n\
|
||||
ldr r5, =gRngValue\n\
|
||||
ldmia r5!, {r1, r2, r3, r4}\n\
|
||||
@ e (result) = a + b + d++\n\
|
||||
add r1, r1, r2\n\
|
||||
add r0, r1, r4\n\
|
||||
add r4, r4, #" STR(STREAM1) "\n\
|
||||
@ a = b ^ (b >> 9)\n\
|
||||
lsr r1, r2, #9\n\
|
||||
eor r1, r1, r2\n\
|
||||
@ b = c + (c << 3) [c * 9]\n\
|
||||
lsl r2, r3, #3\n\
|
||||
add r2, r2, r3\n\
|
||||
@ c = rol(c, 21) + e\n\
|
||||
ror r3, r3, r6\n\
|
||||
add r3, r3, r0\n\
|
||||
sub r5, r5, #16\n\
|
||||
stmia r5!, {r1, r2, r3, r4}\n\
|
||||
pop {r4, r5, r6}\n\
|
||||
bx lr\n\
|
||||
.ltorg"
|
||||
);
|
||||
}
|
||||
|
||||
u32 Random2_32(void)
|
||||
{
|
||||
return _SFC32_Next_Stream(&gRng2Value, STREAM2);
|
||||
}
|
||||
|
||||
void SeedRng(u16 seed)
|
||||
{
|
||||
struct Sfc32State state;
|
||||
SFC32_Seed(&state, seed, STREAM1);
|
||||
|
||||
sRngLoopUnlocked = FALSE;
|
||||
gRngValue = state;
|
||||
sRngLoopUnlocked = TRUE;
|
||||
}
|
||||
|
||||
void SeedRng2(u16 seed)
|
||||
{
|
||||
SFC32_Seed(&gRng2Value, seed, STREAM2);
|
||||
}
|
||||
|
||||
void AdvanceRandom(void)
|
||||
{
|
||||
if (sRngLoopUnlocked == TRUE)
|
||||
Random32();
|
||||
}
|
||||
|
||||
#define LOOP_RANDOM_START \
|
||||
struct Sfc32State *const state = &gRngValue; \
|
||||
sRngLoopUnlocked = FALSE;
|
||||
|
||||
#define LOOP_RANDOM_END sRngLoopUnlocked = TRUE;
|
||||
|
||||
#define LOOP_RANDOM ((u16)(_SFC32_Next(state) >> 16))
|
||||
|
||||
#else
|
||||
EWRAM_DATA static u32 sRandCount = 0;
|
||||
|
||||
u16 Random(void)
|
||||
{
|
||||
|
@ -21,7 +117,6 @@ u16 Random(void)
|
|||
void SeedRng(u16 seed)
|
||||
{
|
||||
gRngValue = seed;
|
||||
sUnknown = 0;
|
||||
}
|
||||
|
||||
void SeedRng2(u16 seed)
|
||||
|
@ -35,15 +130,24 @@ u16 Random2(void)
|
|||
return gRng2Value >> 16;
|
||||
}
|
||||
|
||||
#define LOOP_RANDOM_START
|
||||
#define LOOP_RANDOM_END
|
||||
|
||||
#define LOOP_RANDOM (Random())
|
||||
|
||||
#endif
|
||||
|
||||
#define SHUFFLE_IMPL \
|
||||
u32 tmp; \
|
||||
LOOP_RANDOM_START; \
|
||||
--n; \
|
||||
while (n > 1) \
|
||||
{ \
|
||||
int j = (Random() * (n+1)) >> 16; \
|
||||
int j = (LOOP_RANDOM * (n+1)) >> 16; \
|
||||
SWAP(data[n], data[j], tmp); \
|
||||
--n; \
|
||||
}
|
||||
} \
|
||||
LOOP_RANDOM_END
|
||||
|
||||
void Shuffle8(void *data_, size_t n)
|
||||
{
|
||||
|
@ -66,15 +170,19 @@ void Shuffle32(void *data_, size_t n)
|
|||
void ShuffleN(void *data, size_t n, size_t size)
|
||||
{
|
||||
void *tmp = alloca(size);
|
||||
LOOP_RANDOM_START;
|
||||
--n;
|
||||
|
||||
while (n > 1)
|
||||
{
|
||||
int j = (Random() * (n+1)) >> 16;
|
||||
int j = (LOOP_RANDOM * (n+1)) >> 16;
|
||||
memcpy(tmp, (u8 *)data + n*size, size); // tmp = data[n];
|
||||
memcpy((u8 *)data + n*size, (u8 *)data + j*size, size); // data[n] = data[j];
|
||||
memcpy((u8 *)data + j*size, tmp, size); // data[j] = tmp;
|
||||
--n;
|
||||
}
|
||||
|
||||
LOOP_RANDOM_END;
|
||||
}
|
||||
|
||||
__attribute__((weak, alias("RandomUniformDefault")))
|
||||
|
@ -96,12 +204,14 @@ u32 RandomUniformDefault(enum RandomTag tag, u32 lo, u32 hi)
|
|||
|
||||
u32 RandomUniformExceptDefault(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32))
|
||||
{
|
||||
LOOP_RANDOM_START;
|
||||
while (TRUE)
|
||||
{
|
||||
u32 n = RandomUniformDefault(tag, lo, hi);
|
||||
u32 n = lo + (((hi - lo + 1) * LOOP_RANDOM) >> 16);
|
||||
if (!reject(n))
|
||||
return n;
|
||||
}
|
||||
LOOP_RANDOM_END;
|
||||
}
|
||||
|
||||
u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
|
||||
|
|
|
@ -33,8 +33,8 @@ struct PlayerInfo
|
|||
// Save data using TryWriteSpecialSaveSector is allowed to exceed SECTOR_DATA_SIZE (up to the counter field)
|
||||
STATIC_ASSERT(sizeof(struct RecordedBattleSave) <= SECTOR_COUNTER_OFFSET, RecordedBattleSaveFreeSpace);
|
||||
|
||||
EWRAM_DATA u32 gRecordedBattleRngSeed = 0;
|
||||
EWRAM_DATA u32 gBattlePalaceMoveSelectionRngValue = 0;
|
||||
EWRAM_DATA rng_value_t gRecordedBattleRngSeed = RNG_VALUE_EMPTY;
|
||||
EWRAM_DATA rng_value_t gBattlePalaceMoveSelectionRngValue = RNG_VALUE_EMPTY;
|
||||
EWRAM_DATA static u8 sBattleRecords[MAX_BATTLERS_COUNT][BATTLER_RECORD_SIZE] = {0};
|
||||
EWRAM_DATA static u16 sBattlerRecordSizes[MAX_BATTLERS_COUNT] = {0};
|
||||
EWRAM_DATA static u16 sBattlerPrevRecordSizes[MAX_BATTLERS_COUNT] = {0};
|
||||
|
|
|
@ -25,7 +25,7 @@ static void SetMirageRnd(u32 rnd)
|
|||
// unused
|
||||
void InitMirageRnd(void)
|
||||
{
|
||||
SetMirageRnd((Random() << 16) | Random());
|
||||
SetMirageRnd(Random32());
|
||||
}
|
||||
|
||||
void UpdateMirageRnd(u16 days)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
error = 0; \
|
||||
for (i = 0; i < ARRAY_COUNT(indexSum); i++) \
|
||||
error += abs(3584 - indexSum[i]); \
|
||||
EXPECT_LT(error, (int)(28672 * 0.025));
|
||||
EXPECT_LT(error, (int)(28672 * 0.03));
|
||||
|
||||
TEST("Shuffle randomizes the array [Shuffle8]")
|
||||
{
|
||||
|
@ -196,6 +196,13 @@ TEST("RandomElement generates a uniform distribution")
|
|||
|
||||
TEST("RandomUniform mul-based faster than mod-based (compile-time)")
|
||||
{
|
||||
#if HQ_RANDOM == TRUE
|
||||
const u32 expectedMulSum = 6;
|
||||
const u32 expectedModSum = 4;
|
||||
#else
|
||||
const u32 expectedMulSum = 3;
|
||||
const u32 expectedModSum = 4;
|
||||
#endif
|
||||
struct Benchmark mulBenchmark, modBenchmark;
|
||||
u32 mulSum = 0, modSum = 0;
|
||||
|
||||
|
@ -221,12 +228,19 @@ TEST("RandomUniform mul-based faster than mod-based (compile-time)")
|
|||
// These numbers are different because multiplication and modulus
|
||||
// have subtly different biases (so subtle that it's irrelevant for
|
||||
// our purposes).
|
||||
EXPECT_EQ(mulSum, 3);
|
||||
EXPECT_EQ(modSum, 4);
|
||||
EXPECT_EQ(mulSum, expectedMulSum);
|
||||
EXPECT_EQ(modSum, expectedModSum);
|
||||
}
|
||||
|
||||
TEST("RandomUniform mul-based faster than mod-based (run-time)")
|
||||
{
|
||||
#if HQ_RANDOM == TRUE
|
||||
const u32 expectedMulSum = 289;
|
||||
const u32 expectedModSum = 205;
|
||||
#else
|
||||
const u32 expectedMulSum = 232;
|
||||
const u32 expectedModSum = 249;
|
||||
#endif
|
||||
u32 i;
|
||||
struct Benchmark mulBenchmark, modBenchmark;
|
||||
u32 mulSum = 0, modSum = 0;
|
||||
|
@ -246,6 +260,30 @@ TEST("RandomUniform mul-based faster than mod-based (run-time)")
|
|||
EXPECT_FASTER(mulBenchmark, modBenchmark);
|
||||
|
||||
// Reference mulSum/modSum to prevent optimization.
|
||||
EXPECT_EQ(mulSum, 232);
|
||||
EXPECT_EQ(modSum, 249);
|
||||
EXPECT_EQ(mulSum, expectedMulSum);
|
||||
EXPECT_EQ(modSum, expectedModSum);
|
||||
}
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
TEST("Thumb and C SFC32 implementations produce the same results")
|
||||
{
|
||||
u32 thumbSum;
|
||||
u32 cSum;
|
||||
int i;
|
||||
rng_value_t localState;
|
||||
|
||||
thumbSum = 0;
|
||||
cSum = 0;
|
||||
|
||||
SeedRng(0);
|
||||
localState = gRngValue;
|
||||
|
||||
for(i = 0; i < 32; i++)
|
||||
{
|
||||
thumbSum += Random32();
|
||||
cSum += _SFC32_Next(&localState);
|
||||
}
|
||||
|
||||
EXPECT_EQ(thumbSum, cSum);
|
||||
}
|
||||
#endif
|
|
@ -33,8 +33,20 @@
|
|||
#define STATE gBattleTestRunnerState
|
||||
#define DATA gBattleTestRunnerState->data
|
||||
|
||||
#define RNG_SEED_DEFAULT 0x00000000
|
||||
#if HQ_RANDOM == TRUE
|
||||
#define RNG_SEED_DEFAULT {0, 0, 0, 0}
|
||||
static inline bool32 RngSeedNotDefault(const rng_value_t *seed)
|
||||
{
|
||||
return (seed->a | seed->b | seed->c | seed->ctr) != 0;
|
||||
|
||||
}
|
||||
#else
|
||||
#define RNG_SEED_DEFAULT 0x00000000
|
||||
static inline bool32 RngSeedNotDefault(const rng_value_t *seed)
|
||||
{
|
||||
return *seed != RNG_SEED_DEFAULT;
|
||||
}
|
||||
#endif
|
||||
#undef Q_4_12
|
||||
#define Q_4_12(n) (s32)((n) * 4096)
|
||||
|
||||
|
@ -257,11 +269,12 @@ static void BattleTest_Run(void *data)
|
|||
s32 i;
|
||||
u32 requiredPlayerPartySize;
|
||||
u32 requiredOpponentPartySize;
|
||||
const rng_value_t defaultSeed = RNG_SEED_DEFAULT;
|
||||
const struct BattleTest *test = data;
|
||||
|
||||
memset(&DATA, 0, sizeof(DATA));
|
||||
|
||||
DATA.recordedBattle.rngSeed = RNG_SEED_DEFAULT;
|
||||
DATA.recordedBattle.rngSeed = defaultSeed;
|
||||
DATA.recordedBattle.textSpeed = OPTIONS_TEXT_SPEED_FAST;
|
||||
// Set battle flags and opponent ids.
|
||||
switch (test->type)
|
||||
|
@ -1349,6 +1362,20 @@ static void CB2_BattleTest_NextParameter(void)
|
|||
}
|
||||
}
|
||||
|
||||
static inline rng_value_t MakeRngValue(const u16 seed)
|
||||
{
|
||||
#if HQ_RANDOM == TRUE
|
||||
int i;
|
||||
rng_value_t result = {0, 0, seed, 1};
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
_SFC32_Next(&result);
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
return ISO_RANDOMIZE1(seed);
|
||||
#endif
|
||||
}
|
||||
static void CB2_BattleTest_NextTrial(void)
|
||||
{
|
||||
ClearFlagAfterTest();
|
||||
|
@ -1373,7 +1400,7 @@ static void CB2_BattleTest_NextTrial(void)
|
|||
{
|
||||
PrintTestName();
|
||||
gTestRunnerState.result = TEST_RESULT_PASS;
|
||||
DATA.recordedBattle.rngSeed = ISO_RANDOMIZE1(STATE->runTrial);
|
||||
DATA.recordedBattle.rngSeed = MakeRngValue(STATE->runTrial);
|
||||
DATA.queuedEvent = 0;
|
||||
DATA.lastActionTurn = 0;
|
||||
SetVariablesForRecordedBattle(&DATA.recordedBattle);
|
||||
|
@ -1446,16 +1473,17 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx
|
|||
}
|
||||
else
|
||||
{
|
||||
INVALID_IF(DATA.recordedBattle.rngSeed != RNG_SEED_DEFAULT, "RNG seed already set");
|
||||
const rng_value_t defaultSeed = RNG_SEED_DEFAULT;
|
||||
INVALID_IF(RngSeedNotDefault(&DATA.recordedBattle.rngSeed), "RNG seed already set");
|
||||
STATE->trials = 50;
|
||||
STATE->trialRatio = Q_4_12(1) / STATE->trials;
|
||||
DATA.recordedBattle.rngSeed = 0;
|
||||
DATA.recordedBattle.rngSeed = defaultSeed;
|
||||
}
|
||||
}
|
||||
|
||||
void RNGSeed_(u32 sourceLine, u32 seed)
|
||||
void RNGSeed_(u32 sourceLine, rng_value_t seed)
|
||||
{
|
||||
INVALID_IF(DATA.recordedBattle.rngSeed != RNG_SEED_DEFAULT, "RNG seed already set");
|
||||
INVALID_IF(RngSeedNotDefault(&DATA.recordedBattle.rngSeed), "RNG seed already set");
|
||||
DATA.recordedBattle.rngSeed = seed;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue