The PokéCommunity Forums

The PokéCommunity Forums (https://www.pokecommunity.com/index.php)
-   Binary Hack Research & Development (https://www.pokecommunity.com/forumdisplay.php?f=195)
-   -   Research Trainer AI in Firered (https://www.pokecommunity.com/showthread.php?t=333767)

knizz August 12th, 2014 4:38 PM

Trainer AI in Firered
 
Hello.

I found what seems to be the scrips that determine how trainers act during battles. It's a different bytecode than that used for NPCs, or moves, or moveanimations, or overworld effects, etc.

It runs a series of 32 scripts for each move in the moveset, the scripts can add or subtract to a score for this move depending on the circumstances and then quit.
Unlike the npc scripts there aren't serveral variables that you can access by number but only one that you can use freely, and another one that contains the id of the move being considered right now.
In case a command can use either variable as input, there are two variants of it with separate codes.
(In my notes __8 is the free variable and _2 is the move being considered, I'm renaming these however wherever I encounter them in my notes. _8, _16 and _32 refer to the lengths one byte, two bytes, four bytes)
Many of these commands are followed by one byte that specifies if it refers to the attacking pokemon (1) or defending pokemon (0).

The dispatch table is at 083F55A4. Meanings as far as I have figured them out so far are:
Spoiler:
Code:

00 random_goto__high_param_likely
01 random_goto__low_param_likely
02 random_goto__1_in_256_chance
03 random_goto__255_in_256_chance
04 viability_score (followed by the value that should be added FF=-1 FE=-2 …)
05 jump_if_health_percentage_lt
06 jump_if_health_percentage_ge
07 jump_if_health_percentage_eq
08 jump_if_health_percentage_ne
09 jump_if_any_status1_bit
0A jump_if_no_status1_bit
0B jump_if_any_status2_bit
0C jump_if_no_status2_bit
0D jump_if_any_status3_bit
0E jump_if_no_status3_bit
0F jump_if_any_status4_bit
10 jump_if_no_status4_bit
11 jump_if__8_lt_8
12 jump_if__8_gt_8
13 jump_if__8_eq_8
14 jump_if__8_ne_8
15 jump_if__8_lt_32
16 jump_if__8_gt_32
17 jump_if__8_eq_32
18 jump_if__8_ne_32
19 jump_if_move_id_eq_16
1A jump_if_move_id_ne_16
1B jump_if__8_in_list_8
1C jump_if__8_not_in_list_8
1D jump_if__8_in_list_16
1E jump_if__8_not_in_list_16
1F jump_if_attacker_has_any_damaging_moves
20 jump_if_attacker_has_no_damaging_moves
21 get_battle_turn_counter__8
22 get_some_type
23 move_get_power__2_8
24 is_most_powerful_move__8
25 get_move_to_execute_B
26 jump_if__8_ne_2
27 jump_if__8_eq_2
28 jump_if_move_would_hit_first
29 jump_if_move_would_hit_second
2A —
2B —
2C count_alive_pokemon_on_team
2D get_move_id__8
2E move_get_move_script_id
2F get_ability
30 simulate_damage_muliplier_four_times
31 simulate_damage_bonus_jump_if_eq
32 —
33 —
34 jump_if_any_party_member_has_status_ailment_32
35 jump_if_no_party_member_has_status_ailment_32_BUGGED
36 get_weather__8
37 jump_if_move_id_eq_8
38 jump_if_move_id_ne_8
39 jump_if_stat_buff_lt
3A jump_if_stat_buff_gt
3B jump_if_stat_buff_eq
3C jump_if_stat_buff_ne
3D determine_move_damage_jump_if_fatal
3E determine_move_damage_jump_if_not_fatal
3F jump_if_has_move
40 jump_if_hasnt_move
41 jump_if_move_with_same_movescript_in_either_0_2_history_or_1_3_moveset
42 jump_if_move_with_same_movescript_in_neither_0_2_history_nor_1_3_moveset
43 is_moveset_restricted
44 jump_if_or_if_not_current_move_in_encore
45 f10_or_b1011
46 jump_random_unknown
47 f10_or_b1101
48 get_held_item_x12__8
49 pokemon_species_get_gender_info
4A enter_battle_countdown_get_state
4B stockpile_get_num_uses
4C is_double_battle
4D get_dp08_item__8
4E move_get_type__8
4F move_get_power__8_8
50 move_get_move_script_id__8
51 get_protect_endure_activity
52 —
53 —
54 —
55 —
56 —
57 —
58 call
59 jump
5A return_and_eventually_f10_or_b0001
5B compare_attacker_defender_levels
5C jump_if_taunt_turns_ne_0
5D jump_if_taunt_turns_eq_0




Reminder regarding status1 and status2. The Pokemon Information relevant for a battle is copied from their own data structures into four structures at 02023BE4 (two for player side, two for opponent side). JPAN documented this in this post: http://www.pokecommunity.com/showpost.php?p=7156541&postcount=5 (in the RAM-structures spoiler) and status1 and status2 are part of that structure.

The array of 32 pointers to such scripts is at 081D9BF4.
Code:

081D9BF4:  081D9C74 081DA445  081DBA6F 081DBA8D
081D9C04:  081DBAEF 081DBADF  081DBB16 081DBB3B
081D9C14:  081DBB3C 081DBC91  081DBCED 081DBCED
081D9C24:  081DBCED 081DBCED  081DBCED 081DBCED
081D9C34:  081DBCED 081DBCED  081DBCED 081DBCED
081D9C44:  081DBCED 081DBCED  081DBCED 081DBCED
081D9C54:  081DBCED 081DBCED  081DBCED 081DBCED
081D9C64:  081DBCED 081DBCA8  081DBCD6 081DBCDD


It's worth noting that the entry "081DBCED", that appears multiple times here, doesn't do anything and can be replaced by custom move-rating-scripts.

Here are some lightly documented examples:
Spoiler:
Code:

// don't use moves that are negated by "soundproof"

081D9CE0 2F              @ get ability of
081D9CE1 00              @  defender

081D9CE2 14              @ jump if not equal to
081D9CE3 soundproof
081D9CE4 081D9D27

081D9CE8 19 (jump_if_move_id_equals_16)
081D9CE9 mve_growl
081D9CEB 081DA433 (lower viability of move by 10)

081D9CEF 19 (jump_if_move_id_equals_16)
081D9CF0 mve_roar
081D9CF2 081DA433 (lower viability of move by 10)

081D9CF6 19 (jump_if_move_id_equals_16)
081D9CF7 mve_sing
081D9CF9 081DA433 (lower viability of move by 10)

081D9CFD 19 (jump_if_move_id_equals_16)
081D9CFE mve_supersonic
081D9D00 081DA433 (lower viability of move by 10)

081D9D04 19 (jump_if_move_id_equals_16)
081D9D05 mve_screech
081D9D07 081DA433 (lower viability of move by 10)

081D9D0B 19 (jump_if_move_id_equals_16)
081D9D0C mve_snore
081D9D0E 081DA433 (lower viability of move by 10)

081D9D12 19 (jump_if_move_id_equals_16)
081D9D13 mve_uproar
081D9D15 081DA433 (lower viability of move by 10)

081D9D19 19 (jump_if_move_id_equals_16)
081D9D1A mve_metal_sound
081D9D1C 081DA433 (lower viability of move by 10)

081D9D20 19 (jump_if_move_id_equals_16)
081D9D21 mve_grasswhistle
081D9D23 081DA433 (lower viability of move by 10)

081DA433 04 (add to viability)
081DA434 F6 (minus 10)
081DA435 5A (end)

081DAB77 5                @ jump if health of
081DAB78 1                @  attacker
081DAB79 70                @  less than 70%
081DAB7A 081DAB86          @  target
081DAB7E 0x3A              @ jump if
081DAB7F 0                @  defender
081DAB80 5                @  spdef
081DAB81 3                @  ?
081DAB82 081DAB8E          @ target

081DAB86 0
081DAB87 0x32
081DAB88 081DAB8E
081DAB8C 4                @ move not so viable
081DAB8D -2                @  minus 2 points

081DAB8E 6                @ jump if health of
081DAB8F 0                @  defender
081DAB90 70                @  greater than 70%
081DAB91 081DAB97          @  target
081DAB95 4                @ move not so viable
081DAB96 -2                @  minus 2 points
081DAB97 0x5A              @ end




More information can be found in my IDA database.

At this point I need to thank JPAN. Never before have I relied on JPANs research this much.

tzujm33 August 13th, 2014 10:29 AM

Really NICE, ****ING GREAT if IT would be possible to better the enemy ai, GREAT JOB!

knizz August 14th, 2014 5:11 AM

I also made some overview graphics, for people that are also reverse engineering the battle system.

Notes:
A → B (solid) means "A calls B.".
A → B (dashed) means "A causes B to be called.".
A → B (dotted) is like a call (solid), in the sense that after B is done, A continues running, but it is realized through means like function pointers (dashed).

The blue part exists in at least seven variants for different circumstances. And each battle side (player side 1&2, and opponent side 1&2) can use a different set of the blue functions. It's also likely that the blue part is the interface for link battles.

'bs', 'bx', and 'bc' have no special meanings here ('b' is for 'battle'). I just use random letters to group these.


First graphic describes how a move script can start a move animation script.

The second graphic describes the startup-process of a battle. I omitted the lines from "c1_exec_bc_and_bx" to the other boxes because it got messy, instead I colored the boxes green.

The third graphic emphasizes that move scripts can be run by different subsystems (by a bc_ callback directly, or through a bs_ callback).


I omitted the addresses in most places, but you can look up these names in my database. (Or ask me)

Lance32497 November 26th, 2014 3:51 AM

have you successfully made it in FireRed?Sorry I was just confused

Red John January 20th, 2015 9:21 AM

Quote:

Originally Posted by Lance32497 (Post 8509812)
have you successfully made it in FireRed?Sorry I was just confused

You are confused. This an AI table which would determine how trainers would behave.
And this is awesome knizz! This means I can implement new moves without having to worry about how the AI would use it. Thanks man

daniilS April 29th, 2015 9:52 AM

I've taken a quick glance at the different trainer ai scripts, and here's a short summary of what they do:
Code:

0        won't use moves that have no or a negative effect
1        will actually create some difference in viability scores based on basic calculations
2        starts understanding things like type effectiveness, damage and fainting
3        with a chance of about 3/10, some status moves will get priority on the first turn (list of move scripts at 081DBAA7)
4        with a chance of about 5/10, move effects in this list will get priority: http://pastebin.com/raw.php?i=TP5gCBHp
5        with a chance of about 4/10, non-damaging moves will get priority
6        if there are other pokes left on the ai's team, with a chance of a bit more than 6/10 it will give priority to non-damaging moves. chance raises to a bit more than 9/10 if the user (possibly also its ally) has baton pass
7        nop
8        ai will start looking at health percentages and decide using moves based on that
9        broken/unfinished, does nothing
10-28        nop
29        roamer; will attempt to flee on the first turn
30        safari
31        unused; will flee on 20% health and lower



daniilS April 29th, 2015 12:25 PM

double post because this update deserves it

So, know how sometimes even a smart AI will fail to guess/recognise the opponent's ability?
Well, turns out that is a bug. Long story short: ability usage history can be stored for each of the two/four participants in a battle. It is however only stored for the player: the routine won't do anything if you ask it to store it for the opponent. However, there's a problem: the big and scary function ability_something does magic with abilities, and sometimes it needs to receive the battle side as an argument. When it doesn't need that, the argument will be 0. But at the end of the routine, it will save the ability to history if it was used, and there's the problem: 0 means player's side, and the ability gets registered as if it were used by your poke. So, let's say, if the opponent sends out a poke with intimidate, then the AI will think it is actually your poke that has intimidate, until it gets to activate its real ability. This all was fixed in Emerald.

Now, actually fixing this would mean tracking down every time ability_something is called with an empty argument for the battle side, and that's just too much work. Instead, I offer you guys this solution: I have rewritten the routine that stores the used ability into history: if it's asked to store your poke's ability into history, it will check if your poke actually does have that ability, and if it doesn't, then it won't store anything. Just insert the following routine at 0xC71D0:
Code:

.thumb
.equ loc, 0x080C71D0

check:
        ldr r3, battle_data
        mov r2, #0x58
        mul r2, r0
        add r2, r3
        add r2, #0x20
        ldrb r2, [r2]
        cmp r1, r2
        beq store
doublecheck:
        cmp r0, #0x0
        bne end
        mov r0, #0x2
        b check
store:
        ldr r2, b_resources
        ldr r2, [r2]
        ldr r2, [r2, #0x18]
        add r2, #0x20
        strb r1, [r2, r0]
end:
        bx lr

.align 2
battle_data:        .word 0x02023BE4
b_resources:        .word 0x02023FF4


The AI should now properly guess your poke's ability.

robinjea April 29th, 2015 3:31 PM

Quote:

Originally Posted by daniilS (Post 8728848)
I've taken a quick glance at the different trainer ai scripts, and here's a short summary of what they do:
Code:

0    won't use ridiculously stupid moves
1    will actually create some difference in viability scores based on basic calculations
2    calculates if the opponent and/or the user will faint because of the move
3    if it's the first turn, then there's a chance a status move will get priority
4    there's a chance moves with cool additional effects will get priority
5    makes the ai like status moves a tad more in some situations. possibly makes the ai more stupid, not sure
6    does magic that may take future into account, including baton pass calculations. seems to generally become smarter
7    nop
8    ai will start looking at health percentages and decide using moves based on that. becomes smart
9    broken/unfinished, does nothing
10-28    nop
29    knizz said it's used for the old man
30    safari
31    roamer



Can I ask if this is the value we would put at the Pokemon AI Value in A-Trainer or is this something else? If it IS something else, where do we put it?

daniilS April 29th, 2015 11:29 PM

Quote:

Originally Posted by robin22gongon (Post 8729195)
Can I ask if this is the value we would put at the Pokemon AI Value in A-Trainer or is this something else? If it IS something else, where do we put it?

Don't use A-Trainer: it calls the IV value the AI value. The only two editors that get this right are Jambo51's unnamed trainer editor and Kurapika's Trainer Editor in G3T—however, neither of them allows setting the value to its true max. I've asked Kurapika to fix this, so let's wait and see. Otherwise you could just manually change it with a hex editor.

Aruaruu April 29th, 2015 11:46 PM

Quote:

Originally Posted by daniilS (Post 8729896)
Don't use A-Trainer: it calls the IV value the AI value. The only two editors that get this right are Jambo51's unnamed trainer editor and Kurapika's Trainer Editor in G3T—however, neither of them allows setting the value to its true max. I've asked Kurapika to fix this, so let's wait and see. Otherwise you could just manually change it with a hex editor.

The AI value in A-Trainer is marked as unknown. Unsure if you knew that but whatever...
Also I was wondering if they were preset strategies and I seem to have mistakenly thought that 7 was the best.

Umm, what does nop mean in a case like this though? Would it just select moves randomly or something?

daniilS April 29th, 2015 11:51 PM

Quote:

Originally Posted by Aruaruu (Post 8729904)
The AI value in A-Trainer is marked as unknown. Unsure if you knew that but whatever...
Also I was wondering if they were preset strategies and I seem to have mistakenly thought that 7 was the best.

Umm, what does nop mean in a case like this though? Would it just select moves randomly or something?

A NOP literally does nothing. It won't alter the viability scores, so if that's all you select then it's equal to a wild battle. And yes, people had thought 7 was the max, but I'm not sure if they checked more than the first byte or if they checked stuff like the Trainer Tower. And what do you mean by preset strategies?

Aruaruu April 30th, 2015 12:07 AM

Quote:

Originally Posted by daniilS (Post 8729907)
A NOP literally does nothing. It won't alter the viability scores, so of that's all you select them it's equal to a wild battle. And yes, people had thought 7 was the max, but I'm not sure if they checked more than the first byte or if they checked stuff like the Trainer Tower. And what do you mean by preset strategies?

By preset I mean that they were made so the AI could use things like baton pass well if the value was set to 06 or would use moves with secondary effects more if set to 04
That probably wasn't the best way to describe it though. :/

daniilS April 30th, 2015 2:35 AM

Quote:

Originally Posted by Aruaruu (Post 8729904)
The AI value in A-Trainer is marked as unknown. Unsure if you knew that but whatever...

UPDATE:
Turns out A-trainer allows giving that unknown a max value of 0xFFFF, so turns out that's the only program that supports giving trainers a proper AI atm...

Also I've just updated the two posts from last night. The AI scripts' descriptions now contain enough information, and the bugfixroutine should more or less work in double battles.

DizzyEgg May 1st, 2015 2:20 AM

Quote:

Originally Posted by daniilS (Post 8728848)
I've taken a quick glance at the different trainer ai scripts, and here's a short summary of what they do:
[code]0 won't use moves that have no or a negative effect
1 will actually create some difference in viability scores based on basic calculations
2 starts understanding things like type effectiveness, damage and fainting
3 with a chance of about 3/10, some tatus moves will get priority on the first turn (list of move scripts at 081DBAA7)
4 with a chance of about 5/10, moves in this list will get priority: http://pastebin.com/raw.php?i=TP5gCBHp
5 with a chance of about 4/10, non-damaging moves will get priority
6 if there are other pokes left on the ai's team, with a chance of a bit more than 6/10 it will give priority to non-damaging moves. chance raises to a bit more than 9/10 if the user (possibly also its ally) has baton pass
7 nop
A NOP literally does nothing. It won't alter the viability scores, so of that's all you select them it's equal to a wild battle
.....

Are you sure 7 does nothing? I was always giving this value to strong trainers like leaders and they acted smart. Always going for super effectiveness, often setting up + clever baton passing, they were even able to stall(to a certain degree)

daniilS May 1st, 2015 2:31 AM

Quote:

Originally Posted by DizzyEgg (Post 8731600)
Are you sure 7 does nothing? I was always giving this value to strong trainers like leaders and they acted smart. Always going for super effectiveness, often setting up + clever baton passing, they were even able to stall(to a certain degree)

Seems like I forgot to mention on this thread that the AI value is a bitfield. So if you give a trainer an AI value of 7 (which was assumed to be the max), you activate script 0, 1 and 2.

Splash May 1st, 2015 3:24 AM

Quote:

Originally Posted by daniilS (Post 8731612)
Seems like I forgot to mention on this thread that the AI value is a bitfield. So if you give a trainer an AI value of 7 (which was assumed to be the max), you activate script 0, 1 and 2.

Okay what I'm understanding here is that if you give a value of 7 in the AI value of any tool, the tool will assume that it is the max? Am I correct?

Deokishisu May 1st, 2015 3:26 AM

Quote:

Originally Posted by daniilS (Post 8731612)
Seems like I forgot to mention on this thread that the AI value is a bitfield. So if you give a trainer an AI value of 7 (which was assumed to be the max), you activate script 0, 1 and 2.

Okay, now all of the behavior I've been seeing by messing around with that value makes sense. Thanks for all the awesome work (and knizz too!), especially the fix for Firered up there.

DizzyEgg May 1st, 2015 4:07 AM

Quote:

Originally Posted by daniilS (Post 8731612)
Seems like I forgot to mention on this thread that the AI value is a bitfield. So if you give a trainer an AI value of 7 (which was assumed to be the max), you activate script 0, 1 and 2.

Sorry to bother you, but could you explain what scripts are activated by values 0 - 6? Or how to check what value activates what scripts?

daniilS May 1st, 2015 10:48 AM

Quote:

Originally Posted by Splash (Post 8731657)
Okay what I'm understanding here is that if you give a value of 7 in the AI value of any tool, the tool will assume that it is the max? Am I correct?

Use A-Trainer for now: it allows a max value of 16 bits.
Quote:

Originally Posted by Deokishisu (Post 8731659)
Okay, now all of the behavior I've been seeing by messing around with that value makes sense. Thanks for all the awesome work, especially the fix for Firered up there.

You should really be thanking knizz; He is the one who discovered and documented everything, while I just corrected and updated a few things, and used that info to describe what the scripts do.
Quote:

Originally Posted by DizzyEgg (Post 8731711)
Sorry to bother you, but could you explain what scripts are activated by values 0 - 6? Or how to check what value activates what scripts?

The AI value is a 32-bit bitfield. Every bit, from the 0th to the 31th, activates one of the scripts.

daniilS May 2nd, 2015 9:13 AM

The routine that determines if the AI will switch is located at 08039C84, with the actual thinking happening at 08039A80. I have no idea how it works.


The routine that determines if the AI will use an item is located at 0803A1F4. It will identify the type of item at 0803A198:
Code:

1        full restore
2        heal hp
3        restore status
4        increase stat
5        guard spec
6        other


(see http://www.pokecommunity.com/showpost.php?p=6745155&postcount=11 for more info on battle items).
Then, it will run checks based on the type of item - 1 (see the jump table at 0803A304). For example, it will use a full restore if the hp of the poke is less than 25% of its max hp.


If anyone wants to analyse those routines further, please do.

Deokishisu May 2nd, 2015 9:40 PM

Quote:

Originally Posted by daniilS (Post 8733579)
The routine that determines if the AI will switch is located at 08039C84, with the actual thinking happening at 08039A80. I have no idea how it works.


The routine that determines if the AI will use an item is located at 0803A1F4. It will identify the type of item at 0803A198:
Code:

1        full restore
2        heal hp
3        restore status
4        increase stat
5        guard spec
6        other


(see http://www.pokecommunity.com/showpost.php?p=6745155&postcount=11 for more info on battle items).
Then, it will run checks based on the type of item - 1 (see the jump table at 0803A304). For example, it will use a full restore if the hp of the poke is less than 25% of its max hp.


If anyone wants to analyse those routines further, please do.

I find it interesting that the AI will attempt to use a Revival Herb (fruitlessly) if given one. Which check do you suppose allows for this? I would assume it's either heal hp, restore status, or other. It'd be interesting to see the logic that the game goes through to select a Revival Herb to use, with the obvious end goal being hacking the AI or battle scripts so that Revive-like items actually function for computer opponents. But damn, will that functionality be a nightmare for players when used by the wrong hackers.

daniilS May 2nd, 2015 9:45 PM

Quote:

Originally Posted by Deokishisu (Post 8734164)
I find it interesting that the AI will attempt to use a Revival Herb (fruitlessly) if given one. Which check do you suppose allows for this? I would assume it's either heal hp, restore status, or other. It'd be interesting to see the logic that the game goes through to select a Revival Herb to use, with the obvious end goal being hacking the AI or battle scripts so that Revive-like items actually function for computer opponents. But damn, will that functionality be a nightmare for players when used by the wrong hackers.

I think it gets passed of as heal hp, just check the flags. Coding that in is in my plans.

FIQ May 9th, 2015 7:51 AM

Script 31 isn't unused, it's likely used in the RSE poochyena/zigzagoon fight to make it flee if your HP is low.

Criminon May 10th, 2015 1:08 PM

Hmm. Unable to use my asm insertion program to insert it at that address, so I just looked for free space, copied down the code that it produced. (084B58224243D21820321278914203D0002806D10220F3E7034A12689269203211547047E43B0202F43F0202) and then pasted this over the information at that address, but it keeps freezing. Where exactly does the original code end?


Edit: I'm an idiot. Working as intended. Thanks so much!

MajinBlueDragon August 21st, 2015 1:28 PM

Sorry for this being a bit of a bump, but I noticed that one of the "trainer" AI scripts is that of the roaming/running Pokemon...does this mean that Pokemon such as Entei, Latios, etc (or really, anything) have an "AI value" as well that gets set to this? Does this only control its running behavior, or also the roaming behavior as well? This obviously isn't used for trainer battles, so I'm curious how this actually -is- applied in practice, potentially to create new roaming/fleeing encounters.

Also, are the values here the same as they would be in Emerald, minus the addresses? I know you mentioned Emerald having a bug fix, but other than that I would be able to use this at least as a reference? Thanks so much!


All times are GMT -8. The time now is 8:39 AM.


Like our Facebook Page Follow us on Twitter © 2002 - 2018 The PokéCommunity™, pokecommunity.com.
Pokémon characters and images belong to The Pokémon Company International and Nintendo. This website is in no way affiliated with or endorsed by Nintendo, Creatures, GAMEFREAK, The Pokémon Company or The Pokémon Company International. We just love Pokémon.
All forum styles, their images (unless noted otherwise) and site designs are © 2002 - 2016 The PokéCommunity / PokéCommunity.com.
PokéCommunity™ is a trademark of The PokéCommunity. All rights reserved. Sponsor advertisements do not imply our endorsement of that product or service. User generated content remains the property of its creator.

Acknowledgements
Use of PokéCommunity Assets
vB Optimise by DragonByte Technologies Ltd © 2023.