- MagicalObject: internal #health and #maxHealth now Health type - HealingObject: constructor, create, and heal use Health value object - MagicalWeapon: constructor, create, and use use Health value object - DamageDealer and Healer interfaces: health: number -> health: Health - magical-objects.spec.ts: all assertions use .health.value - Run npm run checks: 0 errors, 70 tests passing
100 lines
4.5 KiB
Markdown
100 lines
4.5 KiB
Markdown
# RPG Combat
|
|
|
|
> A challenge set by [Emily Bache](https://github.com/emilybache). The kata was invented by Daniel Ojeda Loisel and the description is adapted from Steve Smith's version.
|
|
|
|
## What This Is
|
|
|
|
RPG Combat is a small rules engine for a tabletop-style RPG. Characters fight, level up, join factions, and wield magical objects — all governed by a precise set of business rules. The challenge is to implement those rules correctly, and this project takes a different path than most: instead of writing code first and tests later, we start with **formal specifications** and let properties drive every line of implementation.
|
|
|
|
## Original Source
|
|
|
|
The user stories and rules come from [this kata description](https://www.sammancoaching.org/kata_descriptions/rpg_combat.html), originally created by Daniel Ojeda Loisel and adapted from Steve Smith's version.
|
|
|
|
```
|
|
user-stories.md ← the requirements (what the system should do)
|
|
.specs/ ← Allium formal specifications (the formal model)
|
|
src/ ← TypeScript implementation (ADTs, value objects, immutability)
|
|
*.spec.ts ← fast-check property tests (executable verification)
|
|
```
|
|
|
|
## How We Work
|
|
|
|
The workflow follows three intertwined practices:
|
|
|
|
### 1. Spec-First with Allium
|
|
|
|
Before writing any code, requirements are formalized into [Allium](https://github.com/juxt/allium) — a logic-based specification language. Each user story becomes a `.allium` file with invariants (always-true properties) and rules (state transitions). This is the source of truth.
|
|
|
|
### 2. Property-Based Testing with fast-check
|
|
|
|
Allium invariants are translated into fast-check properties. Instead of hand-crafting individual test cases, we define **properties** that must hold for thousands of random inputs:
|
|
|
|
```typescript
|
|
// "Health is never negative" — verified across 1000 random damage values
|
|
fc.property(fc.integer({ min: 0, max: 10000 }), (damage) => {
|
|
const c = new Character({ name: 'goblin', health: 1000 });
|
|
c.takeDamage(damage);
|
|
return c.health >= 0;
|
|
});
|
|
```
|
|
|
|
### 3. "I Can't Believe It's Not Haskell"
|
|
|
|
The TypeScript implementation embraces functional patterns:
|
|
|
|
- **ADTs** (algebraic data types) via discriminated unions for states
|
|
- **Value objects** (Health, Level, Status) with invariants enforced at construction
|
|
- **Immutability** — no mutation, pure functions, new instances returned
|
|
- **YAGNI discipline** — write only what a failing property demands
|
|
|
|
## What We've Done
|
|
|
|
All five user stories are implemented and verified:
|
|
|
|
| Story | Topic | Status |
|
|
| ----- | --------------------------- | ------- |
|
|
| 1 | Character Creation & Damage | ✅ Done |
|
|
| 2 | Levels | ✅ Done |
|
|
| 3 | Factions | ✅ Done |
|
|
| 4 | Magical Objects | ✅ Done |
|
|
| 5 | Changing Level | ✅ Done |
|
|
|
|
**70 tests passing** across 6 spec files.
|
|
|
|
## The Journey: Skills & Tools
|
|
|
|
The real value of this project isn't the code — it's the process. Here's what was built along the way:
|
|
|
|
### Built-in Allium Skills
|
|
|
|
Six Allium skills guide the workflow from requirements to verified code:
|
|
|
|
- **`/skill:elicit`** — explore and clarify requirements with stakeholders
|
|
- **`/skill:distill`** — extract specifications from existing code
|
|
- **`/skill:propagate`** — generate test obligations from specs
|
|
- **`/skill:tend`** — evolve specs as understanding deepens
|
|
- **`/skill:weed`** — check spec-code alignment
|
|
- **`/skill:user-story-conversation`** — Card, Conversation, Confirmation workflow with Example Mapping, Allium specs, and fast-check properties
|
|
|
|
### Custom Extensions
|
|
|
|
Two custom extensions were developed for this project:
|
|
|
|
- **`clear-export`** — exports the current pi session to an HTML transcript in `transcripts/` and starts a fresh session. This creates a permanent record of each decision, iteration, and insight.
|
|
- **`problem-breakdown`** — breaks problems into small, independently executable steps using the `yx` CLI. Each step becomes a "yak" with full execution context, enabling parallel or sequential execution by any agent.
|
|
|
|
### The Transcript Archive
|
|
|
|
Every session is exported as an HTML transcript in the `transcripts/` directory — over 20 sessions documenting the full journey from first requirements review through horizontal refactoring. These are the project's most valuable artifacts.
|
|
|
|
## Build & Test
|
|
|
|
```bash
|
|
npm install
|
|
npm test # 70 properties across 6 spec files
|
|
npm run lint:fix
|
|
npm run format:fix
|
|
npm run typecheck
|
|
npm run checks # pre-commit gate: format + lint + typecheck + test
|
|
```
|