feat(phase2): 타로 셔플·reference 순수 유틸 + 테스트

Fisher-Yates 셔플, 카드 픽 생성, 참고 블록/메타데이터 빌더 구현.
Task 4(interpret API)·Task 6(UI)에서 소비됨.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 20:40:30 +09:00
parent 1752e68d55
commit 53e8b592f0
4 changed files with 95 additions and 0 deletions

29
lib/tarot/reference.ts Normal file
View File

@@ -0,0 +1,29 @@
import type { Pick } from './shuffle';
export function buildReferenceBlock(picks: Pick[]): string {
return picks
.map((p, i) => {
const c = p.card;
const dir = p.reversed ? '역방향' : '정방향';
const kws = (p.reversed ? c.reversedKeywords : c.keywords).join(', ');
const meaning = p.reversed ? c.meaningReversed : c.meaningUpright;
const arcana = c.arcana === 'major' ? `Major (${c.id})` : `Minor (${c.suit})`;
return [
`## ${i + 1}. 위치: ${p.position} | 카드: ${c.nameEn} (${dir})`,
`- 아르카나: ${arcana}`,
`- 원소: ${c.element}`,
`- ${dir} 키워드: ${kws}`,
`- ${dir} 의미: ${meaning}`,
].join('\n');
})
.join('\n\n');
}
export function buildContextMeta(picks: Pick[]) {
const major = picks.filter((p) => p.card.arcana === 'major').length;
const minor = picks.length - major;
const element_distribution: Record<string, number> = { air: 0, water: 0, fire: 0, earth: 0 };
for (const p of picks) element_distribution[p.card.element] += 1;
const orientation_flow = picks.map((p) => (p.reversed ? 'reversed' : 'upright')).join('→');
return { major_minor_ratio: `${major}:${minor}`, element_distribution, orientation_flow };
}

18
lib/tarot/shuffle.ts Normal file
View File

@@ -0,0 +1,18 @@
import type { TarotCard } from './cards';
export type Pick = { card: TarotCard; position: string; reversed: boolean };
export function fisherYates<T>(input: T[]): T[] {
const a = [...input];
for (let i = a.length - 1; i > 0; i -= 1) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
export function buildShuffle(deck: TarotCard[], size: number): (TarotCard & { reversed: boolean })[] {
return fisherYates(deck)
.slice(0, size)
.map((c) => ({ ...c, reversed: Math.random() < 0.5 }));
}