Files
joke-app/apps/web/src/games/ninety-nine/help.test.ts
EJClaw 7da06d1b27 구십구: 코리아보드게임즈 공식 규칙으로 맞춤
- 공식 상품 페이지 규칙 설명 이미지 기준: 5장씩, 시작 카드(2명 40·3명 20·4명 이상 0),
  0 카드는 방향 유지/반대 선택, 칩(목숨)은 공식 규칙이 아니라 하우스 룰
- handSize 'auto'(전용 카드 5, 트럼프 3), startTotal 옵션, 카드 종류 'zero'와 dir 액션
- stateVersion 2, 진행 중인 v1 게임은 옛 규칙 그대로 이어서 하도록 migrate
- 특수 카드 장수(±9·±10·0)는 공개 자료가 없어 각 4장 유지

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 22:16:32 +09:00

53 lines
2.7 KiB
TypeScript

import { expect, test } from 'bun:test';
import { applyAction, setupGame, usersOf } from '../help-kit.test-util';
import { GAMES } from '@bg/games';
import { helpNow } from './help';
const users = usersOf(['A', 'B', 'C']);
const view = (s: unknown, who: string | null) => GAMES['ninety-nine']!.view(s, who) as any;
const nickOf = (id: string) => users[id]!.nickname;
test('구십구: 내 차례 / 상대 차례 / 관전', () => {
const { state } = setupGame('ninety-nine', 3);
const cur = state.current as string;
const other = ['A', 'B', 'C'].find((p) => p !== cur)!;
// 3 players: the start card says 20.
expect(helpNow(view(state, cur), cur, users)).toContain('내 차례예요(지금 합계 20)');
const t = helpNow(view(state, other), other, users);
expect(t).toContain(`${nickOf(cur)}님 차례예요(지금 합계 20)`);
expect(t).toContain('바로 탈락해요');
const spec = helpNow(view(state, null), null, users);
expect(spec).toContain(`${nickOf(cur)}님 차례예요`);
expect(spec).toContain('99를 넘게');
const card = view(state, cur).my.hand.find((c: any) => c.kind !== 'joker');
const s2 = applyAction('ninety-nine', state, cur, { type: 'play', cardId: card.id, ...(card.choices.length > 1 ? (card.choices[0].sign ? { sign: '+' } : card.choices[0].dir ? { dir: 'keep' } : { aceValue: 1 }) : {}) });
expect(helpNow(view(s2, s2.current), s2.current, users)).toContain(`지금 합계 ${s2.total}`);
});
test('구십구: 합계 95에서 낼 수 있는 카드만 밝게, ±카드 안내', () => {
const { state } = setupGame('ninety-nine', 3);
const cur = state.current as string;
state.total = 95;
state.hands[cur] = [
{ id: 'q1', kind: 'add', v: 8, face: { label: '8' } },
{ id: 'q2', kind: 'add', v: 7, face: { label: '7' } },
{ id: 'q3', kind: 'plusMinus', v: 9, face: { label: '±9' } },
];
const t = helpNow(view(state, cur), cur, users);
expect(t).toContain('밝게 보이는 카드만 낼 수 있어요(1장)');
expect(t).toContain('±카드는');
state.hands[cur].push({ id: 'q4', kind: 'zero', face: { label: '0' } });
expect(helpNow(view(state, cur), cur, users)).toContain('0 카드는 합계를 그대로 둬요. 누른 뒤 [방향 그대로]와 [방향 바꾸기] 중 하나를 골라요.');
});
test('구십구: 목숨 규칙과 탈락', () => {
const { state } = setupGame('ninety-nine', 3, { lives: 3 });
const cur = state.current as string;
const other = ['A', 'B', 'C'].find((p) => p !== cur)!;
const t = helpNow(view(state, other), other, users);
expect(t).toContain('목숨 1개를 잃고 합계 20부터 새 판을 해요');
expect(t).toContain('내 목숨은 3개예요');
state.alive[other] = false;
expect(helpNow(view(state, other), other, users)).toContain('탈락했어요');
});