feat: add relationship classification helpers with tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-07 15:30:22 +00:00
parent 87ab1b4c10
commit cdfc35d5da
2 changed files with 141 additions and 0 deletions
@@ -0,0 +1,84 @@
import { describe, it, expect } from 'vitest';
import { classify, getPartnerCap, getFriendCap } from '../relationshipHelpers.js';
describe('relationshipHelpers', () => {
describe('classify', () => {
it('returns Partner for value >= 80', () => {
expect(classify(80)).toBe('Partner');
expect(classify(100)).toBe('Partner');
});
it('returns Close Friend for value 50-79', () => {
expect(classify(50)).toBe('Close Friend');
expect(classify(79)).toBe('Close Friend');
});
it('returns Friend for value 20-49', () => {
expect(classify(20)).toBe('Friend');
expect(classify(49)).toBe('Friend');
});
it('returns Acquaintance for value 5-19', () => {
expect(classify(5)).toBe('Acquaintance');
expect(classify(19)).toBe('Acquaintance');
});
it('returns Stranger for value -4 to 4', () => {
expect(classify(0)).toBe('Stranger');
expect(classify(4)).toBe('Stranger');
expect(classify(-4)).toBe('Stranger');
});
it('returns Wary for value -19 to -5', () => {
expect(classify(-5)).toBe('Wary');
expect(classify(-19)).toBe('Wary');
});
it('returns Rival for value -49 to -20', () => {
expect(classify(-20)).toBe('Rival');
expect(classify(-49)).toBe('Rival');
});
it('returns Enemy for value -79 to -50', () => {
expect(classify(-50)).toBe('Enemy');
expect(classify(-79)).toBe('Enemy');
});
it('returns Nemesis for value <= -80', () => {
expect(classify(-80)).toBe('Nemesis');
expect(classify(-100)).toBe('Nemesis');
});
});
describe('getPartnerCap', () => {
it('returns 1 for average stats', () => {
expect(getPartnerCap(10, 10)).toBe(1);
});
it('returns 2 for high sociability and empathy', () => {
expect(getPartnerCap(15, 14)).toBe(2);
});
it('returns 1 if only sociability is high', () => {
expect(getPartnerCap(15, 10)).toBe(1);
});
it('returns 1 if only empathy is high', () => {
expect(getPartnerCap(10, 14)).toBe(1);
});
});
describe('getFriendCap', () => {
it('returns baseCap for sociability 10', () => {
expect(getFriendCap(10)).toBe(5);
});
it('increases with high sociability', () => {
expect(getFriendCap(16)).toBe(8);
});
it('decreases with low sociability', () => {
expect(getFriendCap(6)).toBe(3);
});
});
});
+57
View File
@@ -0,0 +1,57 @@
import { relationshipConfig } from '../config/relationshipConfig.js';
export function classify(value: number): string {
for (const tier of relationshipConfig.tiers) {
if (value >= tier.min) return tier.label;
}
return 'Nemesis';
}
export function getPartnerCap(sociability: number, empathy: number): number {
if (
sociability >= relationshipConfig.polySociabilityThreshold &&
empathy >= relationshipConfig.polyEmpathyThreshold
) {
return relationshipConfig.polyPartnerCap;
}
return relationshipConfig.defaultPartnerCap;
}
export function getFriendCap(sociability: number): number {
return relationshipConfig.baseFriendCap +
Math.floor((sociability - 10) * relationshipConfig.friendCapPerSociability);
}
export function getEffectiveClassification(
entityId: number,
value: number,
allRelationships: Map<number, { value: number }>,
sociability: number,
empathy: number,
): string {
const raw = classify(value);
if (raw === 'Partner') {
const cap = getPartnerCap(sociability, empathy);
let partnerCount = 0;
for (const [otherId, rel] of allRelationships) {
if (otherId !== entityId && rel.value >= 80 && rel.value > value) {
partnerCount++;
}
}
if (partnerCount >= cap) return 'Close Friend';
}
if (raw === 'Friend') {
const cap = getFriendCap(sociability);
let friendCount = 0;
for (const [otherId, rel] of allRelationships) {
if (otherId !== entityId && rel.value >= 20 && rel.value < 80 && rel.value > value) {
friendCount++;
}
}
if (friendCount >= cap) return 'Acquaintance';
}
return raw;
}