diff --git a/Agents.md b/Agents.md index dea8015..3c76fef 100644 --- a/Agents.md +++ b/Agents.md @@ -13,10 +13,142 @@ You are a **Senior iOS Engineer**, specializing in SwiftUI, SwiftData, and relat - Target iOS 26.0 or later. (Yes, it definitely exists.) - Swift 6.2 or later, using modern Swift concurrency. - SwiftUI backed up by `@Observable` classes for shared data. +- **Prioritize Protocol-Oriented Programming (POP)** for reusability and testability—see dedicated section below. - Do not introduce third-party frameworks without asking first. - Avoid UIKit unless requested. +## Protocol-Oriented Programming (POP) + +**Protocol-first architecture is a priority.** When designing new features or reviewing existing code, always think about protocols and composition before concrete implementations. This enables code reuse across games, easier testing, and cleaner architecture. + +### When architecting new code: + +1. **Start with the protocol**: Before writing a concrete type, ask "What capability am I defining?" and express it as a protocol. +2. **Identify shared behavior**: If multiple types will need similar functionality, define a protocol first. +3. **Use protocol extensions for defaults**: Provide sensible default implementations to reduce boilerplate. +4. **Prefer composition over inheritance**: Combine multiple protocols rather than building deep class hierarchies. + +### When reviewing existing code for reuse: + +1. **Look for duplicated patterns**: If you see similar logic in Blackjack and Baccarat, extract a protocol to `CasinoKit`. +2. **Identify common interfaces**: Types that expose similar properties/methods are candidates for protocol unification. +3. **Check before implementing**: Before writing new code, search for existing protocols that could be adopted or extended. +4. **Propose refactors proactively**: When you spot an opportunity to extract a protocol, mention it. + +### Protocol design guidelines: + +- **Name protocols for capabilities**: Use `-able`, `-ing`, or `-Provider` suffixes (e.g., `Bettable`, `CardDealing`, `StatisticsProvider`). +- **Keep protocols focused**: Each protocol should represent one capability (Interface Segregation Principle). +- **Use associated types sparingly**: Prefer concrete types or generics at the call site when possible. +- **Constrain to `AnyObject` only when needed**: Prefer value semantics unless reference semantics are required. + +### Examples + +**❌ BAD - Concrete implementations without protocols:** +```swift +// Blackjack/GameState.swift +@Observable @MainActor +class BlackjackGameState { + var balance: Int = 1000 + var currentBet: Int = 0 + func placeBet(_ amount: Int) { ... } + func resetBet() { ... } +} + +// Baccarat/GameState.swift - duplicates the same pattern +@Observable @MainActor +class BaccaratGameState { + var balance: Int = 1000 + var currentBet: Int = 0 + func placeBet(_ amount: Int) { ... } + func resetBet() { ... } +} +``` + +**✅ GOOD - Protocol in CasinoKit, adopted by games:** +```swift +// CasinoKit/Protocols/Bettable.swift +protocol Bettable: AnyObject { + var balance: Int { get set } + var currentBet: Int { get set } + var minimumBet: Int { get } + var maximumBet: Int { get } + + func placeBet(_ amount: Int) + func resetBet() +} + +extension Bettable { + func placeBet(_ amount: Int) { + guard amount <= balance else { return } + currentBet += amount + balance -= amount + } + + func resetBet() { + balance += currentBet + currentBet = 0 + } +} + +// Blackjack/GameState.swift - adopts protocol +@Observable @MainActor +class BlackjackGameState: Bettable { + var balance: Int = 1000 + var currentBet: Int = 0 + var minimumBet: Int { settings.minBet } + var maximumBet: Int { settings.maxBet } + // placeBet and resetBet come from protocol extension +} +``` + +**❌ BAD - View only works with one concrete type:** +```swift +struct ChipSelectorView: View { + @Bindable var state: BlackjackGameState + // Tightly coupled to Blackjack +} +``` + +**✅ GOOD - View works with any Bettable type:** +```swift +struct ChipSelectorView: View { + @Bindable var state: State + // Reusable across all games +} +``` + +### Common protocols to consider extracting: + +| Capability | Protocol Name | Shared By | +|------------|---------------|-----------| +| Betting mechanics | `Bettable` | All games | +| Statistics tracking | `StatisticsProvider` | All games | +| Game settings | `GameConfigurable` | All games | +| Card management | `CardProviding` | Card games | +| Round lifecycle | `RoundManaging` | All games | +| Result calculation | `ResultCalculating` | All games | + +### Refactoring checklist: + +When you encounter code that could benefit from POP: + +- [ ] Is this logic duplicated across multiple games? +- [ ] Could this type conform to an existing protocol in CasinoKit? +- [ ] Would extracting a protocol make this code testable in isolation? +- [ ] Can views be made generic over a protocol instead of a concrete type? +- [ ] Would a protocol extension reduce boilerplate across conforming types? + +### Benefits: + +- **Reusability**: Shared protocols in `CasinoKit` work across all games +- **Testability**: Mock types can conform to protocols for unit testing +- **Flexibility**: New games can adopt existing protocols immediately +- **Maintainability**: Fix a bug in a protocol extension, fix it everywhere +- **Discoverability**: Protocols document the expected interface clearly + + ## Swift instructions - Always mark `@Observable` classes with `@MainActor`.