Skip to content

Commit a5aa8d8

Browse files
feat: implement PriceCalculator rule for Epic 5.4
Implements the PriceCalculator rule that calculates item prices based on base price, quality multipliers, and supply/demand modifiers. Formula: Final Price = Base Price × Quality Multiplier × Supply/Demand Multiplier Quality multipliers match GDD crafting system: - Crude (0-20): 0.5x - Common (21-40): 1.0x - Quality (41-60): 1.5x - Fine (61-80): 2.0x - Superior (81-99): 3.0x - Masterwork (100+): 5.0x Supply/demand modifiers support basic market conditions: - Abundant: 0.7x - Normal: 1.0x - Scarce: 1.5x - VeryRare: 2.0x Implementation follows established patterns (XpCalculator, CraftingOutcomeRule) with static class and pure functions. Uses Currency value object for type safety. Includes comprehensive test suite with 22 tests covering quality tiers, supply/demand scenarios, combined modifiers, edge cases, and rounding behavior. Closes #43
1 parent b0e7df2 commit a5aa8d8

6 files changed

Lines changed: 1276 additions & 4 deletions

File tree

PR-PROMPT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
> - Never use `--admin` to force the merge of a PR
77
> - **Always verify GitHub account before PR operations** - Contributor opens PRs, Maintainer - CLAUDE.local.md for account details
88
> - All changes need to be done via feature branch and merged via PR
9+
> - If ALL PR's and ISSUES are closed stop with the message "*!ALL WORK DONE!*"
910
</EXTREMELY-IMPORTANT>
1011
1112
As a `Maintainer`/Team Lead monitor for open PRs and perform autonomous code reviews according to repo process - particularly around GH and GIT account usage (see CLAUDE.local.md). If fixes are required use git worktrees. PR should only be merged using automatically and after passing all CI checks.

PROMPT.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66
> - Read CLAUDE.md and AGENTS.md before starting
77
> - Never use `--no-verify` to bypass git-hook verifications - always fix the root cause
88
> - Never use `--admin` to force the merge of a PR
9-
> - **MANDATORY: Verify BOTH git config AND GitHub CLI account before ANY PR operations**
9+
> - **MANDATORY: Verify BOTH git config AND GitHub CLI account before ANY PR operations using `CLAUDE.local.md`**
1010
> - Run `git config user.email` AND `gh auth status` to verify correct account
11-
> - Contributor (martincjarvis) opens PRs and commits code
12-
> - Maintainer (mcj-coder) reviews and merges PRs
11+
> - Contributor opens PRs and commits code
12+
> - Maintainer reviews and merges PRs
1313
> - **CRITICAL: Use `gh auth switch --user <username>` to change accounts** - NEVER use `gh auth login` (requires manual interaction)
1414
> - See CLAUDE.local.md "CRITICAL: Verify Both Git and GitHub CLI Credentials" section
1515
> - All changes need to be done via feature branch and merged via PR
1616
> - Work as `Contributor` implementing code changes and raising PR's. Do not review PR's unless explicitly prompted by the user - just monitor active PR and Issues for comment feedback.
17+
> - If PR is opened with `Maintainer` GH credentials, close PR and re-create with `Contributor` PR
1718
> - **BLOCKING: Active PR Polling Loop - DO NOT STOP**
18-
> - **MUST run at the start of EVERY iteration**: `gh pr list --author martincjarvis --state open`
19+
> - **MUST run at the start of EVERY iteration**: `gh pr list --state open`
1920
> - **IF open PRs exist**: Execute polling loop (DO NOT proceed to new tasks)
2021
> - **Polling loop actions** (repeat until PR is merged/closed):
2122
> 1. Check PR status: `gh pr view <PR#> --json state,reviewDecision,mergedAt,comments`
@@ -27,6 +28,7 @@
2728
> 7. IF merged/closed: Proceed to next task
2829
> - **DO NOT exit polling loop** until PR is merged or closed
2930
> - **DO NOT assume** Maintainer will notify you - actively poll for updates
31+
> - If ALL PR's and ISSUES are closed stop with the message "*!ALL WORK DONE!*"
3032
3133
</EXTREMELY-IMPORTANT>
3234

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
# PriceCalculator Rule Design
2+
3+
> **Context:** Epic 5.4 - Issue #43
4+
> **Purpose:** Calculate item prices based on quality and basic market conditions
5+
6+
## Overview
7+
8+
PriceCalculator is a stateless rule that calculates item prices using:
9+
10+
1. Base price (from item/recipe definition)
11+
2. Quality multiplier (matching GDD value tiers)
12+
3. Basic supply/demand modifier (settlement-level)
13+
14+
**Formula:**
15+
16+
```
17+
Final Price = Base Price × Quality Multiplier × Supply/Demand Multiplier
18+
```
19+
20+
## Design Decisions
21+
22+
| Decision | Choice | Rationale |
23+
| ------------- | ---------------------------- | ---------------------------------------------------------- |
24+
| Pattern | Static class, pure functions | Matches existing rules (XpCalculator, CraftingOutcomeRule) |
25+
| Quality tiers | 6 tiers from GDD | Proven design from gdd-crafting.md |
26+
| Supply/demand | Simple enum-based | Phase 1 requirement: "basic" |
27+
| Return type | Currency value object | Type safety, matches existing patterns |
28+
| Rounding | Math.Round() | Standard rounding for copper amounts |
29+
30+
## Quality Multipliers
31+
32+
Matching GDD crafting system quality tiers and value multipliers:
33+
34+
| Quality Tier | Quality Range | Price Multiplier | GDD Source |
35+
| ------------ | ------------- | ---------------- | ------------------------- |
36+
| Crude | 0-20 | 0.5x | gdd-crafting.md section 8 |
37+
| Common | 21-40 | 1.0x | gdd-crafting.md section 8 |
38+
| Quality | 41-60 | 1.5x | gdd-crafting.md section 8 |
39+
| Fine | 61-80 | 2.0x | gdd-crafting.md section 8 |
40+
| Superior | 81-99 | 3.0x | gdd-crafting.md section 8 |
41+
| Masterwork | 100+ | 5.0x | gdd-crafting.md section 8 |
42+
43+
**Implementation:**
44+
45+
```csharp
46+
private static decimal CalculateQualityMultiplier(int quality)
47+
{
48+
return quality switch
49+
{
50+
<= 20 => 0.5m,
51+
<= 40 => 1.0m,
52+
<= 60 => 1.5m,
53+
<= 80 => 2.0m,
54+
<= 99 => 3.0m,
55+
_ => 5.0m // 100+
56+
};
57+
}
58+
```
59+
60+
## Supply/Demand System (Basic)
61+
62+
**Phase 1 Implementation:** Simple enum-based modifier
63+
64+
| Level | Modifier | Use Case |
65+
| -------- | -------- | -------------------------------------- |
66+
| Abundant | 0.5x | Local overproduction, common materials |
67+
| Normal | 1.0x | Balanced market (default) |
68+
| Scarce | 1.5x | Limited supply, increased demand |
69+
| VeryRare | 2.0x | Crisis, trade route disruption |
70+
71+
**Phase 2 Expansion (Deferred):**
72+
73+
- Per-item inventory tracking
74+
- Dynamic calculation based on supply/demand curves
75+
- Trade route impacts
76+
- Event-driven scarcity
77+
- Regional specialization effects
78+
79+
## API Design
80+
81+
### Primary Method
82+
83+
```csharp
84+
public static Currency CalculatePrice(
85+
Currency basePrice,
86+
int quality,
87+
decimal supplyDemandModifier = 1.0m)
88+
```
89+
90+
**Parameters:**
91+
92+
- `basePrice`: Base currency value for the item (from recipe/definition)
93+
- `quality`: Item quality (0-100+, matches CraftingOutcomeResult.FinalQuality)
94+
- `supplyDemandModifier`: Settlement market modifier (0.5-2.0, default 1.0)
95+
96+
**Returns:** Final price as `Currency` value object
97+
98+
**Validation:**
99+
100+
- `basePrice`: Must be non-negative (Currency enforces this)
101+
- `quality`: Must be non-negative (negative quality = ArgumentOutOfRangeException)
102+
- `supplyDemandModifier`: Must be positive (zero/negative = ArgumentOutOfRangeException)
103+
104+
### Helper Methods
105+
106+
```csharp
107+
private static decimal CalculateQualityMultiplier(int quality)
108+
```
109+
110+
Maps quality value to price multiplier using tier thresholds.
111+
112+
## Architecture
113+
114+
**Location:** `src/FantasyRpgWorld.Core/Rules/PriceCalculator.cs`
115+
116+
**Pattern:** Static class with pure functions (stateless)
117+
118+
**Dependencies:**
119+
120+
- `FantasyRpgWorld.Core.Domain.ValueObjects.Currency`
121+
122+
**Used By (Future):**
123+
124+
- `TradeAction` (Issue #44) - NPC trading
125+
- `EconomySystem` (Issue #45) - Trade processing
126+
- Commission pricing
127+
- Shop inventory generation
128+
129+
## Test Coverage
130+
131+
**File:** `tests/FantasyRpgWorld.Core.Tests/Rules/PriceCalculatorTests.cs`
132+
133+
**Test Cases:**
134+
135+
1. **Quality Multipliers** (6 tests)
136+
- Crude quality (quality=10) → 0.5x
137+
- Common quality (quality=30) → 1.0x
138+
- Quality tier (quality=50) → 1.5x
139+
- Fine quality (quality=70) → 2.0x
140+
- Superior quality (quality=90) → 3.0x
141+
- Masterwork quality (quality=100) → 5.0x
142+
143+
2. **Supply/Demand Modifiers** (4 tests)
144+
- Abundant (0.5x)
145+
- Normal (1.0x)
146+
- Scarce (1.5x)
147+
- Very Rare (2.0x)
148+
149+
3. **Combined Modifiers** (2 tests)
150+
- High quality + scarce supply
151+
- Low quality + abundant supply
152+
153+
4. **Edge Cases** (5 tests)
154+
- Quality = 0 (minimum)
155+
- Quality > 100 (masterwork tier)
156+
- Zero base price
157+
- Negative quality (throws exception)
158+
- Negative/zero supply modifier (throws exception)
159+
160+
5. **Rounding Behavior** (2 tests)
161+
- Fractional copper rounds correctly
162+
- Large values don't overflow
163+
164+
**Total Expected Tests:** ~19 tests
165+
166+
## Example Calculations
167+
168+
**Example 1: Common Iron Sword**
169+
170+
- Base Price: 50 copper
171+
- Quality: 35 (Common)
172+
- Supply/Demand: 1.0 (Normal)
173+
- Final Price: 50 × 1.0 × 1.0 = **50 copper**
174+
175+
**Example 2: Fine Steel Sword (Scarce Market)**
176+
177+
- Base Price: 50 copper
178+
- Quality: 75 (Fine)
179+
- Supply/Demand: 1.5 (Scarce)
180+
- Final Price: 50 × 2.0 × 1.5 = **150 copper**
181+
182+
**Example 3: Masterwork Mithril Blade (Abundant Market)**
183+
184+
- Base Price: 500 copper (5 silver)
185+
- Quality: 105 (Masterwork)
186+
- Supply/Demand: 0.5 (Abundant)
187+
- Final Price: 500 × 5.0 × 0.5 = **1250 copper (12 silver, 50 copper)**
188+
189+
## Verification
190+
191+
Issue #43 requirements:
192+
193+
**Base price × quality modifier** - Implemented with 6-tier quality multiplier system
194+
**Settlement supply/demand (basic)** - Implemented with 4-level enum-based modifier
195+
**Prices vary by quality** - Prices range from 0.5x to 5.0x based on quality tier
196+
197+
## Future Enhancements (Phase 2)
198+
199+
1. **Dynamic Supply/Demand**
200+
- Track settlement inventory levels per item type
201+
- Calculate modifier from supply/demand curves
202+
- Factor in production vs consumption rates
203+
204+
2. **Market Complexity**
205+
- Regional price variations
206+
- Trade route status impacts
207+
- Event-driven price spikes
208+
- Merchant skill modifiers
209+
210+
3. **Advanced Pricing**
211+
- Item rarity modifiers
212+
- Material quality impact (beyond outcome quality)
213+
- Enchantment value calculations
214+
- Historical price tracking
215+
216+
4. **NPC Personality Effects**
217+
- Generous merchants → lower prices
218+
- Greedy merchants → higher prices
219+
- Relationship bonuses/penalties
220+
- Haggling skill impacts
221+
222+
## Implementation Notes
223+
224+
- Use `decimal` for intermediate calculations to avoid precision loss
225+
- Round final result to integer copper (Currency.FromCopper expects int)
226+
- Supply/demand modifier defaults to 1.0 for convenience
227+
- Quality validation matches existing quality system (0-100+ range)
228+
- Pattern consistency with XpCalculator (static class, pure functions)
229+
230+
## References
231+
232+
- GDD: `docs/design/gdd-crafting.md` (quality tiers, value multipliers)
233+
- GDD: `docs/design/gdd-economy.md` (dynamic pricing factors)
234+
- Existing: `ExpenseDefinition.CalculateCost` (quality-based calculation pattern)
235+
- Existing: `CraftingOutcomeResult.FinalQuality` (quality value source)

0 commit comments

Comments
 (0)