Player: Verify correctness of `Game_Enemy::ChooseRandomAction`

Created on 7 Jul 2019  路  5Comments  路  Source: EasyRPG/Player

The RPG Maker 2003 Steam help file says this:

Process for Choosing Attack Patterns

?The action that the enemy character will take will be chosen based on the following rules.

1.Of all attack patterns, those that fit the ?conditions? will be chosen. If there are multiple relevant conditions, (a) the one with the highest priority and (b) anything within 9 priority points of (a) will be considered.

2.The importance of each attack pattern is calculated based on the effectiveness of the action during the current situation in battle. These values will be mutually compared among the attack patterns that were considered, and converted into a choice probability (the percentage that each pattern comprises out of the total importance value).

3.Using the choice probabilities, the attack pattern will be randomly decided.

This sounds like there is some smart logic here like the autobattle algo which evaluates the effectiveness of the action to weigh the action's probability. Our code does a randomized selection weighed by rating only.

How confident are we in the algo we use?

@CherryDT any help here?

Battle Patch available

All 5 comments

In RPG_RT I noticed a special behavior of the AI if an enemy plans to use a skill which removes states only (without affecting anything else): He actually targets an ally which is affected by at least one state which will be removed by the skill. Moreover such skills are only used at all by the AI if at least one ally (or the user himself) are affected by the mentioned states. Otherwise the action gets skipped and the AI selects another action (if applicable).

Here is a basic sketch of the RPG_RT algorithm

  1. If enemy cannot act, then no action
  2. If enemy is provoked or charged - attack a random live actor
  3. If enemy is confused - attack a random live enemy
  4. Collect all behaviors
  5. Filter out all behaviors whose preconditions are not met
  6. Filter out skill behaviors which are not usable by the enemy
  7. Compute max priority from remaining behaviors
  8. Filter out all behaviors whose priority is < max_priority - 10
  9. Filter out skill behaviors which are ineffective on all of it's possible targets

    • If skill targets self, check if is effective on self

    • if skill targets single ally or multiple allies, check if is effective on at least 1 other monster

    • if skill targets single enemy or multiple enemies, allow the behavior unconditionally

  10. Select a random behavior from the remaining behaviors
  11. Perform targeting

    • If targets all enemies, all allies, or self, then set the target

    • If targets all enemies, choose a random live actor

    • If targets all allies, choose a random other enemy with whom the skill is effective on

  12. Set switch activation flags, to be activated when the action is executed.

A few bugs I see already in player w.r.t. the above:

  • [ ] The IsSkillUsable() check should check for affect hp / mp and/or inflict states. This is missing because that logic is in the Game_Party skill usable function.
  • [ ] We don't check if skill is effective on targets
  • [ ] We should support pluggable enemy ai algorithms, just like autobattle. (Not a bug, but a feature we should have)

Some interesting observations regarding the weighting of priorities:

The action which fulfills the conditions with the highest priority gets a weight of 10. All the other actions which fulfill the conditions are getting a weight according to the table below:

|Priority|Weight|
|-|-|
|Highest|10|
|Highest - 1|9|
|Highest - 2|8|
|Highest - 3|7|
|Highest - 4|6|
|Highest - 5|5|
|Highest - 6|4|
|Highest - 7|3|
|Highest - 8|2|
|Highest - 9|1|
|Highest - 10 and below|Action gets discarded|

All weights get added to a total value which is the denominator for the action selection probability calculation. Each action probability is calculated with weight / (sum of the weights).

Example: This example uses 5 actions which have a priority of 40, 37, 33, 30 and 29. The fourth and fifth action gets discarded because their priority is 10 and more below the highest priority. The denominator is the sum of the weights of the remaining actions which is 20 in this example. The probabilities are shown in the table below:

|Action|Priority|Weight|Probability|
|-|-|-|-|
|Action 1|40|10|10/20 (50%)|
|Action 2|37|7|7/20 (35%)|
|Action 3|33|3|3/20 (15%)|
|Action 4|30|-|Action gets discarded|
|Action 5|29|-|Action gets discarded|

One interesting outcome of this algorithm.

Suppose an enemy has 2 actions:

  1. Attack: Rating 50
  2. Skill Revive: Rating 65

Because IsSkillEffectiveOn() filtering happens at a later step, the revive action will always be chosen even if there are no dead enemies. The result is that the enemy will do nothing (Null action) each round until another enemy is killed, thereupon which it will try to revive the other enemy.

This is different from the IsSkillUsable() check, which is part of the first stage of filtering. If the enemy in the example above did not have enough sp to use the revive skill, then it would choose the attack action.

Was this page helpful?
0 / 5 - 0 ratings