M3 기반: 공용 베팅 엔진, 카드 정의, 카드·칩 화면 컴포넌트, 돈 표현 금지 검사
- packages/engine/betting: 한국식(체크·삥·콜·따당·쿼터·하프·풀·다이, 라운드당 1회·첫 행동에서만 레이즈),
노리밋·팟리밋(최소 레이즈, 불완전 올인 레이즈는 재오픈 안 함, BB 옵션), 앤티·블라인드, 올인,
받지 않은 베팅 반환, 사이드 팟 분할, 동점 분배(홀수 칩 순서), 시간 초과 자동 행동
- 테스트 24개: 포커·섯다 계획서 계산 예시, 행동별 상태 전이, 사이드 팟 예시, 무작위 베팅 1,500판
(칩 총량 보존·라운드 종료·라운드 끝 금액 일치·팟 합계=판 전체)
- packages/engine/cards: 트럼프 52+조커, 화투 48(+보너스)·섯다 20장 정의와 테스트
- 웹: 직접 그린 SVG 트럼프·화투 카드, 칩 표시("1,250 칩"), 놀이용 칩 안내문
- apps/web/src/no-money-wording.test.ts: 베팅 게임 화면에 통화 기호·"원"·판돈·충전·환전 등 금지
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
39
apps/web/src/no-money-wording.test.ts
Normal file
39
apps/web/src/no-money-wording.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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', '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([]);
|
||||
});
|
||||
Reference in New Issue
Block a user