Tips, Filters & Scoring Reference
Tips, Filters & Scoring Reference
Section titled “Tips, Filters & Scoring Reference”Practical guidance for writing effective AI configs, plus reference tables for move filters, item filters, and score modifiers.
Tips and Common Pitfalls
Section titled “Tips and Common Pitfalls”1. Use score_bonus for status moves
Section titled “1. Use score_bonus for status moves”The number one mistake is creating action priorities for setup or status moves with only bias_multiplier. A status move starts at a base score of approximately 1.0. Even bias_multiplier: 10.0 gives you 10.0, which loses to Earthquake at 100.0. Always add score_bonus for non-damaging actions.
{ "id": "setup_swords_dance", "condition": "q.pokemon.current_hp_percent > 0.7", "action_type": "move", "move_filter": { "names": ["swordsdance"] }, "priority": 70, "bias_multiplier": 1.5, "score_bonus": 80.0}2. matchup.score vs under_threat
Section titled “2. matchup.score vs under_threat”q.matchup.score only considers STAB types and is very coarse (roughly -4 to +4). It often returns 0 for neutral matchups, making conditions like > 0 unreliable.
q.pokemon.under_threat (0.0 to 1.0) is more nuanced — it considers super-effective types, speed comparison, opponent stat boosts, and HP. Prefer q.pokemon.under_threat for setup and switch decisions.
// Less reliable — coarse, often 0 for neutral matchups"condition": "q.matchup.score > 0"
// More reliable — considers multiple factors"condition": "q.pokemon.under_threat < 0.3"3. Rule priority tiers
Section titled “3. Rule priority tiers”Rules are evaluated from highest to lowest priority. Put your most important rules at high priority numbers. Here are recommended tiers:
| Priority Range | Category | Examples |
|---|---|---|
| 200+ | Hard overrides | Trapped = cannot switch, max boosts = stop boosting |
| 90—100 | Critical decisions | Heal at very low HP, priority move for the finish |
| 70—85 | Strategic decisions | Setup when safe, Toxic against walls |
| 50—60 | Tactical preferences | Coverage bonuses, screen setup |
| 5—20 | General scoring | Accuracy, STAB, type effectiveness |
4. Inheritance rule list behavior
Section titled “4. Inheritance rule list behavior”When you specify action_priorities: [...] in a child config, your rules are prepended to the parent’s rules. They do not replace them. Child rules come first and are evaluated first.
If you want to start fresh and discard all inherited rules, use an empty array:
{ "parent": "smart_trainers:expert", "action_priorities": []}This clears the parent’s action priorities entirely. The same behavior applies to move_scoring_rules, switch_conditions, and gimmick_rules.
5. Use bias 0.0 to completely prevent an action type
Section titled “5. Use bias 0.0 to completely prevent an action type”Setting bias_multiplier: 0.0 combined with a high priority is the way to prevent actions entirely:
{ "id": "never_switch_boosted", "condition": "q.pokemon.stat_boosts.atk >= 3", "action_type": "switch", "priority": 200, "bias_multiplier": 0.0}This prevents the AI from ever switching out a Pokemon that has +3 or more Attack boosts. The score is multiplied by 0, making it impossible to select.
6. Debug mode
Section titled “6. Debug mode”You can enable debug mode to see the AI’s decision process in chat. All scores, rule matches, and final rankings are printed for each turn. This is invaluable when tuning AI configs — you can see exactly why the AI chose a particular action and which rules fired.
7. Showdown move ID format
Section titled “7. Showdown move ID format”When using "names" or "move_ids" in move filters, use Showdown-format IDs: lowercase, no spaces, no hyphens.
| Move Name | Showdown ID |
|---|---|
| Swords Dance | swordsdance |
| Dragon Dance | dragondance |
| Will-O-Wisp | willowisp |
| U-turn | uturn |
| Volt Switch | voltswitch |
8. MoLang syntax basics
Section titled “8. MoLang syntax basics”MoLang is the expression language used for all conditions in Smart Trainers configs. Here is a quick reference:
// Comparison operatorsq.pokemon.current_hp_percent < 0.5 // less thanq.pokemon.current_hp_percent > 0.5 // greater thanq.pokemon.current_hp_percent == 0.5 // equalq.pokemon.current_hp_percent >= 0.5 // greater or equalq.pokemon.current_hp_percent <= 0.5 // less or equal
// Logical operatorsq.pokemon.current_hp_percent < 0.5 && q.target.has_status == 0 // ANDq.pokemon.current_hp_percent < 0.3 || q.pokemon.has_status // OR!q.target.has_status // NOT
// Boolean values: 0 = false, anything > 0 = true// So q.target.has_status alone works as a boolean check
// Mathq.move.power * q.move.type_effectivenessq.pokemon.current_hp_percent * 100q.target.current_hp_percent * 2.5
// Function calls (parentheses)q.pokemon.has_type('fire')q.pokemon.has_move('earthquake')q.target.has_revealed_ability('levitate')
// Constant true condition"condition": "1"Move Filters
Section titled “Move Filters”Move filters are used in action_priorities and gimmick_rules to target specific moves. All fields are optional — omitting a field means “match any.”
{ "move_filter": { "categories": ["heal", "buff"], "types": ["fire", "water"], "names": ["swordsdance", "dragondance"], "move_ids": ["earthquake"], "condition": "q.move.power > 80" }}| Field | Type | Description |
|---|---|---|
categories | string[] | Match moves with any of these AI categories. |
types | string[] | Match moves of any of these elemental types. |
names | string[] | Match specific move names (Showdown IDs). |
move_ids | string[] | Same as names — both are checked. |
condition | Molang | Additional Molang condition. Gives access to q.move.* queries. |
Item Filters
Section titled “Item Filters”Item filters are used in usageRules within battle bag configs to target specific items.
{ "itemFilter": { "categories": ["HEALING"], "items": ["cobblemon:full_restore"] }}| Field | Type | Description |
|---|---|---|
categories | string[] | Match items with any of these categories. |
items | string[] | Match specific item IDs. |
condition | Molang | Additional Molang condition. |
Score Modifiers
Section titled “Score Modifiers”Score modifiers are used in move_scoring_rules. Each rule has a score_modifier with a type and value. The value field is always a Molang expression, so it can reference queries dynamically.
| Type | Behavior | Example |
|---|---|---|
multiply | new_score = old_score * value | \{"type": "multiply", "value": "1.5"\} |
add | new_score = old_score + value | \{"type": "add", "value": "50"\} |
set | new_score = value (ignores old score) | \{"type": "set", "value": "0"\} |
script | new_score = value (Molang expression returns the final value) | \{"type": "script", "value": "q.move.power * q.move.type_effectiveness"\} |
Because the value field is a Molang expression, you can write dynamic modifiers:
{ "type": "multiply", "value": "q.move.type_effectiveness"}This multiplies the move’s score by its type effectiveness (2.0 for super effective, 0.5 for not very effective, etc.).