# Card Creation Architecture Redesign - Executive Summary **Date:** 2026-01-22 **Project:** Paper Dynasty Card Generation System **Author:** Cal Corum + Claude (Jarvis) --- ## Problem Statement The current card creation pipeline has an architectural inconsistency: 1. **Python generates continuous chance values** (e.g., 4.78 out of 108) 2. **Database fits to discrete card mechanics** (2d6 × d20 combinations) 3. **Database saves fitted values back** - what you send ≠ what gets stored This results in subtle discrepancies (e.g., sending 4.75, card shows 4.65) because the card mechanics only support specific discrete probability values. ### Root Cause The card uses 2d6 (rows 2-12) combined with d20 splits. Valid chance values are constrained by: - Row frequencies: 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 (totaling 36) - D20 subdivisions: 1-20 range creates fractional chances Python generates values ignorant of these constraints; the database must "round" to fit. --- ## Proposed Solution **Move the card-building logic upstream to Python**, making it the single source of truth. ### New Architecture ``` BEFORE: Python: stats → continuous chances → POST raw values Database: receives → fits to card → saves DIFFERENT values → renders AFTER: Python: stats → continuous chances → CardBuilder → discrete card structure → POST Database: receives → stores → renders (no fitting needed) ``` ### Key Components | Component | Responsibility | |-----------|----------------| | `CardBuilder` | Fits continuous chances to discrete card structure | | `CardContract` | Defines placement strategy (which rows for which plays) | | `BuiltCard` | Structured output ready for storage/rendering | | `RawBattingChances` / `RawPitchingChances` | Input from existing stat calculations | --- ## Contract System A major enhancement: **pluggable placement strategies** that give cards different "personalities" using the same raw stats. ### Available Contracts | Contract | Behavior | Use Case | |----------|----------|----------| | **Standard** | On-base results on middle rows (6-8) | Default behavior | | **Clutch** | On-base results on edge rows (2-4, 10-12) | Role players, postseason heroes | | **Power Heavy** | HR/3B/2B on prime rows, singles on edges | HR leaders, sluggers | | **Contact First** | Singles on prime rows, power on edges | Leadoff hitters, high-AVG players | | **Groundball Pitcher** | Groundouts on middle rows | Sinkerballers | | **Flyball Pitcher** | Strikeouts/flyouts on middle rows | Power pitchers | ### Example: Same Stats, Different Cards ``` Power Heavy Contract: Row 7 (most likely): HOMERUN Row 12 (least likely): SINGLE Contact First Contract: Row 7 (most likely): SINGLE Row 12 (least likely): HOMERUN ``` ### Contract Interface ```python class CardContract: def get_row_preference(self, category: PlayCategory) -> List[int] def get_play_priority(self) -> List[PlayCategory] def should_use_splits(self, category: PlayCategory) -> bool def max_splits_per_column(self) -> int ``` --- ## Benefits ### Immediate 1. **Deterministic output** - What Python calculates is exactly what appears on the card 2. **No step 7** - Database doesn't modify/re-save values 3. **Local preview** - Validate cards without hitting the API 4. **Single source of truth** - Fitting logic in one place ### Strategic 1. **Card personalities** - Same player can have different "feels" via contracts 2. **Testable** - Unit test card building without database 3. **Extensible** - Add new contracts without touching core logic 4. **Data-driven** - Store contract name in DB, apply at generation time --- ## Migration Path ### Phase 1: Extract & Validate - Extract current database fitting logic to shared module - Run old and new in parallel, compare outputs - Fix discrepancies ### Phase 2: Python Adoption - Modify Python card-creation to use `CardBuilder` - POST fitted card structures instead of raw chances - Keep raw chances in separate field for debugging ### Phase 3: Database Simplification - Database receives pre-fitted card structures - Remove fitting logic from database - Eliminate step 7 (no re-saving) ### Phase 4: Enhancements - Add preview endpoint (returns structure without saving) - Implement contract selection in card creation workflow - Build custom contract UI --- ## Technical Details ### Discrete Probability Space Valid chance values are defined by `EXACT_CHANCES`: ```python [5.7, 5.4, 5.1, 4.8, 4.75, 4.5, 4.25, 4.2, 3.9, 3.8, 3.75, ...] ``` Plus whole numbers 1-6 (full row allocations). ### Pitching Card Specifics - **X-checks**: Redirect to batter's card (e.g., `GB (ss) X`) - **Batter Power**: `bp_homerun`, `bp_single` redirect to batter - **Secondary plays**: D20 splits pair primary with complementary outcome - **Bolding**: Strikeouts bolded (good for pitcher), hits not bolded ### Row Frequencies ``` Row: 2 3 4 5 6 7 8 9 10 11 12 Freq: 1 2 3 4 5 6 5 4 3 2 1 ``` Row 7 is most valuable (6 chances = 16.67% of outcomes). --- ## Files Produced | File | Description | |------|-------------| | `card_builder_sketch.py` | Main module: CardBuilder, contracts, data structures | | `contracts.py` | Standalone contracts reference (superseded by integration) | | `EXECUTIVE_SUMMARY.md` | This document | --- ## Open Questions 1. **Shared package or copy?** Extract to pip-installable package both repos import, or maintain synced copies? 2. **Keep raw chances?** Store both raw (for analysis) and fitted (for rendering), or just fitted? 3. **Contract storage** - Where to store contract selection? Player level? Cardset level? Both? 4. **Custom contracts** - Allow users to create custom contracts via UI? --- ## Recommendation **Proceed with the shared card-building module approach.** The contract system adds significant value by enabling card personalities without changing the underlying statistics. This addresses the original consistency problem while opening new design possibilities. **Suggested next step:** Create a proof-of-concept by implementing `CardBuilder` against a single real player's stats and comparing output to current database behavior. --- ## Appendix: Code Location All sketch files are in: ``` ~/.claude/scratchpad/2026-01-22-card-builder-sketch/ ``` To test: ```bash cd ~/.claude/scratchpad/2026-01-22-card-builder-sketch python card_builder_sketch.py ```