Files
joke-app/apps/web/src/no-money-wording.test.ts
EJClaw fa7ceea668 M7: 라스베가스 규칙·화면·테스트
- 규칙 모듈 packages/games/src/las-vegas: 지폐 54장 더미·카지노 채우기(합 5 이상), 굴리기/놓기(같은 눈 전부),
  중립(흰) 주사위 변형(auto/on/off, 3명은 라운드 시작 하우스 주사위 2개 자동 배치), 동점 무효 정산,
  하우스·남은 지폐 더미 아래로(카지노 1→6, 큰 금액부터), 라운드마다 선 이동, 장 수 동점 처리,
  시간 초과(굴리기 자동 / 개수→지폐→작은 수 순 자동 놓기)와 3회 연속 자리 비움, 기권,
  hideMoney 시 남의 금액은 장 수만 보이는 view, 놓기 미리 보기(1등/동점 주의) 계산
- 테스트: 문서 10절 시나리오 전부, 1,000시드 무작위 대국(타임아웃·기권 포함, 지폐·주사위 보존,
  정보 유출, onTimeout 합법, 결정성) + 엔진 randomPlayout 100시드
- 화면 apps/web/src/games/las-vegas: 카지노 6곳(PC 한 줄, 폰 3열×2행), 색깔 주사위 쌓기, 놀이용 지폐,
  굴림 애니메이션, 숫자별 묶음 버튼 → 카지노 강조와 예상 순위 → 확정, 라운드 정산(동점 무효) 표시,
  최종 결과표, 폰에서는 굴리기/놓기 영역이 화면 아래에 고정
- 등록: GAMES, 카탈로그, 화면 레지스트리, 아이콘, 놀이용 점수 문구 검사 대상에 추가
- E2E: PC 방장 + 폰 친구, 3라운드로 바꿔 결과까지 실제 플레이(키보드 선택, 카지노 카드 선택, 동점 무효 확인)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 14:53:47 +09:00

40 lines
1.6 KiB
TypeScript

/**
* Guard (docs/04 §6, reviewer request): betting-game UI text must never look like real money.
* Scans the betting games' screens and shared chip/card components for currency-like wording.
*/
import { expect, test } from 'bun:test';
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
import { join } from 'node:path';
const ROOTS = ['games/poker', 'games/seotda', 'games/gostop', 'games/marble', 'games/las-vegas', 'components/cards'].map((p) => join(import.meta.dir, p));
const FORBIDDEN: [RegExp, string][] = [
[/₩|\$\s?\d|\d\s?\$/, '통화 기호'],
[/\d[\d,]*\s*(만\s*)?원(?![가-힣])/, '"원" 단위'],
[/판돈|돈을|돈이|돈은|현금|머니|골드/, '돈을 떠올리는 낱말'],
[/충전|환전|입금|출금|송금|결제|구매/, '거래 낱말'],
];
function files(dir: string): string[] {
if (!existsSync(dir)) return [];
return readdirSync(dir).flatMap((f) => {
const p = join(dir, f);
return statSync(p).isDirectory() ? files(p) : /\.(tsx?|css)$/.test(f) ? [p] : [];
});
}
/** Only string literals / JSX text matter; comments are ignored. */
function visibleText(src: string): string {
return src.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:])\/\/.*$/gm, '$1');
}
test('no currency-like wording in betting game UI', () => {
const problems: string[] = [];
for (const f of ROOTS.flatMap(files)) {
const text = visibleText(readFileSync(f, 'utf8'));
text.split('\n').forEach((line, i) => {
for (const [re, why] of FORBIDDEN) if (re.test(line)) problems.push(`${f.split('/src/')[1]}:${i + 1} ${why}: ${line.trim().slice(0, 80)}`);
});
}
expect(problems).toEqual([]);
});