Documents proposed architecture for moving card-building logic upstream to Python, including: - Executive summary with problem statement and migration path - CardBuilder sketch with contract system for pluggable placement strategies - Support for different card "personalities" (Standard, Clutch, Power Heavy, etc.) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
6.3 KiB
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:
- Python generates continuous chance values (e.g., 4.78 out of 108)
- Database fits to discrete card mechanics (2d6 × d20 combinations)
- 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
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
- Deterministic output - What Python calculates is exactly what appears on the card
- No step 7 - Database doesn't modify/re-save values
- Local preview - Validate cards without hitting the API
- Single source of truth - Fitting logic in one place
Strategic
- Card personalities - Same player can have different "feels" via contracts
- Testable - Unit test card building without database
- Extensible - Add new contracts without touching core logic
- 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:
[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_singleredirect 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
-
Shared package or copy? Extract to pip-installable package both repos import, or maintain synced copies?
-
Keep raw chances? Store both raw (for analysis) and fitted (for rendering), or just fitted?
-
Contract storage - Where to store contract selection? Player level? Cardset level? Both?
-
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:
cd ~/.claude/scratchpad/2026-01-22-card-builder-sketch
python card_builder_sketch.py