mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2025-01-14 07:33:44 +01:00
RandomUniformExcept and RNG_METRONOME
This commit is contained in:
parent
b08c8f85fc
commit
b406a9c1bd
@ -32,6 +32,10 @@ void SeedRng2(u16 seed);
|
|||||||
* RandomUniform(tag, lo, hi) returns a number from lo to hi inclusive
|
* RandomUniform(tag, lo, hi) returns a number from lo to hi inclusive
|
||||||
* with uniform probability.
|
* 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
|
* RandomElement(tag, array) returns an element in array with uniform
|
||||||
* probability. The array must be known at compile-time (e.g. a global
|
* probability. The array must be known at compile-time (e.g. a global
|
||||||
* const array).
|
* const array).
|
||||||
@ -58,6 +62,7 @@ enum RandomTag
|
|||||||
RNG_FROZEN,
|
RNG_FROZEN,
|
||||||
RNG_HOLD_EFFECT_FLINCH,
|
RNG_HOLD_EFFECT_FLINCH,
|
||||||
RNG_INFATUATION,
|
RNG_INFATUATION,
|
||||||
|
RNG_METRONOME,
|
||||||
RNG_PARALYSIS,
|
RNG_PARALYSIS,
|
||||||
RNG_POISON_POINT,
|
RNG_POISON_POINT,
|
||||||
RNG_RAMPAGE_TURNS,
|
RNG_RAMPAGE_TURNS,
|
||||||
@ -103,10 +108,12 @@ enum RandomTag
|
|||||||
})
|
})
|
||||||
|
|
||||||
u32 RandomUniform(enum RandomTag, u32 lo, u32 hi);
|
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);
|
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);
|
const void *RandomElementArray(enum RandomTag, const void *array, size_t size, size_t count);
|
||||||
|
|
||||||
u32 RandomUniformDefault(enum RandomTag, u32 lo, u32 hi);
|
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);
|
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);
|
const void *RandomElementArrayDefault(enum RandomTag, const void *array, size_t size, size_t count);
|
||||||
|
|
||||||
|
@ -12977,41 +12977,37 @@ static void Cmd_mimicattackcopy(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool32 InvalidMetronomeMove(u32 move)
|
||||||
|
{
|
||||||
|
return gBattleMoves[move].effect == EFFECT_PLACEHOLDER
|
||||||
|
|| sForbiddenMoves[move] & FORBIDDEN_METRONOME;
|
||||||
|
}
|
||||||
|
|
||||||
static void Cmd_metronome(void)
|
static void Cmd_metronome(void)
|
||||||
{
|
{
|
||||||
CMD_ARGS();
|
CMD_ARGS();
|
||||||
|
|
||||||
#if B_METRONOME_MOVES >= GEN_9
|
#if B_METRONOME_MOVES >= GEN_9
|
||||||
u16 moveCount = MOVES_COUNT_GEN9;
|
u32 moveCount = MOVES_COUNT_GEN9;
|
||||||
#elif B_METRONOME_MOVES >= GEN_8
|
#elif B_METRONOME_MOVES >= GEN_8
|
||||||
u16 moveCount = MOVES_COUNT_GEN8;
|
u32 moveCount = MOVES_COUNT_GEN8;
|
||||||
#elif B_METRONOME_MOVES >= GEN_7
|
#elif B_METRONOME_MOVES >= GEN_7
|
||||||
u16 moveCount = MOVES_COUNT_GEN7;
|
u32 moveCount = MOVES_COUNT_GEN7;
|
||||||
#elif B_METRONOME_MOVES >= GEN_6
|
#elif B_METRONOME_MOVES >= GEN_6
|
||||||
u16 moveCount = MOVES_COUNT_GEN6;
|
u32 moveCount = MOVES_COUNT_GEN6;
|
||||||
#elif B_METRONOME_MOVES >= GEN_5
|
#elif B_METRONOME_MOVES >= GEN_5
|
||||||
u16 moveCount = MOVES_COUNT_GEN5;
|
u32 moveCount = MOVES_COUNT_GEN5;
|
||||||
#elif B_METRONOME_MOVES >= GEN_4
|
#elif B_METRONOME_MOVES >= GEN_4
|
||||||
u16 moveCount = MOVES_COUNT_GEN4;
|
u32 moveCount = MOVES_COUNT_GEN4;
|
||||||
#elif B_METRONOME_MOVES >= GEN_3
|
#elif B_METRONOME_MOVES >= GEN_3
|
||||||
u16 moveCount = MOVES_COUNT_GEN3;
|
u32 moveCount = MOVES_COUNT_GEN3;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (TRUE)
|
gCurrentMove = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove);
|
||||||
{
|
|
||||||
gCurrentMove = (Random() % (moveCount - 1)) + 1;
|
|
||||||
if (gBattleMoves[gCurrentMove].effect == EFFECT_PLACEHOLDER)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!(sForbiddenMoves[gCurrentMove] & FORBIDDEN_METRONOME))
|
|
||||||
{
|
|
||||||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||||||
SetAtkCancellerForCalledMove();
|
SetAtkCancellerForCalledMove();
|
||||||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
||||||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Cmd_dmgtolevel(void)
|
static void Cmd_dmgtolevel(void)
|
||||||
|
13
src/random.c
13
src/random.c
@ -35,6 +35,9 @@ u16 Random2(void)
|
|||||||
__attribute__((weak, alias("RandomUniformDefault")))
|
__attribute__((weak, alias("RandomUniformDefault")))
|
||||||
u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi);
|
u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi);
|
||||||
|
|
||||||
|
__attribute__((weak, alias("RandomUniformExceptDefault")))
|
||||||
|
u32 RandomUniformExcept(enum RandomTag, u32 lo, u32 hi, bool32 (*reject)(u32));
|
||||||
|
|
||||||
__attribute__((weak, alias("RandomWeightedArrayDefault")))
|
__attribute__((weak, alias("RandomWeightedArrayDefault")))
|
||||||
u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights);
|
u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights);
|
||||||
|
|
||||||
@ -46,6 +49,16 @@ u32 RandomUniformDefault(enum RandomTag tag, u32 lo, u32 hi)
|
|||||||
return lo + (((hi - lo + 1) * Random()) >> 16);
|
return lo + (((hi - lo + 1) * Random()) >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 RandomUniformExceptDefault(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32))
|
||||||
|
{
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
u32 n = RandomUniformDefault(tag, lo, hi);
|
||||||
|
if (!reject(n))
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
|
u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
|
||||||
{
|
{
|
||||||
s32 i, targetSum;
|
s32 i, targetSum;
|
||||||
|
@ -6,19 +6,13 @@ ASSUMPTIONS
|
|||||||
ASSUME(gBattleMoves[MOVE_METRONOME].effect == EFFECT_METRONOME);
|
ASSUME(gBattleMoves[MOVE_METRONOME].effect == EFFECT_METRONOME);
|
||||||
}
|
}
|
||||||
|
|
||||||
// To do: Turn the seeds to work with WITH_RNG for Metronome.
|
|
||||||
#define RNG_METRONOME_SCRATCH 0x118
|
|
||||||
#define RNG_METRONOME_PSN_POWDER 0x119
|
|
||||||
#define RNG_METRONOME_ROCK_BLAST 0x1F5
|
|
||||||
|
|
||||||
SINGLE_BATTLE_TEST("Metronome picks a random move")
|
SINGLE_BATTLE_TEST("Metronome picks a random move")
|
||||||
{
|
{
|
||||||
GIVEN {
|
GIVEN {
|
||||||
RNGSeed(RNG_METRONOME_SCRATCH);
|
|
||||||
PLAYER(SPECIES_WOBBUFFET);
|
PLAYER(SPECIES_WOBBUFFET);
|
||||||
OPPONENT(SPECIES_WOBBUFFET);
|
OPPONENT(SPECIES_WOBBUFFET);
|
||||||
} WHEN {
|
} WHEN {
|
||||||
TURN { MOVE(player, MOVE_METRONOME); }
|
TURN { MOVE(player, MOVE_METRONOME, WITH_RNG(RNG_METRONOME, MOVE_SCRATCH)); }
|
||||||
} SCENE {
|
} SCENE {
|
||||||
MESSAGE("Wobbuffet used Metronome!");
|
MESSAGE("Wobbuffet used Metronome!");
|
||||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||||
@ -34,11 +28,10 @@ SINGLE_BATTLE_TEST("Metronome's called powder move fails against Grass Types")
|
|||||||
ASSUME(gBattleMoves[MOVE_POISON_POWDER].flags & FLAG_POWDER);
|
ASSUME(gBattleMoves[MOVE_POISON_POWDER].flags & FLAG_POWDER);
|
||||||
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
|
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
|
||||||
ASSUME(gBattleMoves[MOVE_POISON_POWDER].effect == EFFECT_POISON);
|
ASSUME(gBattleMoves[MOVE_POISON_POWDER].effect == EFFECT_POISON);
|
||||||
RNGSeed(RNG_METRONOME_PSN_POWDER);
|
PLAYER(SPECIES_WOBBUFFET);
|
||||||
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
OPPONENT(SPECIES_TANGELA);
|
||||||
OPPONENT(SPECIES_TANGELA) {Speed(2);}
|
|
||||||
} WHEN {
|
} WHEN {
|
||||||
TURN { MOVE(player, MOVE_METRONOME); }
|
TURN { MOVE(player, MOVE_METRONOME, WITH_RNG(RNG_METRONOME, MOVE_POISON_POWDER)); }
|
||||||
} SCENE {
|
} SCENE {
|
||||||
MESSAGE("Wobbuffet used Metronome!");
|
MESSAGE("Wobbuffet used Metronome!");
|
||||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||||
@ -53,17 +46,16 @@ SINGLE_BATTLE_TEST("Metronome's called multi-hit move hits multiple times")
|
|||||||
{
|
{
|
||||||
GIVEN {
|
GIVEN {
|
||||||
ASSUME(gBattleMoves[MOVE_ROCK_BLAST].effect == EFFECT_MULTI_HIT);
|
ASSUME(gBattleMoves[MOVE_ROCK_BLAST].effect == EFFECT_MULTI_HIT);
|
||||||
RNGSeed(RNG_METRONOME_ROCK_BLAST);
|
|
||||||
PLAYER(SPECIES_WOBBUFFET);
|
PLAYER(SPECIES_WOBBUFFET);
|
||||||
OPPONENT(SPECIES_WOBBUFFET);
|
OPPONENT(SPECIES_WOBBUFFET);
|
||||||
} WHEN {
|
} WHEN {
|
||||||
TURN { MOVE(player, MOVE_METRONOME); }
|
TURN { MOVE(player, MOVE_METRONOME, WITH_RNG(RNG_METRONOME, MOVE_ROCK_BLAST)); }
|
||||||
} SCENE {
|
} SCENE {
|
||||||
MESSAGE("Wobbuffet used Metronome!");
|
MESSAGE("Wobbuffet used Metronome!");
|
||||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||||
MESSAGE("Wobbuffet used Rock Blast!");
|
MESSAGE("Wobbuffet used Rock Blast!");
|
||||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_BLAST, player);
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_BLAST, player);
|
||||||
HP_BAR(opponent);
|
HP_BAR(opponent);
|
||||||
MESSAGE("Hit 4 time(s)!");
|
MESSAGE("Hit 2 time(s)!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,25 @@ TEST("RandomUniform generates lo..hi")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool32 InvalidEven(u32 n)
|
||||||
|
{
|
||||||
|
return n % 2 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST("RandomUniformExcept generates lo..hi")
|
||||||
|
{
|
||||||
|
u32 lo, hi, i;
|
||||||
|
PARAMETRIZE { lo = 0; hi = 1; }
|
||||||
|
PARAMETRIZE { lo = 0; hi = 2; }
|
||||||
|
PARAMETRIZE { lo = 0; hi = 3; }
|
||||||
|
PARAMETRIZE { lo = 2; hi = 4; }
|
||||||
|
for (i = 0; i < 1024; i++)
|
||||||
|
{
|
||||||
|
u32 r = RandomUniformExceptDefault(RNG_NONE, lo, hi, InvalidEven);
|
||||||
|
EXPECT(lo <= r && r <= hi && r % 2 != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST("RandomWeighted generates 0..n-1")
|
TEST("RandomWeighted generates 0..n-1")
|
||||||
{
|
{
|
||||||
u32 n, sum, i;
|
u32 n, sum, i;
|
||||||
@ -65,6 +84,29 @@ TEST("RandomUniform generates uniform distribution")
|
|||||||
EXPECT_LT(error, UQ_4_12(0.025));
|
EXPECT_LT(error, UQ_4_12(0.025));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST("RandomUniformExcept generates uniform distribution")
|
||||||
|
{
|
||||||
|
u32 i, error;
|
||||||
|
u16 distribution[4];
|
||||||
|
|
||||||
|
memset(distribution, 0, sizeof(distribution));
|
||||||
|
for (i = 0; i < 4096; i++)
|
||||||
|
{
|
||||||
|
u32 r = RandomUniformExceptDefault(RNG_NONE, 0, ARRAY_COUNT(distribution) - 1, InvalidEven);
|
||||||
|
EXPECT(0 <= r && r < ARRAY_COUNT(distribution));
|
||||||
|
distribution[r]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
for (i = 0; i < ARRAY_COUNT(distribution); i++)
|
||||||
|
{
|
||||||
|
if (i % 2 != 0)
|
||||||
|
error += abs(UQ_4_12(0.5) - distribution[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_LT(error, UQ_4_12(0.05));
|
||||||
|
}
|
||||||
|
|
||||||
TEST("RandomWeighted generates distribution in proportion to the weights")
|
TEST("RandomWeighted generates distribution in proportion to the weights")
|
||||||
{
|
{
|
||||||
u32 i, sum, error;
|
u32 i, sum, error;
|
||||||
|
@ -603,8 +603,9 @@ struct BattleTestRunnerState
|
|||||||
u8 parameters;
|
u8 parameters;
|
||||||
u8 runParameter;
|
u8 runParameter;
|
||||||
u16 rngTag;
|
u16 rngTag;
|
||||||
u8 trials;
|
u16 rngTrialOffset;
|
||||||
u8 runTrial;
|
u16 trials;
|
||||||
|
u16 runTrial;
|
||||||
u16 expectedRatio;
|
u16 expectedRatio;
|
||||||
u16 observedRatio;
|
u16 observedRatio;
|
||||||
u16 trialRatio;
|
u16 trialRatio;
|
||||||
|
@ -332,7 +332,42 @@ u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi)
|
|||||||
{
|
{
|
||||||
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomUniform called with inconsistent trials %d and %d", STATE->trials, n);
|
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomUniform called with inconsistent trials %d and %d", STATE->trials, n);
|
||||||
}
|
}
|
||||||
STATE->trialRatio = Q_4_12(1) / n;
|
STATE->trialRatio = Q_4_12(1) / STATE->trials;
|
||||||
|
return STATE->runTrial + lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32))
|
||||||
|
{
|
||||||
|
const struct BattlerTurn *turn = NULL;
|
||||||
|
u32 default_;
|
||||||
|
|
||||||
|
if (gCurrentTurnActionNumber < gBattlersCount)
|
||||||
|
{
|
||||||
|
u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber];
|
||||||
|
turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId];
|
||||||
|
if (turn && turn->rng.tag == tag)
|
||||||
|
{
|
||||||
|
if (reject(turn->rng.value))
|
||||||
|
Test_ExitWithResult(TEST_RESULT_INVALID, "WITH_RNG specified a rejected value (%d)", turn->rng.value);
|
||||||
|
return turn->rng.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == STATE->rngTag)
|
||||||
|
{
|
||||||
|
if (STATE->trials == 1)
|
||||||
|
{
|
||||||
|
u32 n = 0, i;
|
||||||
|
for (i = lo; i < hi; i++)
|
||||||
|
if (!reject(i))
|
||||||
|
n++;
|
||||||
|
STATE->trials = n;
|
||||||
|
PrintTestName();
|
||||||
|
}
|
||||||
|
STATE->trialRatio = Q_4_12(1) / STATE->trials;
|
||||||
return STATE->runTrial + lo;
|
return STATE->runTrial + lo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -962,6 +997,7 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx
|
|||||||
INVALID_IF(test->resultsSize > 0, "PASSES_RANDOMLY is incompatible with results");
|
INVALID_IF(test->resultsSize > 0, "PASSES_RANDOMLY is incompatible with results");
|
||||||
INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials);
|
INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials);
|
||||||
STATE->rngTag = ctx.tag;
|
STATE->rngTag = ctx.tag;
|
||||||
|
STATE->rngTrialOffset = 0;
|
||||||
STATE->runTrial = 0;
|
STATE->runTrial = 0;
|
||||||
STATE->expectedRatio = Q_4_12(passes) / trials;
|
STATE->expectedRatio = Q_4_12(passes) / trials;
|
||||||
STATE->observedRatio = 0;
|
STATE->observedRatio = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user