feat: implement tweak_archetype() and manual_adjustments() (#12) #49
No reviewers
Labels
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/paper-dynasty-card-creation#49
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "ai/paper-dynasty-card-creation-12"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #12
Summary
tweak_archetype(): Prompts for updated archetype stats (AVG/OBP/SLG/BB%/K% vs L and R, power profile, batted-ball profile, and baserunning for batters), then recalculates D20 card ratings via the existingBatterRatingCalculator/PitcherRatingCalculator. Usescopy.copy()so the original archetype is never mutated.manual_adjustments(): Lets the user choose a split (vs L or vs R), displays all 22 D20 chance fields with a running total, accepts<field_number> <value>edits in a loop, and prints a warning if the total deviates from 108 when done.Files changed
custom_cards/interactive_creator.py— replaced both "Feature coming soon" stubs with working implementations; addedimport copyTest results
No automated test suite. Changes verified by reading the implementation against the archetype dataclass fields and calculator signatures.
🤖 Generated with Claude Code
AI Code Review
Files Reviewed
custom_cards/interactive_creator.py(modified)Findings
Correctness
tweak_archetype()— D20 fields verified. All 22 fields inD20_FIELDSmatch the rating dicts returned by bothBatterRatingCalculator._calculate_split()andPitcherRatingCalculator._calculate_split(). Thepopoutfield (always0.0) is present and correctly included. ✓tweak_archetype()returns{"ratings": ..., "baserunning": ...}for batters and{"ratings": ...}for pitchers, which is exactly whatdisplay_ratings(),manual_adjustments(), andcreate_batting_card()/create_pitching_card()expect. ✓copy.deepcopy()inmanual_adjustments()is correct —card_data["ratings"]is a list of dicts; a shallow copy would have caused in-place mutation of the caller's data. ✓copy.copy()intweak_archetype()is fine —BatterArchetype.primary_positions(a list) is shared by the shallow copy, but is never modified here so no aliasing issue. ✓abs(total - 108.0) > 0.01is appropriate givenmround()rounding to 0.05. ✓Security
input()and converted tofloat/intwith explicittry/except; no injection surface.Style & Conventions
# type: ignorecomments are expected and correct: the union typeBatterArchetype | PitcherArchetypeprevents the type checker from narrowing through theplayer_type == "batter"runtime guard, andPitcherArchetypegenuinely lacksxbt_pct. ✓prompt_float/prompt_intare defined as local helpers insidetweak_archetype(). Given they're single-use, this is appropriately minimal per project conventions. ✓Suggestions
tweak_archetype()always starts fresh from the originalarchetypeobject passed fromcreate_player(). The modifiedarchis local and discarded. If the user invokes option 2 twice (e.g., tweaks AVG first, then wants to further tweak K%), the second prompt shows the original archetype defaults, not the result of the first tweak. The recalculatedcard_datafrom the first tweak is shown visually, but the prompt baselines reset. Users can work around this by usingmanual_adjustments()for follow-up adjustments, or by doing all stat changes in a single tweak session. Worth noting as a follow-up improvement if iterative stat-based tweaking is a common workflow.Verdict: COMMENT
Implementation is correct and matches the PR description. Both stubs are replaced with working, well-guarded implementations. The
D20_FIELDSlist is accurate,copyusage is appropriate, and edge cases (empty input, out-of-range field numbers, negative values) are handled. The non-accumulating tweak behaviour is a minor UX gap but doesn't block merging. LGTM.Automated review by Claude PR Reviewer
AI Code Review
Files Reviewed
custom_cards/interactive_creator.py(modified)Findings
Correctness
TODO/FIXMEcomments remain in the changed code.tweak_archetype(): usescopy.copy()on the archetype before mutating it, correctly preventing any modification of the shared archetype object. Field names (avg_vs_r,obp_vs_r,slg_vs_r,bb_pct_vs_r,k_pct_vs_r,avg_vs_l,obp_vs_l,slg_vs_l,bb_pct_vs_l,k_pct_vs_l,hr_per_hit,triple_per_hit,double_per_hit,gb_pct,fb_pct,ld_pct,speed_rating,steal_jump,xbt_pct) all match their definitions inBatterArchetypeandPitcherArchetype.tweak_archetype()correctly skips the baserunning section (guarded byif player_type == "batter"). For pitchers,speed_rating,steal_jump, andxbt_pctdo not exist onPitcherArchetype, so the guard is necessary and correct.tweak_archetype()matches whatreview_and_tweak()expects:{"ratings": ..., "baserunning": ...}for batters,{"ratings": ...}for pitchers.manual_adjustments(): usescopy.deepcopy()(appropriate sincecard_datacontains nested dicts), correctly modifies only the user-chosen split, and prints a warning if the total deviates from 108 by more than 0.01. The 22-fieldD20_FIELDSlist matches the exact set of D20 chance fields in bothBatterRatingCalculatorandPitcherRatingCalculator.ValueErroron non-numeric input, bounds checking on field index, rejection of negative values.Security
float/intand written back to an in-memory dict. No security issues.Style & Conventions
prompt_floatandprompt_intare defined as local nested functions insidetweak_archetype(). This is a reasonable choice for small helpers used only in that scope; no concern.display_ratings()(adding spaces around+in f-strings) are consistent with the rest of the file's style.# type: ignore[arg-type]and# type: ignore[union-attr]annotations on the baserunning prompts are appropriate since mypy cannot narrowarchtoBatterArchetypeafter the shallow copy — the guard ensures correctness at runtime.TODOincreate_pitching_card()(starter_rating,relief_rating,closer_ratinghardcoded to 5/5/None) is untouched by this PR and is out of scope.Suggestions
tweak_archetype()pitcher path does not exposehard_pct,med_pct,soft_pct,pull_pct,center_pct,oppo_pct,ifh_pct, orhr_fb_pctfor editing. These are present on both archetype types and influence hit/out distributions. A future improvement could expose these, but it is not a blocker for this PR's stated scope.tweak_archetype(), the returnedcard_datano longer references the modifiedarchobject (the archetype passed intoreview_and_tweak()is the original). This means a second call to option 2 starts from the original archetype again rather than the previously tweaked one. This is the expected behaviour given the docstring says "adjust key percentages", but worth noting for a future iteration if users want to stack tweaks.Verdict: APPROVED
Both stub functions are correctly implemented, field names are verified against their dataclass definitions, the mutation-safety pattern (
copy.copy/copy.deepcopy) is applied correctly, and all input paths are validated. The PR does exactly what its description claims. No blocking issues found.Automated review by Claude PR Reviewer
Approved. Solid implementations of both stubs —
tweak_archetype()correctly usescopy.copy()to avoid mutating the original, andmanual_adjustments()provides a clean interactive loop with the 108-total validation warning.9584daee57to962b9cf6f1