M9: 게임 26종 30초 요약 카드·[?] 지금 할 수 있는 것 내용
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
65
apps/web/src/games/davinci-code/help.ts
Normal file
65
apps/web/src/games/davinci-code/help.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
/** 다빈치 코드 [?] "지금 할 수 있는 것" (docs/07 §5). Pure: only reads the client view. */
|
||||
import type { DavinciView, TileValue } from '@bg/games/davinci-code';
|
||||
import type { PublicUser } from '@bg/shared/lite';
|
||||
|
||||
const COLOR = { black: '검정', white: '흰색' } as const;
|
||||
const nm = (players: Record<string, PublicUser>, id: string | null | undefined) => `${(id && players[id]?.nickname) || '다른 사람'}님`;
|
||||
const valueKo = (n: TileValue | undefined) => (n === 'joker' ? '조커' : n === undefined ? '' : String(n));
|
||||
const hidden = (v: DavinciView, p: string) => (v.rows[p] ?? []).filter((t) => !t.revealed).length;
|
||||
|
||||
export function helpNow(v: DavinciView, me: string | null, players: Record<string, PublicUser>): string {
|
||||
const goal = '상대 타일의 숫자를 맞혀 모두 드러내면 그 상대는 탈락해요. 마지막까지 숨은 타일이 남은 사람이 이겨요.';
|
||||
if (v.phase === 'finished') return `게임이 끝났어요. ${v.final?.summary ?? ''}`.trim() + '\n결과 창에서 [한 판 더]를 누르면 다시 할 수 있어요.';
|
||||
|
||||
const seated = !!me && v.seats.includes(me);
|
||||
const alive = seated && !!v.alive[me!];
|
||||
const cur = v.current;
|
||||
const curNick = nm(players, cur);
|
||||
|
||||
if (v.phase === 'chooseStart') {
|
||||
const done = v.picked ? Object.values(v.picked).filter(Boolean).length : 0;
|
||||
const total = v.seats.filter((p) => v.alive[p]).length;
|
||||
if (!alive) return `모두 시작 타일 ${v.startCount}개의 색을 고르는 중이에요(${done}/${total}).\n${goal}`;
|
||||
if (v.myPick) return `검정 ${v.myPick.black}개, 흰색 ${v.myPick.white}개를 골랐어요. 다른 사람을 기다리는 중이에요(${done}/${total}).`;
|
||||
return `시작 타일 ${v.startCount}개를 검정과 흰색 몇 개씩 받을지 골라 [이렇게 받기]를 눌러요.\n숫자는 고른 색 안에서 무작위로 정해지고, 내 줄에 작은 수부터 자동으로 놓여요.`;
|
||||
}
|
||||
|
||||
if (!alive) {
|
||||
const head = seated ? '탈락했어요. 끝까지 구경할 수 있어요.\n' : '';
|
||||
return `${head}${curNick} 차례예요. ${goal}`;
|
||||
}
|
||||
|
||||
if (cur !== me) {
|
||||
const doing: Record<string, string> = {
|
||||
draw: '타일을 가져오는 중이에요',
|
||||
guess: '추리하는 중이에요',
|
||||
afterCorrect: '맞혀서 계속할지 고르는 중이에요',
|
||||
placeJoker: '조커를 넣을 자리를 고르는 중이에요',
|
||||
revealOwn: '틀려서 자기 타일을 공개하는 중이에요',
|
||||
};
|
||||
return `${curNick}이 ${doing[v.phase] ?? '차례를 진행하는 중이에요'}. 기다려 주세요.\n내 숨은 타일은 ${hidden(v, me!)}개 남았어요. 공개된 숫자와 위치, 추리 기록을 보며 상대 숫자를 짐작해 보세요.`;
|
||||
}
|
||||
|
||||
const pend = v.pending?.n !== undefined ? `가져온 타일(${COLOR[v.pending.color]} ${valueKo(v.pending.n)})은 나만 볼 수 있어요. ` : '';
|
||||
switch (v.phase) {
|
||||
case 'draw':
|
||||
return v.options.chooseColors
|
||||
? '내 차례예요. 가운데 검정·흰색 더미 중 원하는 색을 눌러 타일을 1개 가져와요.\n가져온 타일의 숫자는 나만 봐요.'
|
||||
: '내 차례예요. 가운데 [타일 가져오기]를 눌러 타일을 1개 가져와요.\n가져온 타일의 숫자는 나만 봐요.';
|
||||
case 'guess': {
|
||||
const penalty = v.pending ? '가져온 타일이 모두에게 공개돼요' : v.options.emptyPoolPenalty === 'reveal-own' ? '내 숨은 타일 하나를 공개해야 해요' : '차례가 넘어가요';
|
||||
const dim = v.options.deductionHelper ? '숫자판에서 흐린 숫자는 이미 보이는 숫자이거나 공개된 정보로 보면 어려운 숫자예요.' : '숫자판에서 흐린 숫자는 이미 다른 곳에 보이는 숫자예요.';
|
||||
return `${pend}상대 줄에서 파란 테두리가 있는 숨은 타일을 누르고, 숫자를 골라 [이 숫자!] 버튼을 눌러요.\n타일은 작은 수부터 놓여 있으니 옆에 공개된 숫자가 힌트예요. ${dim}\n틀리면 ${penalty}.`;
|
||||
}
|
||||
case 'afterCorrect':
|
||||
return v.pending
|
||||
? '맞혔어요! [한 번 더 맞히기]로 계속하거나 [여기서 멈추기]를 눌러요.\n멈추면 가져온 타일이 숨긴 채 내 줄에 들어가요. 계속하다 틀리면 그 타일이 공개돼요.'
|
||||
: `맞혔어요! [한 번 더 맞히기]로 계속하거나 [여기서 멈추기]를 눌러요.\n더미가 비어서 ${v.options.emptyPoolPenalty === 'reveal-own' ? '계속하다 틀리면 내 숨은 타일 하나를 공개해야 해요' : '계속하다 틀려도 벌칙 없이 차례만 넘어가요'}.`;
|
||||
case 'placeJoker':
|
||||
return '조커를 넣을 자리를 골라요. 내 줄의 [여기] 버튼 중 하나를 누르세요.\n조커는 어느 틈에나 넣을 수 있고, 한 번 놓으면 옮길 수 없어요.';
|
||||
case 'revealOwn':
|
||||
return '틀렸어요. 더미가 비어서 내 숨은 타일 하나를 공개해야 해요.\n내 줄에서 파란 테두리 타일 중 하나를 누르세요.';
|
||||
default:
|
||||
return `내 차례예요. ${goal}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user