huge graphical skills were needed for that
Approved by Ginganinja
Summary
Hello, I've been programming for about ten months, randomly stopping and resuming the project, a battle bot, which has now reached the level of a beginner that never anticipates. Clearly not enough to win matches, but I thought it was interesting to present anyway, especially to show how the AI thinks.
It is written in python, and the code is at the end. It does not depend of the tier, only needing a set database to work in any tier. It also adapts to the given team.
Let's jump straight to the AI :
How the AI proceeds
The bot will use two databases according to the tier he's playing in. (Right now, ou.)
One of them is the huge statistic DB available to all : http://www.smogon.com/stats/2015-04/
And the other is one I make myself. It contains the most common sets about the most common pokemons.
Using this, the bot will, as soon as it sees the opponent team, make a "guess" about their sets. In the case he's dealing with a pokemon that is in the precise database, he will assume he has its most common set. If it is not in the handmade database, he will create a (most likely stupid) set using the statistics : most common item, ability, EV spread, and 4 most used moves. This leads to some stupid situations, for instance, if a pokemon use half the time Fire Blast, and otherwise uses Flamethrower... the bot could end up guessing he has both. But that is easily solved by extending the database.
(Add 23/5) If he meets a Pokemon that isn't in the OU database, he will look for him in the UU, then RU, then NU database, and proceed alike.
This having been done, the bot thinks he approximatively has a knowledge of the situation. This leads us to the core of the matter : the bot works by situation rating.
In any situation, the bot will be able to rate it, and that is how the entire decision protocol is decided. When reviewing its possibilities, the bot will then replicate the game mechanics in order to imagine what the situation would be, if this - a specified turn - was to happen. It will then choose what heighten the "situation rating" the most, which ultimately leads to wiping out the enemy team.
That is where the AI is at first different from what would be the most basic (and future-less) way of thinking : looking one turn away, if you're able to inflict more damages than taking.
Let's take several real exemples of what should a situation rating thinking protocol fulfill :
- A fast pokemon that has barely any hp is not worthless. It can still outspeed something and strike. The bot shouldn't throw it away for nothing.
- A slow pokemon that has barely any hp is worthless, priorities apart. No matter the situation, it will get killed before being able to do anything. Thus, it is a death fodder : if the situation gets bad, the bot should sack it, to regain the upper hand.
- Let's say there is one sweeper in each team threatening each other highly. A fast one in the ally team does 50% to the other, and the other, slower, kills it in one hit. If the opportunity comes, the bot should rate very high inflicting 50% to the opponent opponent, because it knows it will completely flip the result of this decisive fight.
- In short, if the opponent has something that threatens the team, it should be ready to make 'losing' trades to weaken it.
There are several advantages to proceeding this way, and mainly the fact that as long as you define what each move does, plus giving particular ratings to weird moves such as hazards, the bot will have an objective sight of the thing. It will use a healing move if its situation is better after a healing move than after doing something else. (heals aren't made yet tho)
And so, here's my situation rating function :
Imaginating every possible 1v1 between allies and enemy, even if they're dead (that's 36.) and have them fight to the death. Then, take the remaining %hp (if both are dead, that's 0 and 0), and add the ally's positively to the score, and the enemy's negatively.
So, the "Situation rating" is a number, between -3600, and 3600.
This method, in addition to fulfilling the cases stated upper, and not being so heavy to calculate (it's pretty much a complexer euclidian division, you keep substracting things), gives an insight of long-term strategy, which is extremely hard to teach to a bot. Toxicing the tanks makes sense when thinking with this method, because it will heavily modify every fight in favor of the bot, since these are long fights (Also, it will make even more sense toxicing those that can heal, once heals are implemented).
There is however one downside to it : this function ignores what's currently happening on the field. Because of this, switches are fucked up without modifications : the bot would switch to someone else only if the current fight is so bad he'd rather take the hit with another mon and not answer. ... That doesn't happen often. Switching isn't only about tanking the hit, you've also got to think about the situation that is about to come. You don't switch in heatran against Mamoswine because it will make you able to tank Ice Shard.
To correct that, I had to add a subjective rating modifier when rating switches : the bot will look at the score modification once he has switched, plus each mon have hit each other once ! Tho right now, it's a bit retarded and only consider that each mon will hit each other once with their best damaging move - not their best move which could be a status one, for exemple.
This isn't randomly dropped : the idea is that if you've got a positive score with this, the opponent will switch out once you've switched in and got hit : the situation being perfectly symetric, this is likely to lead to a global score modification of 0, which is better than keeping on losing your fight two turns before.
Then again, all this is modified by the importance the situation rating gives to each pokemon, if your tank sucks against the opponent team and is only able to tank the pokemon on the field, he will throw it in right there : him getting hit barely changes the score not in your favor. If your pokemon on the field musn't get hit as it would flip the 1v1 with other mons, he will attempt to save it.
This ends my explanation of the AI.
I can see two downside to this method : the first one is that the bot has to assume he knows the situation, and base his entire reasoning out of it. But he doesn't. For instance he will think that all Latios when first met have the four moves he has in mind. But Latios has a coverage that far wider than four moves, thus, if he thinks for exemple the latios don't have earthquake, he will repeatedly throw heatran in ... and die every time latios happened to have earthquake. That is one of my main concern right now and is the real flaw this AI carries everywhere, which I don't know how to fix.
That's typically a problem that I doubt the programmer of this probability based bot : http://www.smogon.com/forums/threads/an-ou-bot.3529338/ has. The way our approach to the problem differ is what had me want to present mine.
The other one is that the bot isn't aiming to wipe out the opponent team, it's aiming to keep the highest situation rating. This can cause weird stuff at game ends, where the bot has an obvious way to finish the battle, but will not proceed as it would lead to a situation rating drop (and victory ._. ). I've never seen it happen (since he never wins >< ) but I can guess that if the opponent has setup moves, this can cause disasters.
What currently lacks
- Heal knowledge. While this is very easy to program (about 2 minutes ..), this implies I would have to remake the "1v1 confrontation to death" part, which currently takes one move for both pokemon and imagine them spam it until eventual death. (Which is a bit dumb, i mean, priorities exist, so the best move changes over time). Also, endless fights management inc...
- LEARNING APTITUDE !! I currently have the structure made to exploit the learning part but no data acquisition part ready ! I mean by that that the pokemons in the main db have several sets stored in and so, the bot will, once it's done, look at the sets that do not contradict what has been seen already. (The idea about this is that something with LO has most likely an offensive set, while something Leftovers will have a defensive - and this is seen through the sets).
- Boosts. Well, the bot will rate the situation where the opponent pokemon is boosted ... but before putting that, I gotta make a cleverer "switch then 1 turn" evaluation. The entire point about boosting is that you will want to put the mon that can boost itself on someone it can boost on. Currently, the switching evualation is taking the upcoming move + having them hit each other.
- Mega-evolution. Well ... I don't know how to do on this part. The enemy pokemon litteraly changes to another ._.
- Hazards.
- Heal bell. Tho this shouldn't be too hard ... "reset all status to normal". But far on my priority list.
- Proper understanding of multi hit moves. Right now, he thinks they all hit once. (except bullet seed, for testing reasons)
- Almost all abilities.
Currently implemented :
The bot knows, aside from damaging moves :
- Paralysis, poison, toxic poison, burns
- Choice items
- Every damage-modifying item
- Priorities on moves, except ability-related ones (hello talonflame).
- Immunity abilities (Volt asborb, levitate, etc)
- Most end-turn hp modifications : Leftovers, Sludge Bomb, weather damage.
- Contact damage : Rough Skin / Iron Barbs / Rocky Helmet
Known bugs
- Alternative forms regrouped that do not have their specific entry in the databases are unknown. For exemple, Keldeo-Resolute needs an abstract rename, as only Keldeo has associated datas, in both smogon's DB and my handmade set DB. Well, handmade, it's a copypastes of smogon's analyses' sets.
- Having one of its mons killed by a pivot-move makes the bot crash. He tries to answer as soon as he witnesses the KO of its mon, except the opponent has something to choose first.
- Choice items do not work with Hidden power. "The opponent just used Hidden Power, so he won't be able to use that "hiddenpowerfire60" that is in his set... right ? RIGHT ?" - Cube.
- Multi hit moves only proc contact damage once in the head of the bot. Well, they only hit once in his head anyway ...
- The bot isn't aware of the toxic poison stacking on his active pokemon. Like, he will consider at every turn that the stacking starts now, from 6.25%.
Code
I'm french, so don't be surprised if there are half the annotations (# stuff) you cannot understand.
Core code (Connection + AI) : http://pastebin.com/hZriU1xX
Surge (Extracts string data and makes it usable matter for python) : http://pastebin.com/84dtu4db
Datacalc (calculation data) : http://pastebin.com/6qMErixR
Datasets (the set pasted data) : http://pastebin.com/DKSPheYJ
I couldn't put the pokedexjs one because it's too heavy for pastebin, but its nearly json.loads(the PS files available on Zarel Github)
To put an exemple for each : http://pastebin.com/nuZKg403
Replays
http://replay.pokemonshowdown.com/ounomega-234254752 (First Cube match on the official ladder)http://replay.pokemonshowdown.com/ounomega-234387090
http://replay.pokemonshowdown.com/ounomega-234412684
http://replay.pokemonshowdown.com/ou-234412678
Test matches where I was the other player:
http://replay.pokemonshowdown.com/pokestrat-ounomega-2980
http://replay.pokemonshowdown.com/pokestrat-ou-3169
---
This thread is a copy of my thread on Technical Projects which is a dead forum, but given this forum has shown interest in OU bots before, I thought I would put it here too. Don't hesitate to comment on / suggest/ask anything !
Last edited by a moderator: