Gotta Simulate ’Em All — Pokemon. Simulating Pokemon Battles From... | by Tyler Marrs | Towards Data Science

Many of us grew up playing Pokemon at various iterations of the series. When I was ten years old, the first generation (red and blue) of Pokemon came out in the United States. I spent countless hours playing that game with my friends. In 2018, I came across a PyData talk, “Vincent Warmerdam: Winning with Simple, even Linear, Models | PyData London 2018”, that mentioned how easy it is to obtain Pokemon data. The talk did not focus on Pokemon battle simulation, although it inspired me to create a Pokemon battle simulator and write this article.
In this article, I will discuss:
Limitations of this implementation
The data collection process
Algorithms used throughout the simulator
How I ranked Pokemon
NOTE: If you would like to review all of the source code, please find it at github.com/tylerwmarrs/pokemon-battle-simulation.
While I tried to capture most of the battle mechanics in the game, some mechanics were left out.
Ailments, such as paralysis, poison, and others, were left out to simplify a battle.
There is no way that a Pokemon may restore health.
Battles consist of damaging abilities that a Pokemon learns by leveling.
Attacking mechanics could be improved by ranking effectiveness instead of choosing at random.
These limitations may impact some Pokemon ranks. I highly encourage anyone interested to implement the missing details by contributing to the repository.
The data collection process was painless. A website provided me with the majority of the information I needed on the Pokemon themselves. The website pokeapi.co, provides an open API with results in the JSON format. The documentation of the website is quite thorough. Obtaining Pokemon attributes and moves involved:
Identifying Pokemon from the red/blue versionsFor each Pokemon, download the attributesIdentify moves from the red/blue versionsFor each move, download the attributes
Identifying Pokemon from the red/blue versions
For each Pokemon, download the attributes
Identify moves from the red/blue versions
For each move, download the attributes
Once all of the moves and Pokemon downloaded, I mapped the moves to each Pokemon using a pandas join.
I prefer to work with CSV files over JSON, so I spent some time flattening the structure of the attributes required for simulating battle. The JSON structure consisted of optional attributes, nested many layers deep. Flattening the attributes made it much easier to query.
Pokemon Attributes
name — The name of the Pokemon
hp — The health points
attack — General attack statistic
speed — Speed is a statistic used to compute damage
special attack — Special attack statistic
defense — Statistic to determine damage taken against regular attacks
special defense — Statistic to determine damage taken against special attacks
types — The type(s) of the Pokemon; Fire, Water, Rock, etc.
Move Attributes
name — The name of the move
pp — Number of times the move may be applied
type — The type of move; Fire, Normal, Water, etc.
crit_rate — A flag to adjust the critical chance of the move
power — Power is a statistic used to compute damage
min_hits — The minimum number of times the move may apply in a given turn.
max_hits — The maximum number of times the move may apply in a given turn.
In addition to the moves and Pokemon, I needed to obtain data to help quantify the damage. Pokemon is a turn-based game in which a player chooses an action to apply. This simulation is only concerned with attack moves to compute damage. Each Pokemon is a specific type, and so is each attack. The goal is to either defeat the opposing Pokemon or capture it. This simulation only captures victory and defeat. When an attack is applied, it may cause more damage given:
The attacking Pokemon type
The type of attack
The defending Pokemon type
The table shows how attack damage applies to a given Pokemon type. I retrieved it from this website using and cleaned it. The table captures exceptions to the default type modifier value of one.
The critical chance algorithm, based on a random number generator, depends on the attack. Some attacks are more likely to crit than others. I obtained the formula from this website and removed some flaws within the implementation of the game.
A couple of notable flaws include:
The original game formula intended for a 100% critical chance; however, the implementation resulted in a maximum of 99.6%. My implementation allows for a 100% critical chance.
Some boosting abilities decrease the critical chance when it should have increased it. However, boosting moves are entirely ignored in this simulation.
The algorithm takes the Pokemon’s base speed and the attacks critical chance rate to determine if a critical hit is occurring. The speed of the Pokemon is a big factor in deciding a critical chance.
The damage algorithm determines how much damage to apply given the Pokemon attributes and critical strike boolean from the critical hit algorithm. These algorithms are derived from the formulas on this website.
Randomly chooses a move with power points remaining based on the attacking Pokemon’s move list. When all moves are exhausted, null is returned. This is needed in the case that two Pokemon will tie; otherwise, an endless loop will occur in the apply_move algorithm.
This algorithm manages:
Critical chance randomization
Same type attack bonus (a fire Pokemon applying a fire move)
Attacking Pokemon’s power points (PP)
Defending Pokemon’s health points (HP)
Number of times the move should be applied
Note that this function has a hardcoded level value of 10, which may impact the damage calculation.
The battle algorithm keeps track of the simulation state:
Randomly chooses the Pokemon that may attack first
Which Pokemon’s turn it is
Checking for a winner
Checking for a tie
Here is an example of a battle that illustrates Pikachu against Gastly.
pikachu = Pokemon('pikachu')gastly = Pokemon('gastly')battle(pikachu, gastly)Output------gastly damaged pikachu with night-shade for 2.0 hpgastly pp for night-shade is 14/15pikachu hp is 33.0/35pikachu damaged gastly with thunder-shock for 10.0 hppikachu pp for thunder-shock is 29/30gastly hp is 20.0/30gastly damaged pikachu with dream-eater for 22.0 hpgastly pp for dream-eater is 14/15pikachu hp is 11.0/35pikachu damaged gastly with thunder-shock for 11.0 hppikachu pp for thunder-shock is 28/30gastly hp is 9.0/30gastly damaged pikachu with lick for 6.0 hpgastly pp for lick is 29/30pikachu hp is 5.0/35pikachu damaged gastly with thunder-shock for 10.0 hppikachu pp for thunder-shock is 27/30gastly hp is -1.0/30{'pokemon': 'pikachu', 'pokemonb': 'gastly', 'moves': 6, 'winner': 'pikachu', 'first_attack': 'gastly'}
With the battle algorithm complete, I created some logic to simulate battles 1,000 times between all Pokemon that contain at least one damaging move. Pokemon of the same species are excluded from battling. For example, Pikachu vs. Pikachu is not simulated. At the beginning of every battle, each Pokemon is randomly assigned up to four of their damaging abilities. The results are tallied between each pair of Pokemon tracking who lost, who won, and the number of ties. A single Pokemon battles 146,000 times.
With the statistics aggregated, I ranked the Pokemon in two ways:
Bayesian ranking — priors were set as 50/50 chancePercent ranking
Bayesian ranking — priors were set as 50/50 chance
Percent ranking
The top 20 Pokemon, shown below by Bayesian rank, seems logical in that Mewtwo is ranked number one. Mewtwo is considered the best Pokemon in the first generation with attributes that make it so. It was also one of the rarest Pokemon to capture.
The bottom 20 Pokemon also seems logical since Magikarp is last. Magikarp has the worst attributes in the game with low defense and attack.
For a full list of rankings, please visit this link.
While my implementation of Pokemon battling is limited, the results seem logical. Hopefully, I have inspired another Pokemon fan to take some of these concepts so they may apply them in their simulation. Simulating and modeling is a great skill to have. Applying it to something you are familiar with makes the modeling process more manageable.
Please take the time to critique this work if you find it interesting.