MosswartOverlord/docs/suitbuilder.md
erik 4e73a5d07d docs: add suitbuilder algorithm documentation
Describes the full search pipeline, item loading, bucket creation,
armor reduction, scoring weights, and constraint satisfaction logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 21:01:25 +00:00

9.3 KiB

Suitbuilder Algorithm

The suitbuilder finds optimal equipment loadouts across multiple characters' inventories. It fills 17 equipment slots (9 armor, 6 jewelry, 2 clothing) using a constraint satisfaction solver with depth-first search and branch pruning.

Search Pipeline

The search runs in 5 phases, streamed to the browser via SSE:

  1. Load items - Fetch from inventory API (armor by set, jewelry by slot type, clothing DR3-only)
  2. Create buckets - Group items into 17 slot buckets, expand multi-slot items
  3. Apply reductions - Generate tailored variants of multi-coverage armor pieces
  4. Sort buckets - Order buckets and items within them for optimal pruning
  5. Recursive search - Depth-first search with backtracking, streaming top 10 results

Item Loading

Items are fetched from the internal inventory API (localhost:8000/search/items) in four batches:

Batch Filter Notes
Primary set armor item_set={name} All armor in user's primary set
Secondary set armor item_set={name} All armor in user's secondary set
Clothing shirt_only / pants_only Only DR3+ shirts and pants
Jewelry jewelry_only + slot_names={type} Rings, bracelets, necklaces, trinkets separately

After loading, a domination pre-filter removes items that are strictly worse than another item in the same slot with the same set. Item A is "surpassed" by item B when B has equal-or-better spells (Legendary > Epic > Major), equal-or-better ratings, equal-or-better armor, and is strictly better in at least one category.

Bucket Creation

Each of the 17 slots gets a bucket. Items are assigned to buckets with special handling:

  • Multi-slot items (e.g., "Left Wrist, Right Wrist") are cloned into each applicable slot bucket
  • Generic jewelry ("Ring" -> Left Ring + Right Ring, "Bracelet" -> Left Wrist + Right Wrist)
  • Robes (6+ coverage areas) are excluded entirely - they can't be reduced to single slots

All 17 buckets are created even if empty, allowing the search to produce incomplete suits when no valid item exists for a slot.

Armor Reduction (Tailoring)

Multi-coverage armor can be tailored to fit a single slot. Only loot-generated items (those with a material) are eligible. Reduction patterns follow Mag-SuitBuilder logic:

Original Coverage Reduces To
Upper Arms + Lower Arms Upper Arms or Lower Arms
Upper Legs + Lower Legs Upper Legs or Lower Legs
Lower Legs + Feet Feet
Chest + Abdomen Chest
Chest + Abdomen + Upper Arms Chest
Chest + Upper Arms + Lower Arms Chest
Chest + Upper Arms Chest
Abdomen + Upper Legs + Lower Legs Abdomen or Upper Legs or Lower Legs
Chest + Abdomen + Upper Arms + Lower Arms (hauberks) Chest
Abdomen + Upper Legs Abdomen

Reduced items are added to the target slot's bucket as "Item Name (tailored to Slot)".

Bucket Sort Order

Bucket ordering (which slot to fill first)

Buckets are searched in this priority:

  1. Core armor - Chest, Head, Hands, Feet, Upper Arms, Lower Arms, Abdomen, Upper Legs, Lower Legs
  2. Jewelry - Neck, Left Ring, Right Ring, Left Wrist, Right Wrist, Trinket
  3. Clothing - Shirt, Pants

Within each category, buckets are further sorted by their position in the priority list (not by item count). This means armor slots are always filled before jewelry, and jewelry before clothing.

Item ordering within each bucket

Items within a bucket are sorted to try the best candidates first. The sort depends on slot type:

Slot Type Sort Priority (highest first)
Armor User's primary set > secondary set > others, then crit damage rating desc, then damage rating desc, then armor level desc
Jewelry Spell count desc, then total ratings desc
Clothing (Shirt/Pants) Damage rating desc, then spell count desc, then other ratings desc

All sorts include (character_name, name) as final tiebreakers for deterministic results.

The solver uses depth-first search with backtracking across the ordered buckets:

for each bucket (slot) in order:
    for each item in bucket:
        if item passes constraints:
            add item to suit state
            recurse to next bucket
            remove item (backtrack)
    if no items were accepted:
        skip this slot (allow incomplete suits)
        recurse to next bucket

When all buckets are processed, the suit is scored and kept if it ranks in the top N (default 10).

Branch Pruning

Two pruning strategies cut off hopeless branches early:

  1. Mag-SuitBuilder style: If current_items + 1 < highest_armor_count_seen - remaining_armor_buckets, prune. This ensures we don't explore branches that can't produce suits with enough armor pieces.

  2. Max-items pruning: If current_items + remaining_buckets < best_suit_item_count, prune. The branch can't produce a suit with more items than the best found so far.

Item Acceptance Rules (can_add_item)

An item must pass all of these checks:

  1. Slot available - The slot must not already be occupied in the current suit state
  2. Item uniqueness - The same physical item (by ID) can't appear in multiple slots
  3. Set membership (armor only):
    • Primary set items: accepted up to effective limit (5 minus locked primary pieces)
    • Secondary set items: accepted up to effective limit (4 minus locked secondary pieces)
    • Other set items: rejected for armor slots, allowed for jewelry only if they contribute required spells
    • No-set items: rejected for armor, allowed for clothing always, allowed for jewelry only if they contribute required spells
  4. Spell contribution (when required spells are specified):
    • Items with spells must contribute at least one new required spell not already covered by the current suit
    • Items where all spells are duplicates of already-covered spells are rejected, even from the target sets
    • Jewelry has an additional gate: it must contribute an uncovered required spell or it's rejected (empty slot preferred over useless jewelry)

Locked Slots

Users can lock specific slots with a predetermined set and/or spells. Locked slots are:

  • Removed from the bucket list (not searched)
  • Their set contributions are subtracted from set requirements (e.g., 2 locked primary pieces means only 3 more needed)
  • Their spells are counted as already fulfilled

Scoring

The scoring system determines suit ranking. Points are awarded in this priority order:

1. Set Completion (highest weight)

Condition Points
Primary set complete (found pieces >= effective need) +1000
Secondary set complete +1000
Missing primary piece -200 per missing piece
Missing secondary piece -200 per missing piece
Excess primary pieces (beyond 5) -500 per excess piece
Excess secondary pieces (beyond 4) -500 per excess piece

2. Crit Damage Rating (armor pieces)

Rating Points
CD1 (crit_damage_rating = 1) +10 per piece
CD2 (crit_damage_rating = 2) +20 per piece

3. Damage Rating (clothing only - Shirt/Pants)

Rating Points
DR1 +10 per piece
DR2 +20 per piece
DR3 +30 per piece

4. Spell Coverage

Condition Points
Each fulfilled required spell +100

5. Base Item Score

Condition Points
Each item in the suit +5

6. Armor Level (tiebreaker only)

Condition Points
Total armor level +1 per 100 AL (e.g., 4500 AL = +45)

Score is floored at 0 (never negative).

Practical Effect of Scoring Weights

The weights create this effective priority:

  1. Complete sets matter most - A suit with both sets complete (+2000) always beats one with a missing piece, regardless of other stats
  2. Spells matter second - Each required cantrip/ward is worth +100, so 10 spells = +1000 (equivalent to one complete set)
  3. Crit damage and damage rating are tiebreakers - CD2 on all 9 armor pieces = +180, DR3 on both clothes = +60
  4. Armor level barely matters - Only ~45 points for a full suit of 4500 AL; it only breaks ties between otherwise-equal suits

Frontend Display

Results stream in as SSE events. The frontend maintains a sorted list of top 10 suits:

  • New suits are inserted in score-ordered position (highest first)
  • If the list is full (10 suits) and the new suit scores lower than all existing ones, it's discarded
  • Medals are assigned by position: gold/silver/bronze for top 3

Score Display Classes

Score Range CSS Class
>= 90 excellent
>= 75 good
>= 60 fair
< 60 poor

Item Display

Each suit shows a table with all 17 slots. Per item:

  • Armor pieces: Show CD (crit damage) and CDR (crit damage resist) ratings
  • Clothing pieces: Show DR (damage rating) and DRR (damage resist rating)
  • Spells: Show up to 2 Legendary/Epic spells, then "+N more"
  • Multi-slot items that need tailoring are marked with an asterisk (*)

Suit Selection

Clicking a suit populates the right-panel equipment slots visual. Users can then:

  • Lock slots (preserving set/spell info for re-searches)
  • Copy suit summary to clipboard
  • Clear individual slots