While I don't expect this to be easy/practical, I figured I'd ask just in case it was a thing...
Is it possible to define somewhere specific patterns of attack for certain trainers to use when possible?
My game is based off of an online gym league. In the online version, the leaders are played by real people, and there are some pretty infamous strategies that we pull off.
For instance, our Bug gym leader is well known for her Baton Pass strategy. Volbeat uses Tail Glow, giving it +3 SpA, and then Baton Passes to a Speed Boost Yanmega, which usually 6-0's her opponents. This is only a two-move strategy, so not terribly complex... I was thinking it would be great if I could make the in-game bug leader attempt the same thing. Of course if they just select moves randomly, the chances that it'll open Tail Glow and then Baton Pass to the correct pokemon are extremely unlikely.
This could also be applicable for gym leaders in projects as a whole. I've noticed in my game that Gym Leaders often don't use their 'signature move' (whatever TM they give out) as often as they perhaps should. Is there maybe some way to give proirity to a certain move per trainer?
The script section PokeBattle_AI is the first place to look.
For your example, I would implement it in two parts. The first part is to make Tail Glow very favoured if @opponent.trainertype==PBTrainers::GYMLEADER_BUG (e.g. +150 to the score). The second part is to make Baton Pass very favoured if attacker.stages[PBStats:PATK]>=3 && @opponent.trainertype==PBTrainers::GYMLEADER_BUG (or something similar).
There's a bit of AI code (line 1948) which states that, if a move has a really high score compared to the other moves, there is a 90% chance that it is automatically chosen rather than doing the standard weighted choice between them (which would most likely choose the preferred move anyway). Specifically, this happens if a move's score is greater than 100 and the standard deviation of all the scores is also greater than 100. I'm not sure what numbers you'd have to add on above in order to meet this requirement, though.
That's what I'd do - just fiddle with the scores for each function code.
Oooh. Okay, that's not nearly as complicated as I thought. c:
However, I think I will wait until the AI overhaul happens before I go fiddling around with it too much. Even if it's a while, it's not a big deal. When I do start it though, I'll probably have like twenty more questions about this so I apologise ahead of time. >>
Yeah, you'll be waiting a while for an AI improvement from me. I really think I'd need some help with it, simply because I don't play Pokémon nowadays (and never competitively), so I don't know how an expert trainer should think. I can code it, but someone else needs to tell me what to make the AI do and when. And given there's 307 different function codes, plus some miscellaneous weighting (like STAB and immunities and weather), that's a lot to deal with.
mAYBE YOU COULD DO A RE-DESIGN AND SET IT SO THAT A PLAYER CAN SET GYM LEADERS VIA THE SCRIPT SECTION FOR ai... (Sorry bout the caps, I'm going to continue with proper text now)
By this I mean something that looks like this kind of style.
#Pokémon 2,moves# if
#Pokémon 3,moves# if
#Pokémon 4,moves# if
#Pokémon 5,moves# if
#Pokémon 6,moves# if
Then you could use your little bit of code up there to specify weather moves and combo attacks, although you'd probably have to state which order they would need to be in, maybe even a true/false boolean for which moves the AI should officially use... As for trainers having better AI, that would be all you haha... It could give you an idea where to start, I do like the idea of this kind of AI though.
The first thing would be to get a feel for the numbers used, and all the effects that are considered/should be considered. A score is initially 100, but can go down to 0 or up to a number I don't know. Then there's figuring out what importance each effect should have (e.g. does a move benefiting from STAB outweigh the target having a Reflect up?). The skill of the trainer should probably alter the score change by most (every?) effect (but how much and in what ways?).
We first need to get a reasonable idea of what score changes are appropriate. For example, should a thawing move gain +70 or +130 to its score if the user is frozen, or should it vary depending on the trainer's skill or the opponent's known moves, and if so, by how much? There are many other examples, but if we can figure out the difference between "slight bias towards"/"prefer"/"strongly prefer"/"definitely use this" at the function code stage, then that's the start. Don't just extrapolate from what's already there - figure out the limits yourself and see if the existing code agrees.
The nice thing about the move-choosing AI is that it's all in the one (long) method. Remember, though, that there's other AI too, such as deciding whether to switch Pokémon or use an item, and they'll need looking at too.
In short, have a look at the current AI and make a note of everything you think is important, of what you would expect a hypothetical effect to depend on and how, and see if there's anything that blatantly needs fixing. It's a huge topic to go into, but an interesting one.
Okay, first question. I'm assuming the 'score' is given to a particular action, and has an influence in the probability of that action being chosen, but do we know how that's determined? i.e does it just choose the action with the highest score, or does it total the scores as a percentage and roll into proportions from that?
Also, just skimming the AI for now, I'm a little confused as to the context of this. Like, the code starts checking for type effectiveness, right? But is it taking a single move and seeing what it can do with it, or is it judging what types the opponent is and then choosing which of its moves would be best? I see a couple different directions this could be moving, and I'm not syntactically fluent enough to tell immediately. ><
A score is calculated for each move in turn, and then one of the moves is randomly chosen with the scores used as probabilities. For example, a move with a score of 140 would be twice as likely to be chosen as one with a score of 70.
That's the very basic summary. As I mentioned above, if one move's score is a great deal higher than the others, then it will be chosen automatically 90% of the time (and will most likely be chosen in the remaining 10% anyway given its relatively huge score).
So yes, for each move in turn, it checks a whole load of things (function code, weather, types, potential damage dealt, Choice Band, battle progress, etc.) to see what can be done with that move, and gives that move an appropriate score. It seems a reasonable method to me, although it can be done differently.
The entirety of the AI is made up (unlike, say, the shininess calculation), so any of it can be changed at will.
Okay, that's definitely going to be the better way to start.
So let's look at it like this:
A trainer/pokemon in essentials has 16 possibilties of what they can do in a given turn:
1-4: They use move 1-4
5-9: They switch to Pokemon #2-6
10-15: They use their item in slot 1-6 (iirc essentials gives 6 item slots, right? but you get the point either way)
16: They run (if it is a wild Pokemon)
I'm not sure how you're handling fleeing-wilds, or even if you are, or even how the main series does it, but if that's a thing to be trifled with, probably best to get it out of the way first, leaving 15 options if the battle is a Trainer, or a Pokemon that has decided not to flee.
The next priority would go to self-preservation, be it in the form of items or a reliable recovery move (Recover etc, Rest, and Pain Split is going to need its own contextually-dependent handler, but that might need to be separate).
In order to do this effectively, the Pokemon/Trainer needs to be able to predict the incoming damage combined with residual damage. I could see this being a little tricky, as in, variables from past turns would need to store what happened. Tell me if this would be practical:
The following assumes that the AI pokemon has a reliable recovery move, besides Pain Split.
Firstly, check if the Player has just switched Pokemon (even if due to fainting, or if it's the first turn of the battle).
If so, reset any prediction variables.
Then, judge whether the Player or AI Pokemon is faster:
If the Player Pokemon was in previously, recall which Pokemon moved first. The answer, whether it was the AI or the Player, should be stored in a prediction variable (this may need to be defined in the previous turn).
If that variable shows that the same Pokemon that is now in play moved first last turn, assume that it will move first again.
If that variable cannot determined, check the base stats of both Pokemon species. Take the level difference between the two pokemon and add [level difference]x[n] (subject to tweaking? maybe 2 or 3) to the base speed of the higher-leveled Pokemon. Judge using those modified values which Pokemon will move first.
With those assumptions in place, remember if the player used Tailwind, a priority move (besides Sucker Punch), or if their speed stage has changed (a la dragon dance, speedboost, icy wind, etc) since the previous turn. If so, change the prediction to assume the player will move first.
Remember if the AI used Tailwind, has Prankster or if their speed stat has changed since the previous turn. If so, assume the AI will move first.
If both of these conditions are true, then we need to recall specifically what happened. Vague order of priority/outcome (in case of = resort to the assumption based on the previous turn/base stats alone)
Player Extremespeed (if AI is not ghost type)
Player Priority besides Sucker Punch = AI Prankster
Player Tailwind or +2 to speed = AI Tailwind or +2 to speed
Player +1 to speed = AI +1 to speed
Player -1 to speed = AI -1 to speed
Player -2 to speed = AI -2 to speed
Player paralyzed = AI paralyzed
Or we could just cheat in the first place and have AI detect what the player's actual speed stat is (but previous use of priority/ESpeed and Prankster still need to factored in)
Anyway, after determining who AI thinks is going to move first, it needs to figure out how much damage it expects to take. This should be done by recalling what % of damage it took on the last turn, or what the maximum non-crit damage it's taken since that Pokemon has been in was.
If the total cannot be assessed (i.e a new pokemon is out) and the AI is expected to move second, use a recovery move when health is below 50% (in the case of Recover-esques) or 33% + the expected amount of residual damage (in the case of Rest)
If the total cannot be assessed and the AI is expected is expected to first, use a recover-esque move when health is below 33% + expected residual, or Rest when HP is below 25% + expected residual
By expected residual damage, I mean it should also calculate the amount of damage from statuses such as Sand, Hail, Leech Seed, Poison, Burn, Curse, Black Sludge, Sticky Barb, Wrap etc and Whirlpool etc.
The total should then be modified if Light Screen or Reflect were used by the AI (* 0.5), if they used a relevant defense-boositng move (consider type of last attack, if physical see if Def increased, special sp.def of course)(if so, *0.75 perstage). If AI is a rocktype and sandstorm has newly come into play (*0.75). If the attacking type was water or fire, and rain/sun have come into play (* 1.5/*.75 and *.75/*1.5 respectively). If the opponent used a relevant offense-boosting moving (*1.5 per stage). If defenses dropped (*1.5 per stage), or offense dropped (*.75 per stage)
...I think that's all...
If the total can be assessed and the AI is expected to move first, use a recovery move when health is less than the % of damage that is expected to be done + the expected residual, and less than 75%
If the total can be assessed and the AI is expected to move second, use a recovery when health is less than twice the % damage that is expected to be done + residual, and less than 87.5%
Something like that should create a fairly robust item-independent mechanism for making stall-sets on AI pokemon viable, something I've noticed in the current version is ineffective because even if a Pokemon has a recovery move, they often choose not to use it.
Also it totally is a word. c: I just double checked. I may have used it somewhat awkwardly though.
The AI begins in the method pbDefaultChooseEnemyCommand. It first checks whether the enemy trainer should use an item on the Pokémon. If not, it checks whether it should switch out/flee. If not, then it scores up the moves and chooses one of them to use.
The only Pokémon that flee from battles are roaming Pokémon, which always try to flee as soon as its turn occurs in a battle round (this is separate to the move-choosing, i.e. a move is chosen as well just in case it can't flee and needs to fight).
Essentials currently doesn't care for strategies per se. That is, it doesn't consider one strategy first, see if the Pokémon should use it, and does what's best there. The function code part of the AI simply looks at all relevant parts of the battle and adjusts a move's score accordingly. For example, Rest currently does this:
if attacker.hp==attacker.totalhp || attacker.status==PBStatuses::SLEEP
score+=30 if attacker.status!=0
Basically: "Don't use if already at full HP or asleep" and "More likely to use the less HP you have" and "More likely to use if you have a status problem (which Rest will replace)".
Relative speed is currently only considered for certain moves, not in a general sense, but that's fine. It only really affects the moves it's currently used for anyway (such as paralysing moves, two turn moves, moves with Speed-dependent damage, moves which need to happen first/last, moves affecting Speed stat stages, etc.). Other moves may well need to depend on relative speeds, but they can be picked up on a case-by-case basis. At the moment we're still being rather general.
Rather than that complicated mess of remembering what each battler has done in the past, the computer does cheat a bit and use the actual stats of a battler (rough stats, i.e. ignoring any modifiers, although you suggest to consider them now). The amount it cheats by (i.e. how many modifiers it counts) should depend on the trainer's skill. Whether or not the AI should be allowed to know what the player has chosen to do this round is another question (e.g. more likely to use Pursuit if it knows the player is going to switch out).
Pretty much everything you've said about the healing moves will apply to the function codes for those moves, and won't involve inventing a new kind of AI system. It seems a lot to consider when you write it all out like that, when it's just for a handful of moves. Perhaps some factors aren't important enough to be included, or are only considered by skilled trainers.
An important part of the AI should be the trainer's skill, which is the base amount of money defined for that trainer's type. Wild Pokémon have a skill of 0, and Gym Leaders have a skill of 100. There should be several different "layers" of AI for different skill levels, and while the actual values can be determined later on, it would be good to get a feel for which layer of AI should consider which effects, and how many layers there should be. The highest layer (Gym Leaders) will consider everything, of course.
I'm trying to think of some sort of scaling math system to account for adjust proportions of varying skill scores, but I'll get back to you about ideas for that once I have more focus on it.
To be clear though, this wouldn't be trying to 'favor' a stall strategy, but just make the Pokemon that have access to these moves self-sustainable as they should be in the first place, whereas they otherwise often choose not to use these moves even if they have them and are at critical condition.
The other place that this comes in is for using those recovery items. These moves would take priority over wasting such an item on a Pokemon that has its own sustainability.
For the most part what I'm suggesting is geared towards a maximum skill opponent because I think it's going to be much easier to tone it down than it will be to tune it up for different trainers.
But, moral of the story is that the general practicality is smart-casting over the more human-prediction. Got it.