MosswartOverlord/docs/plans/2026-01-30-suitbuilder-design.md
erik e0265e261c Add suitbuilder backend improvements and SSE streaming fix
- Add dedicated streaming proxy endpoint for real-time suitbuilder SSE updates
- Implement stable sorting with character_name and name tiebreakers for deterministic results
- Refactor locked items to locked slots supporting set_id and spell constraints
- Add Mag-SuitBuilder style branch pruning tracking variables
- Enhance search with phase updates and detailed progress logging
- Update design document with SSE streaming proxy fix details

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 19:14:07 +00:00

252 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Suitbuilder Design Document
**Date:** 2026-01-30
**Status:** In Progress
**Reference:** `magsuitalgo.md` (detailed MagSuitBuilder algorithm analysis)
---
## Section 1: Problem Statement & Goals
### What is Suitbuilder?
Suitbuilder helps players find the best equipment combination from their inventory across multiple "mule" characters. In Asheron's Call, players have many characters storing thousands of armor pieces, jewelry, and accessories. Finding the optimal combination manually is impractical.
### Why is this hard?
The naive approach would check every possible combination: if you have 50 items per slot across 17 equipment slots, that's 50^17 combinations - more than atoms in the universe. MagSuitBuilder solved this with smart algorithms, and we're replicating that approach.
### Main Goal - Priority Order
Given user constraints, find equipment combinations that:
1. **Complete armor set bonuses** (5 pieces primary set + 4 pieces secondary set) - HIGHEST PRIORITY
2. **Cover all required spells/cantrips** without duplicates
3. **Maximize armor protection** - LOWEST PRIORITY (tiebreaker)
### Why we're doing this
The original MagSuitBuilder is a Windows desktop app. We want this functionality in our web-based Dereth Tracker so players can search their inventories from any browser.
---
## Section 2: Algorithm Overview
### The Bucket Approach
Instead of checking all item combinations, we organize items into "buckets" by equipment slot (Head, Chest, Hands, etc.). Then we search through buckets one at a time using recursion with backtracking.
### How it works
1. **Create buckets** - One bucket per equipment slot (11 armor slots + jewelry slots)
2. **Sort buckets** - Process smallest buckets first (fewer branches to explore)
3. **Recursive search** - Try each item in bucket 1, then recurse to bucket 2, etc.
4. **Backtrack** - When a branch fails constraints, undo and try next item
5. **Skip allowed** - Can skip a slot entirely (incomplete suit better than constraint violation)
### Why this is fast
- Bucket with 20 items × bucket with 15 items × bucket with 10 items = 3,000 combinations
- vs. checking every item against every other item = millions of combinations
### Two-Phase Search (from MagSuitBuilder)
1. **Phase 1 - ArmorSearcher**: Find optimal armor combinations (enforces set constraints)
2. **Phase 2 - AccessorySearcher**: Add jewelry to complete spell coverage
### Key Data Structures
- **Spell Bitmap**: 32-bit integer where each bit = one spell. Overlap detection is instant via bitwise OR
- **Coverage Mask**: Tracks which body areas are covered to prevent conflicts
- **Set Counter**: Tracks pieces per armor set (max 5 primary, 4 secondary)
---
## Section 3: Current Implementation State
**Updated after Task 1 Audit (2026-01-30)**
### What's Working ✅
| Feature | Location | Status |
|---------|----------|--------|
| Bucket creation (all 17 slots) | Lines 1006-1135 | ✅ Working |
| Bucket sorting (armor first, smallest first) | Lines 1127-1130 | ✅ Working |
| ItemPreFilter (surpassing logic) | Lines 503-583, used at 969 | ✅ Working |
| Item sorting (by spell count/ratings) | Lines 971-991 | ✅ Working |
| Spell bitmap system | SpellBitmapIndex class | ✅ Working |
| Set constraints (5+4 logic) | can_add_item() | ✅ Working |
| Coverage mask with reductions | Lines 1137-1240 | ✅ Working |
| API Endpoints | `/search`, `/characters`, `/sets` | ✅ Working |
| Streaming Results (SSE) | recursive_search() | ✅ Working |
| Frontend | `suitbuilder.html`, `suitbuilder.js` | ✅ Working |
### What's Broken ❌
| Bug | Location | Issue |
|-----|----------|-------|
| **Early termination** | Lines 1326-1329 | "TEMPORARY FIX" stops after finding 1 suit |
| **Armor level not scored** | `_calculate_score()` | Not included as tiebreaker |
### Current Scoring Formula
```
score = set_completion_bonus (1000 per complete set)
+ set_missing_penalty (-200 per missing piece)
+ crit_damage (10-20 per item)
+ damage_rating on clothes (10-30)
+ spell_coverage (100 per fulfilled spell)
+ base_item_score (5 per item)
MISSING: + armor_level as lowest-priority tiebreaker
```
### Code Structure
- `suitbuilder.py`: 1,847 lines
- Main solver class (`ConstraintSatisfactionSolver`): ~1,130 lines
### Note: magsuitalgo.md Analysis Was Outdated
The analysis document incorrectly stated:
- ❌ "Only creates 2 buckets" → Actually creates all 17
- ❌ "No item pre-filtering" → ItemPreFilter exists and is used
- ❌ "No item sorting" → Items are sorted by spell count/ratings
The code is in much better shape than documented. Only 2 bugs need fixing.
---
## Section 4: Implementation Plan
**Updated after audit - most tasks already done!**
---
### Task 1: Audit & Document Current Search Flow ✅ COMPLETE
**Findings:**
- Bucket creation: ✅ All 17 slots created correctly
- Pre-filtering: ✅ ItemPreFilter.remove_surpassed_items() used at line 969
- Sorting: ✅ Items sorted by spell count (armor/jewelry) and damage rating (clothes)
- Set constraints: ✅ 5+4 logic in can_add_item()
**Bugs Found:**
1. Early termination at lines 1326-1329 ("TEMPORARY FIX")
2. Armor level missing from scoring
---
### Task 2: Fix Bucket Creation ✅ ALREADY WORKING
No changes needed - all 17 slots are created at lines 1006-1135.
---
### Task 3: Fix Search Completion ✅ COMPLETE
**Goal:** Remove early termination so search finds multiple suits
**The Bug (was at lines 1326-1329):**
```python
# TEMPORARY FIX: Stop search after finding first suit to test completion
if len(self.best_suits) >= 1:
logger.info(f"[DEBUG] EARLY TERMINATION...")
return
```
**Fix Applied:** Removed this block entirely.
---
### Task 4: Add Item Sorting ✅ ALREADY WORKING
No changes needed - items sorted at lines 971-991.
---
### Task 5: Add Armor Level to Scoring ✅ COMPLETE
**Goal:** Add armor_level as lowest-priority tiebreaker
**Location:** `_calculate_score()` method
**Fix Applied:** Added armor level scoring after base item score:
```python
# 6. Armor level as tiebreaker (LOWEST PRIORITY)
# Scale down significantly so it only matters when other scores are equal
armor_score = state.total_armor // 100 # ~5 points per 500 AL
score += armor_score
```
---
### Task 6: Add Item Pre-Filtering ✅ ALREADY WORKING
No changes needed - ItemPreFilter used at line 969.
---
### Task 7: Add AccessorySearcher Phase (Future)
**Goal:** After armor search, optimize jewelry slots
**Scope:** Separate task, do after core search is verified working
---
## How to Use This Document
**For Claude (AI assistant):**
- Read this document at the start of each session
- Work on ONE task at a time
- Verify before moving to next task
- If confused, re-read the relevant section
- Do NOT make changes outside the current task scope
**For the developer:**
- Update "Status" at top when tasks complete
- Add notes under each task as we learn things
- Reference `magsuitalgo.md` for detailed algorithm explanations
---
## Progress Tracking
| Task | Status | Notes |
|------|--------|-------|
| Task 1: Audit Current Flow | ✅ Complete | Found 2 bugs, most features working |
| Task 2: Fix Bucket Creation | ✅ Already Working | No changes needed |
| Task 3: Fix Search Completion | ✅ Complete | Removed early termination at lines 1326-1329 |
| Task 4: Add Item Sorting | ✅ Already Working | No changes needed |
| Task 5: Add Armor Level Scoring | ✅ Complete | Added armor_score = total_armor // 100 |
| Task 6: Add Item Pre-Filtering | ✅ Already Working | No changes needed |
| Task 7: AccessorySearcher | Not Started | Future |
| Task 8: Fix SSE Streaming Proxy | ✅ Complete | Added dedicated streaming endpoint in main.py |
---
## Bug Fixes Applied
### SSE Streaming Proxy Fix (2026-01-30)
**Problem:** The generic inventory proxy at `/inv/{path:path}` used `httpx.request()` which buffers the entire response before returning. For SSE streams like suitbuilder search, this caused:
- No progress updates reaching the frontend
- "Gateway Time-out" after buffering the full 5-minute search
**Solution:** Added dedicated streaming proxy endpoint `/inv/suitbuilder/search` in `main.py`:
```python
@app.post("/inv/suitbuilder/search")
async def proxy_suitbuilder_search(request: Request):
async def stream_response():
async with httpx.AsyncClient.stream(...) as response:
async for chunk in response.aiter_bytes():
yield chunk
return StreamingResponse(
stream_response(),
media_type="text/event-stream",
headers={"X-Accel-Buffering": "no"} # Disable nginx buffering
)
```
**Result:** Suits now stream to frontend in real-time with scores visible as they're found.