Skip to content

Add level to Player/PlayerConfig; tier utilities and lowerTierAdversary auto-detection (#21)#22

Merged
gwillish merged 2 commits intomainfrom
feature/21-player-level-tier
Apr 5, 2026
Merged

Add level to Player/PlayerConfig; tier utilities and lowerTierAdversary auto-detection (#21)#22
gwillish merged 2 commits intomainfrom
feature/21-player-level-tier

Conversation

@gwillish
Copy link
Copy Markdown
Owner

@gwillish gwillish commented Apr 5, 2026

Summary

  • Adds level: Int (default 1) to Player and PlayerConfig; backward-compatible with existing JSON via custom init(from:) that falls back to 1 when the key is absent.
  • Forwards level through Player.asConfig().
  • Adds DifficultyBudget.tier(forLevel:) — SRD tier mapping (L1→T1, L2–4→T2, L5–7→T3, L8–10→T4).
  • Adds DifficultyBudget.partyTier(levels:) — median level, rounded up at tier boundaries; empty input returns T1.
  • Extends suggestedAdjustments with optional adversaryTiers:[Int] and partyTier:Int?; auto-inserts .lowerTierAdversary when any adversary has a known tier strictly below party tier. All existing call sites are source-compatible (params default to []/nil).

Test plan

  • playerDefaultLevelIsOne — new Player has level == 1
  • playerLevelRoundTrip — level survives encode/decode
  • playerLevelDefaultsToOneWhenAbsentInJSON — legacy JSON (no level key) decodes to 1
  • asConfigPreservesAllFieldslevel forwarded by asConfig()
  • playerConfigCodableRoundTripPlayerConfig with level roundtrips
  • playerConfigLevelDefaultsToOneWhenAbsentInJSON — legacy PlayerConfig JSON decodes to 1
  • tierForLevel* — all four tier boundary tests
  • partyTier* — empty, single, odd count, even count with boundary rounding
  • lowerTierAdversary* — detected, not detected when tiers match, not detected without partyTier, tier-zero ignored
  • suggestedAdjustmentsBackwardCompatible — existing call sites still work

All 85 DHModelsTests pass. Closes #21.

gwillish added 2 commits April 5, 2026 16:13
…lowerTierAdversary

- Add `level: Int` (default 1) to `Player` and `PlayerConfig`, with
  custom `init(from:)` so existing JSON missing the field decodes to 1.
- Forward `level` through `Player.asConfig()`.
- Add `DifficultyBudget.tier(forLevel:)` per SRD tier mapping (L1→T1,
  L2–4→T2, L5–7→T3, L8–10→T4).
- Add `DifficultyBudget.partyTier(levels:)` using median level, rounded
  up at tier boundaries; empty input returns T1.
- Extend `suggestedAdjustments` with `adversaryTiers:[Int]` and
  `partyTier:Int?` params; auto-inserts `.lowerTierAdversary` when at
  least one adversary has a known tier strictly below the party tier.
- All existing call sites remain source-compatible (new params are
  defaulted to `[]` / `nil`).

Resolves #21.
@gwillish gwillish merged commit c616e73 into main Apr 5, 2026
@gwillish gwillish deleted the feature/21-player-level-tier branch April 5, 2026 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add level to Player/PlayerConfig; add tier utilities and auto-detect lowerTierAdversary in DifficultyBudget

1 participant