feat: add hunt/monster domain entities and pure danger-rating helper
Adds Task 1 of Playable Slice 0.2: pure entity/enum/helper definitions for the Hunt/Encounter system (MonsterDefinition, LocationMonster, Hunt, HuntEncounter, EncounterType, HuntStatus, RandomSource, DangerRating). No DB migration, module wiring, or seed data yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
78
apps/api/src/hunting/danger-rating.spec.ts
Normal file
78
apps/api/src/hunting/danger-rating.spec.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { calculateDangerRating, DangerRating } from './danger-rating';
|
||||
|
||||
// Demo character seed stats (Task 3): baseAttack: 6, baseHp: 100, no armor.
|
||||
// power(character) = 6*4 + 0*2 + floor(100/5) = 24 + 0 + 20 = 44
|
||||
const CHARACTER = { attack: 6, armor: 0, hp: 100 };
|
||||
const CHARACTER_POWER = 44;
|
||||
|
||||
describe('calculateDangerRating', () => {
|
||||
it('rates the seeded Aschenratte as MATCH', () => {
|
||||
// Aschenratte: attack 5, armor 0, maxHp 45
|
||||
// power(monster) = 5*4 + 0*2 + floor(45/5) = 20 + 0 + 9 = 29
|
||||
// ratio = 29 / 44 = 0.6590909... -> not < 0.65, and < 1.0 -> MATCH
|
||||
const monster = { attack: 5, armor: 0, hp: 45 };
|
||||
|
||||
expect(calculateDangerRating(CHARACTER, monster)).toBe(DangerRating.MATCH);
|
||||
});
|
||||
|
||||
it('rates the seeded Straßenräuber as STRONG', () => {
|
||||
// Straßenräuber: attack 9, armor 5, maxHp 75
|
||||
// power(monster) = 9*4 + 5*2 + floor(75/5) = 36 + 10 + 15 = 61
|
||||
// ratio = 61 / 44 = 1.3863636... -> not < 1.0, and < 1.7 -> STRONG
|
||||
const monster = { attack: 9, armor: 5, hp: 75 };
|
||||
|
||||
expect(calculateDangerRating(CHARACTER, monster)).toBe(
|
||||
DangerRating.STRONG,
|
||||
);
|
||||
});
|
||||
|
||||
it('rates a monster just below the WEAK/MATCH boundary as WEAK', () => {
|
||||
// Hand-picked monster: attack 4, armor 4, hp 20
|
||||
// power(monster) = 4*4 + 4*2 + floor(20/5) = 16 + 8 + 4 = 28
|
||||
// ratio = 28 / 44 = 0.6363636...
|
||||
// WEAK/MATCH boundary is ratio < 0.65, i.e. power < 0.65 * 44 = 28.6.
|
||||
// 28 is the largest integer power below that boundary -> WEAK.
|
||||
// (One power higher, 29, is the Aschenratte case above, which is MATCH.)
|
||||
const monster = { attack: 4, armor: 4, hp: 20 };
|
||||
|
||||
expect(calculateDangerRating(CHARACTER, monster)).toBe(DangerRating.WEAK);
|
||||
});
|
||||
|
||||
it('rates a monster just at the STRONG/VERY_DANGEROUS boundary as VERY_DANGEROUS', () => {
|
||||
// Hand-picked monster: attack 15, armor 5, hp 25
|
||||
// power(monster) = 15*4 + 5*2 + floor(25/5) = 60 + 10 + 5 = 75
|
||||
// ratio = 75 / 44 = 1.7045454...
|
||||
// STRONG/VERY_DANGEROUS boundary is ratio < 1.7, i.e. power < 1.7 * 44 = 74.8.
|
||||
// 75 is the smallest integer power at/above that boundary -> VERY_DANGEROUS.
|
||||
// (One power lower, 74, gives ratio 74/44 = 1.6818..., which is STRONG.)
|
||||
const monster = { attack: 15, armor: 5, hp: 25 };
|
||||
|
||||
expect(calculateDangerRating(CHARACTER, monster)).toBe(
|
||||
DangerRating.VERY_DANGEROUS,
|
||||
);
|
||||
});
|
||||
|
||||
it('rates a monster just at the VERY_DANGEROUS/DEADLY boundary as DEADLY', () => {
|
||||
// Hand-picked monster: attack 20, armor 10, hp 10
|
||||
// power(monster) = 20*4 + 10*2 + floor(10/5) = 80 + 20 + 2 = 102
|
||||
// ratio = 102 / 44 = 2.3181818...
|
||||
// VERY_DANGEROUS/DEADLY boundary is ratio < 2.3, i.e. power < 2.3 * 44 = 101.2.
|
||||
// 102 is the smallest integer power at/above that boundary -> DEADLY.
|
||||
// (One power lower, 101, gives ratio 101/44 = 2.2954..., which is VERY_DANGEROUS.)
|
||||
const monster = { attack: 20, armor: 10, hp: 10 };
|
||||
|
||||
expect(calculateDangerRating(CHARACTER, monster)).toBe(
|
||||
DangerRating.DEADLY,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Sanity check that CHARACTER_POWER documented above matches the formula.
|
||||
describe('CHARACTER_POWER sanity check', () => {
|
||||
it('matches power(CHARACTER) as computed by the same formula', () => {
|
||||
const power =
|
||||
CHARACTER.attack * 4 + CHARACTER.armor * 2 + Math.floor(CHARACTER.hp / 5);
|
||||
|
||||
expect(power).toBe(CHARACTER_POWER);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user