PSDK .25 AI
The PSDK .25 AI has been reworked from scratch because the previous AI had a way to work that made it very inflexible. Now the .25 AI is using OOP to allow full customization.
How does it work ?
In PSDK, the AI should answer to two kind of request:
- What to do?
(#trigger
) this correspond to the exact same choice as performed by player
- With who to switch?
(#request_switch(who)
) this correspond to the switch choice when one of the player mon is dead but on AI side.
Each trainer unless they’re couple are controlled by a separate AI. This is why AI are initialized with the two following parameter:
- bank
: telling from which bank the pokemon are controlled by the AI
- party_id
: telling to which party in the bank (party index in Battle Info) the pokemon are being controlled by AI
For example, player usually control Pokémon from bank 0 and party_id 0. This mean that the AI that would replace the player needs to be initialize with 0, 0.
The trigger method
The #trigger
method is responsive of choosing the action to do for all Pokemon the AI controls. Meaning that in couple battle a single AI can control upto 2 Pokémon at once.
This method calls #battle_action_for(pokemon)
for all Pokemon it controls.
The #battle_action_for(pokemon)
method will attempt to find the best actions depending on its capability. All the actions will have an heuristic
and they’ll be shuffled before the maximum one is taken. This prevent two identical heuristic to cause the same action to always be triggered. (Regardless, it doesn’t prevent a high heuristic to always trigger the same action!)
Here’s the list of method called by #battle_action_for(pokemon)
:
- #move_action_for(move, pokemon)
combined with #usable_moves(pokemon)
in order to get all the possible move action and their heuristics.
- #mega_evolve_action_for(pokemon)
in order to get the possible mega action (if the AI is capable of mega evolving)
- #switch_actions_for(pokemon, move_heuristics)
in order to get the possible switch action (if the AI is capable of switching)
- #item_actions_for(pokemon, move_heuristics)
in order to get the possible item use action (if the AI is capable of using items)
- #flee_action_for(pokemon)
in order to get the possible flee action (if the AI is capable of fleeing)
How the heuristic is calculated
One of the most important heuristic is the move heuristic. In order to allow some computation we made a specific choice regarding this heuristic. We needed some heuristic to use exponential curve, this mean that we made the move heuristic start at a certain level a theorically end at another level.
Move heuristic
The move heuristic is computed the following way:
ruby
heuristic = (0.75 + base_power * effectiveness * stab / 8000) * special_modifier
When special modifier is 1, this formula yields values between 0.75 & 1.0 where 0.75 is a move with 0 effectiveness, and 1.0 is the most powerfull move (explosion: 250) with a stab of 2 (ability) and an effectiveness of x4 (double type).
This formula might be wrong on 3rd type modification & with a standard special modifier, that’s a risk we agree to take. Feel free to fix that yourself.
Regarding special_modifier. This modifier is set by default to Math.sqrt(user.atk / target.dfe)
(depending on the category). Some moves might implement their own factor, for example protect might get a decreasing factor depending on the number of time it was used.
Note: The power of status move is computed the following way
ruby
Math.exp((user.last_sent_turn - $game_temp.battle_turn + 1) / 10.0) * effectiveness * 0.85
If the move has a special modifier, it’s set to 1 instead of this formula.
Flee heuristic
This action has very high priority, if the Pokémon can flee, the heuristic is infinite.
Switch heuristic
This kind of action has a specific behaviour. When called by trigger if capable, it will first evaluate the danger factor allowing or not the Pokemon to try switch actions. If the AI cannot read opponent move (because unused or incapable) the danger factor will be random between 0 & 1. If the danger factor is higher than the maximum user move heuristic, the user will get switch computation to choose a better Pokemon to switch in.
The definitive heuristic if switch got computed is the maximum heuristic of the moves the Pokemon to switch in.
Item heuristic
Boosting item
Boosting item are treated the same way as status move so the heuristic of those item is the following:
ruby
Math.exp((pokemon.last_sent_turn - $game_temp.battle_turn + 1) / 10.0) * 0.85
Healing item
Heal item are only considered if the AI can heal and the hp of the Pokemon are below a certain threshold (0.1). The formula for the heuristic is the following:
ruby
(wrapper.item.is_a?(GameData::ConstantHealItem) ? wrapper.item.hp_count.to_f / pokemon.max_hp : wrapper.item.hp_rate) * 2.0
If an item heals 50% of the user HP, you’re almost certain that the item will be used if the user has less than 10% of its life.
Burn healing item
Those items can be used only if the AI can heal and the user is burnt. The heuristic formula is:
ruby
1 - pokemon.hp_rate / 4
Poison healing item
Those items can be used only if the AI can heal and the user is poisoned. The heuristic formula is:
ruby
1 - pokemon.hp_rate / 4
Paralysis healing item
Those items can only be used if the AI can heal and the user is paralyzed. The heuristic is 0.78
.
Note: We’d like some return over this heuristic.
Freeze healing item
Those items can only be used if the AI can heal and the user is frozen. The heuristic is 0.85
.
Note: We’d like some return over this heuristic.
Sleep healing item
Those items can only be used if the AI can heal and the user is asleep. The heuristic is 0.76
.
Note: We’d like some return over this heuristic.
How the AI level is choosen
If you build the battle info yourself, you can set the AI level yourself. Otherwise, if you rely on automatic generation it is decided the following way:
- If the battle is a roaming battle, the AI level is -1 (RoamingWild)
- If the battle is a wild battle, the AI level is 0 (Wild)
- If the variable Yuki::Var::AI_LEVEL (34) is greater than 0, the level is this variable
- If the base money is strictly below 16, the AI level is 0 (Wild)
- If the base money is strictly below 20, the AI level is 1 (TrainerLv1)
- If the base money is strictly below 36, the AI level is 2 (TrainerLv2)
- If the base money is strictly below 48, the AI level is 3 (TrainerLv3)
- If the base money is strictly below 80, the AI level is 4 (TrainerLv4)
- If the base money is strictly below 100, the AI level is 5 (TrainerLv5)
- If the base money is strictly below 200, the AI level is 6 (TrainerLv6)
- If the base money is strictly below Infinity, the AI level is 7 (TrainerLv7)
Wild
Wild AI is the most basic AI, it does not know anything and just use random moves on random targets.
TrainerLv1
AI corresponding to the youngster & similar trainers (base money < 20)
Its capability are: - can_see_power
TrainerLv2
AI corresponding to the bird keeper (base money < 36)
Its capability are: - can_see_power - can_see_effectiveness
TrainerLv3
AI corresponding to the bird keeper (base money < 48)
Its capability are: - can_see_power - can_see_effectiveness - can_see_move_kind
TrainerLv4
AI corresponding to gambler (base money < 80)
Its capability are: - can_see_power - can_see_effectiveness - can_see_move_kind - can_mega_evolve - can_switch - can_use_item
TrainerLv5
AI corresponding to Boss (base money < 100)
Its capability are: - can_see_power - can_see_effectiveness - can_see_move_kind - can_mega_evolve - can_switch - can_use_item - can_choose_target
TrainerLv6
AI corresponding to rival, gym leader, elite four (base money < 200)
Its capability are: - can_see_power - can_see_effectiveness - can_see_move_kind - can_mega_evolve - can_switch - can_use_item - can_choose_target - can_heal
TrainerLv7
AI corresponding to champion (base money >= 200)
Its capability are: - can_see_power - can_see_effectiveness - can_see_move_kind - can_mega_evolve - can_switch - can_use_item - can_choose_target - can_heal - can_read_opponent_movepool
RoamingWild
This AI is for Roaming Pokémon, it inherit from TrainerLv3 and add the can_flee capability.
How to define a new AI
In order to define a new AI, you have to create a AI class that inherit from Battle::AI::Base. You can specify its capability by redefining #init_capability
like done in TrainerLv1
. You can also change any aspect of the AI as long as you use proper input output for all redefined functions.
Once your class is fully defined, you can call Battle::AI::Base.register(level, klass)
.
Example: ```ruby # AI corresponding to the youngster & similar trainers (base money < 20) class TrainerLv1Custom < Battle::AI::Base private
def init_capability super @can_see_power = true end end Battle::AI::Base.register(96, TrainerLv1Custom) ```
How to define a specific move ai rate
Since the AI cannot describe everything, especially for status move. We decided to make moves responsive of giving their own specific heuristic. To do so, you need to define the following method in the move class: #special_ai_modifier(user, target, ai)
. This method takes the user, target and the AI that called the method. This way you can have a very precise definition.
Example:
ruby
class SleepingMove < Battle::Move
# Define the special ai modifier of this move
# @param user [PFM::PokemonBattler]
# @param target [PFM::PokemonBattler]
# @param ai [Battle::AI::Base]
def special_ai_modifier(user, target, ai)
return user.asleep? ? 1 : 0
end
end
Note: This move is not actually a sleeping move, it’s just an example for the special AI modifier.
The special AI modifier might get multiplied to the move power if the move is not a status move. If the move is a status move and is innefective, the result of special_ai_modifier might get multiplied to 0 so you don’t have to handle that case.
How to see AI decisions
PSDK comes with a simple AI debug window, you can show it by writing the following line in the console:
ruby
ScriptLoader.load_tool('AI/DebugWindow'); Debug::AiWindow.run
Note: This is not supported on Mac OS.