mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Fixed the hell out of the capture rate formula. #150
- Wobbles are based on WHICH number is greater than some pivot, not how many. This was making everything totally wrong, especially 0 wobbles. - HG/SS balls all modify capture rate, rather than ball bonus. - Everything really is integer math; even the sqrts. Bonuses are relative to 10, not 1. HP is now treated as integer math, too. - Implemented a minor game bug with very hard to catch Pokémon.
This commit is contained in:
parent
6da2b325fa
commit
78bff787f6
1 changed files with 48 additions and 38 deletions
|
@ -45,43 +45,54 @@ def earned_exp(base_exp, level):
|
||||||
return base_exp * level // 7
|
return base_exp * level // 7
|
||||||
|
|
||||||
def capture_chance(percent_hp, capture_rate,
|
def capture_chance(percent_hp, capture_rate,
|
||||||
ball_bonus=1, status_bonus=1, heavy_modifier=0):
|
ball_bonus=10, status_bonus=1,
|
||||||
|
capture_bonus=10, capture_modifier=0):
|
||||||
"""Calculates the chance that a Pokémon will be caught, given its capture
|
"""Calculates the chance that a Pokémon will be caught, given its capture
|
||||||
rate and the percentage of HP it has remaining.
|
rate and the percentage of HP it has remaining.
|
||||||
|
|
||||||
|
Bonuses are such that 10 means "unchanged".
|
||||||
|
|
||||||
Returns five values: the chance of a capture, then the chance of the ball
|
Returns five values: the chance of a capture, then the chance of the ball
|
||||||
shaking three, two, one, or zero times. Each of these is a float such that
|
shaking three, two, one, or zero times. Each of these is a float such that
|
||||||
0.0 <= n <= 1.0. Feel free to ignore all but the first.
|
0.0 <= n <= 1.0. Feel free to ignore all but the first.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if heavy_modifier:
|
# HG/SS Pokéballs modify capture rate rather than the ball bonus
|
||||||
# Only used by Heavy Ball. Changes the target's capture rate outright
|
capture_rate = capture_rate * capture_bonus // 10 + capture_modifier
|
||||||
capture_rate += heavy_modifier
|
if capture_rate < 1:
|
||||||
if capture_rate <= 1:
|
|
||||||
capture_rate = 1
|
capture_rate = 1
|
||||||
|
elif capture_rate > 255:
|
||||||
# This should really be integer math, right? But the formula uses FOURTH
|
capture_rate = 255
|
||||||
# ROOTS in a moment, so it can't possibly be. It probably doesn't matter
|
|
||||||
# either way, so whatever; use regular ol' division. ball_bonus and
|
|
||||||
# status_bonus can be 1.5, anyway.
|
|
||||||
|
|
||||||
# A slight math note:
|
# A slight math note:
|
||||||
# The formula is originally: (3 max - 2 curr) rate bonus / (3 max)
|
# The actual formula uses (3 * max_hp - 2 * curr_hp) / (3 * max_hp)
|
||||||
# I have reduced this to: (1 - 2/3 * pct) rate bonus
|
# This uses (1 - 2/3 * curr_hp/max_hp)
|
||||||
# My rationale is that this cannot possibly be integer math, so rounding is
|
# Integer division is taken into account by flooring immediately
|
||||||
# not a problem and commutation won't make a difference. It also
|
# afterwards, so there should be no appreciable rounding error.
|
||||||
# simplifies the input considerably.
|
base_chance = int(
|
||||||
base_chance = (1 - 2/3 * percent_hp) * capture_rate \
|
capture_rate * ball_bonus // 10 * (1 - 2/3 * percent_hp)
|
||||||
* ball_bonus * status_bonus
|
)
|
||||||
|
base_chance = base_chance * status_bonus // 10
|
||||||
|
|
||||||
shake_index = (base_chance / 255) ** 0.25 * (2**16 - 1)
|
# Shake index involves integer sqrt. Lovely.
|
||||||
|
isqrt = lambda x: int(x ** 0.5)
|
||||||
|
if not base_chance:
|
||||||
|
# This is very silly. Due to what must be an oversight, it's possible
|
||||||
|
# for the above formula to end with a zero chance to catch, which is
|
||||||
|
# then thrown blindly into the below denominator. Luckily, the games'
|
||||||
|
# division function is a no-op with a denominator of zero.. which
|
||||||
|
# means a base_chance of 0 is effectively a base chance of 1.
|
||||||
|
base_chance = 1
|
||||||
|
shake_index = 1048560 // isqrt(isqrt(16711680 // base_chance))
|
||||||
|
|
||||||
# Iff base_chance < 255, then shake_index < 65535.
|
# Iff base_chance < 255, then shake_index < 65535.
|
||||||
# The game now picks four random uwords. However many of them are <=
|
# The Pokémon now has four chances to escape. The game starts picking
|
||||||
# shake_index is the number of times the ball will shake. If all four are
|
# random uint16s. If such a random number is < shake_index, the Pokémon
|
||||||
# <= shake_index, the Pokémon is caught.
|
# stays in the ball, and it wobbles. If the number is >= shake_index, the
|
||||||
|
# ball breaks open then and there, and the capture fails.
|
||||||
|
# If all four are < shake_index, the Pokémon is caught.
|
||||||
|
|
||||||
# If shake_index >= 65535, all four randoms must be <= it, and the Pokémon
|
# If shake_index >= 65535, all four randoms must be < it, and the Pokémon
|
||||||
# will be caught. Skip hard math
|
# will be caught. Skip hard math
|
||||||
if shake_index >= 65535:
|
if shake_index >= 65535:
|
||||||
return (1.0, 0.0, 0.0, 0.0, 0.0)
|
return (1.0, 0.0, 0.0, 0.0, 0.0)
|
||||||
|
@ -90,21 +101,20 @@ def capture_chance(percent_hp, capture_rate,
|
||||||
# Something is guaranteed to happen.
|
# Something is guaranteed to happen.
|
||||||
|
|
||||||
# Alrighty. Here's some probability.
|
# Alrighty. Here's some probability.
|
||||||
# The chance that a single random number will be <= shake_index is:
|
# The chance that a single random uint16 will be < shake_index, thus
|
||||||
p = (shake_index + 1) / 65536
|
# keeping the Pokémon in the ball, is:
|
||||||
# Now, the chance that two random numbers will be <= shake_index is p**2.
|
p = shake_index / 65536
|
||||||
# And the chance that neither will be is (1 - p)**2.
|
|
||||||
# With me so far?
|
|
||||||
# The chance that one will be and one will NOT be is p * (1 - p) * 2.
|
|
||||||
# The 2 is because they can go in any order: the first could be less, or
|
|
||||||
# the second could be less. That 2 is actually nCr(2, 1); the number of
|
|
||||||
# ways of picking one item in any order from a group of two.
|
|
||||||
# Try it yourself add up those three values and you'll get 1.
|
|
||||||
|
|
||||||
# Right. Hopefully, the following now makes sense.
|
# Now, the chance for n wobbles is the chance that the Pokémon will stay in
|
||||||
# There are five cases: four randoms are <= shake_index (which means
|
# the ball for (n-1) attempts, then break out on the nth.
|
||||||
# capture), or three are, etc.
|
# The chance of capture is just the chance that the Pokémon stays in the
|
||||||
|
# ball for all four tries.
|
||||||
|
|
||||||
|
# There are five cases: captured, wobbled three times, etc.
|
||||||
return [
|
return [
|
||||||
p**i * (1 - p)**(4 - i) * nCr(4, i)
|
p**4, # capture
|
||||||
for i in reversed(range(5))
|
p**3 * (1 - p),
|
||||||
|
p**2 * (1 - p),
|
||||||
|
p**1 * (1 - p),
|
||||||
|
(1 - p),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue