Skip to content

Instantly share code, notes, and snippets.

@robin92
Last active December 9, 2021 22:06
Show Gist options
  • Save robin92/6ba58899c73a1d421247a63b682b43dc to your computer and use it in GitHub Desktop.
Save robin92/6ba58899c73a1d421247a63b682b43dc to your computer and use it in GitHub Desktop.
Pokemon breeding probability calculator

Pokemon breeding probability calculator

What is this?

This is a simple command line program written in Python for calculating probability of hatching an egg with fixed number of perfect IVs.

The mechanism of IV inheritance in Pokemon (up to 7th gen) is quite simple. Parnets can pass 3 or 5 (if one holds destiny knot) of their IVs to the offspring. IVs to be passed are selected randomly, with equal probability, and do not overlap (eg. passing Attack from both parents is impossible). Passed IVs get value from one of a parents with an equal probability (for each passed attribute, not as a whole). The last remaining IV is randomized from {0, 1, ..., 31}, each value has the same probability.

The program assumes that destiny knot is held by a parent. It uses brute force technique to iterate over all possible offspring IVs. Based on gathered data, some statistics are printed.

How to use this?

Install latest python3 and run

$ python breeding.py 101111 111110
Odds of getting fixed number of perfect IVs
6   1.0417%
5  33.8542%
4  48.9583%
3  16.1458%
Expected number of perfect IVs: 4.1979

IV Spread distribution
(1, 1, 1, 1, 1, 1)   1.0417%
(1, 1, 1, 1, 1, 0)   8.8542%
(1, 1, 1, 1, 0, 1)   4.0365%
(1, 1, 1, 1, 0, 0)   4.0365%
(1, 1, 1, 0, 1, 1)   4.0365%
(1, 1, 1, 0, 1, 0)   4.0365%
(1, 1, 0, 1, 1, 1)   4.0365%
(1, 1, 0, 1, 1, 0)   4.0365%
(1, 0, 1, 1, 1, 1)   8.8542%
(1, 0, 1, 1, 1, 0)  16.6667%
(1, 0, 1, 1, 0, 1)   4.0365%
(1, 0, 1, 1, 0, 0)   4.0365%
(1, 0, 1, 0, 1, 1)   4.0365%
(1, 0, 1, 0, 1, 0)   4.0365%
(1, 0, 0, 1, 1, 1)   4.0365%
(1, 0, 0, 1, 1, 0)   4.0365%
(0, 1, 1, 1, 1, 1)   4.0365%
(0, 1, 1, 1, 1, 0)   4.0365%
(0, 0, 1, 1, 1, 1)   4.0365%
(0, 0, 1, 1, 1, 0)   4.0365%

Application can also calculate expected number of eggs that will need to be hatched for getting the desired offspring

$ python breeding.py 101111 111110 111111
Odds of getting fixed number of perfect IVs
6   1.0417%
5  33.8542%
4  48.9583%
3  16.1458%
Expected number of perfect IVs: 4.1979

IV Spread distribution
(1, 1, 1, 1, 1, 1)   1.0417%
(1, 1, 1, 1, 1, 0)   8.8542%
(1, 1, 1, 1, 0, 1)   4.0365%
(1, 1, 1, 1, 0, 0)   4.0365%
(1, 1, 1, 0, 1, 1)   4.0365%
(1, 1, 1, 0, 1, 0)   4.0365%
(1, 1, 0, 1, 1, 1)   4.0365%
(1, 1, 0, 1, 1, 0)   4.0365%
(1, 0, 1, 1, 1, 1)   8.8542%
(1, 0, 1, 1, 1, 0)  16.6667%
(1, 0, 1, 1, 0, 1)   4.0365%
(1, 0, 1, 1, 0, 0)   4.0365%
(1, 0, 1, 0, 1, 1)   4.0365%
(1, 0, 1, 0, 1, 0)   4.0365%
(1, 0, 0, 1, 1, 1)   4.0365%
(1, 0, 0, 1, 1, 0)   4.0365%
(0, 1, 1, 1, 1, 1)   4.0365%
(0, 1, 1, 1, 1, 0)   4.0365%
(0, 0, 1, 1, 1, 1)   4.0365%
(0, 0, 1, 1, 1, 0)   4.0365%

Wanted offspring
(1, 1, 1, 1, 1, 1)   1.0417%
Expected number of hatched eggs: 96.0000
import sys
from collections import defaultdict
from copy import copy
from itertools import combinations
PERCENT = '__format_%'
_format = {
PERCENT: '{:>8.4f}%',
}
_inf = float('inf')
class BreedingError(RuntimeError):
def __init__(self, msg):
super().__init__(msg)
class Error(RuntimeError):
def __init__(self, msg):
super().__init__(msg)
def main():
try:
run()
except (Error, BreedingError) as err:
print(err)
return -1
def run():
desired_offspring = None
def _cast(str):
def _cast_one(c):
if c == '0': return 0
elif c == '1': return 1
raise Error('Unsupported input vectors! Vectors must contain only 1 or 0.')
return tuple(_cast_one(x) for x in str)
if len(sys.argv) < 3:
raise Error('Unsupported input vectors! Two parents needed.')
elif len(sys.argv) > 3:
desired_offspring = _cast(sys.argv[3])
father = _cast(sys.argv[1])
mother = _cast(sys.argv[2])
histogram_spread = breed(father, mother)
histogram_iv = defaultdict(lambda: 0)
for spread, count in histogram_spread.items():
histogram_iv[sum(spread)] += count
def _print():
print('Odds of getting fixed number of perfect IVs')
total = sum(histogram_iv.values())
for perfect_iv, count in reversed(sorted(histogram_iv.items(), key=lambda p: p[0])):
print(perfect_iv, _format[PERCENT].format(count / total * 100))
expected_perfect_iv = sum(iv * count for iv, count in histogram_iv.items()) / total
print('Expected number of perfect IVs: {:5.4f}'.format(expected_perfect_iv))
print()
print('IV Spread distribution')
for spread, count in reversed(sorted(histogram_spread.items())):
print(spread, _format[PERCENT].format(count / total * 100))
if desired_offspring is not None:
# looking for offspring with 1 for each 1 in desired_offspring IV and 1 or 0 for each
# 0 in desired_offspring
acc = 0
for spread, count in histogram_spread.items():
for lhs, rhs in zip(desired_offspring, spread):
if not ((lhs == 1 and rhs == 1) or lhs == 0):
break
else:
acc += count
probability = acc / total
expected_eggs = (1 / probability) if probability != 0 else _inf
print()
print('Wanted offspring')
print(desired_offspring, _format[PERCENT].format(probability * 100))
print('Expected number of hatched eggs: {:5.4f}'.format(expected_eggs))
_print()
def breed(father, mother):
'''
Calculates IV spread histogram by brute-forcing all possible breeding outcomes.
'''
length = 6
histogram_spread = defaultdict(lambda: 0)
if len(father) != len(mother) != length:
raise BreedingError('Unsupported input vectors! Exactly {} IVs needed.'.format(length))
def run(fixed):
offspring = length * [None]
def _report():
if all(x is not None for x in offspring):
histogram_spread[tuple(offspring)] += 1
def _run(idx):
if not(0 <= idx < len(offspring)):
_report()
return
if idx == fixed:
_run(idx + 1)
return
offspring[idx] = father[idx]
_run(idx + 1)
offspring[idx] = mother[idx]
_run(idx + 1)
# IVs use discrete uniform distribution on [0, 31], perfect = 31
for v in 31 * [0] + [1]:
offspring[fixed] = v
_run(0)
# no need to iterate over combinations as we can reverse selecting 5 from 6
# to selecting 1 from 6 and "randomize" this value
for fixed in range(len(father)):
run(fixed)
return histogram_spread
if __name__ == '__main__':
exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment