M9: 게임 26종 30초 요약 카드·[?] 지금 할 수 있는 것 내용
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
77
apps/web/src/games/chess/quick.tsx
Normal file
77
apps/web/src/games/chess/quick.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
/** 체스 30초 규칙 요약 (docs/07 §5). */
|
||||
import type { QuickCard } from '../types';
|
||||
|
||||
const GLYPH: Record<string, string> = { k: '♚', q: '♛', r: '♜', b: '♝', n: '♞', p: '♟' };
|
||||
|
||||
/** One piece as a text glyph (filled shape, coloured by side). `︎` keeps ♟ from turning into an emoji. */
|
||||
function Piece({ t, x, y }: { t: string; x: number; y: number }) {
|
||||
const white = t === t.toUpperCase();
|
||||
return (
|
||||
<text
|
||||
x={x + 0.5}
|
||||
y={y + 0.56}
|
||||
fontSize={0.82}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="central"
|
||||
fontFamily="'Segoe UI Symbol', 'Noto Sans Symbols 2', 'DejaVu Sans', serif"
|
||||
fill={white ? '#fbfbf8' : '#1b1b1b'}
|
||||
stroke={white ? '#333' : '#000'}
|
||||
strokeWidth={0.03}
|
||||
>
|
||||
{GLYPH[t.toLowerCase()]}
|
||||
{'︎'}
|
||||
</text>
|
||||
);
|
||||
}
|
||||
|
||||
/** Small chess board; `rows` use FEN letters (uppercase = white) and '.' for empty. */
|
||||
function MiniBoard({ rows, dots = [], check, size = 150 }: { rows: string[]; dots?: [number, number][]; check?: [number, number]; size?: number }) {
|
||||
const h = rows.length;
|
||||
const w = rows[0]!.length;
|
||||
const scale = size / Math.max(w, h);
|
||||
return (
|
||||
<svg viewBox={`0 0 ${w} ${h}`} width={w * scale} height={h * scale} aria-hidden style={{ borderRadius: 6 }}>
|
||||
{rows.map((r, y) =>
|
||||
r.split('').map((_, x) => <rect key={`${x}-${y}`} x={x} y={y} width={1} height={1} fill={(x + y) % 2 === 0 ? '#f0d9b5' : '#b58863'} />),
|
||||
)}
|
||||
{check && <rect x={check[0]} y={check[1]} width={1} height={1} fill="#ef4444" opacity={0.7} />}
|
||||
{dots.map(([x, y]) => (
|
||||
<circle key={`d${x}-${y}`} cx={x + 0.5} cy={y + 0.5} r={0.16} fill="rgba(20,85,30,0.55)" />
|
||||
))}
|
||||
{rows.map((r, y) => r.split('').map((c, x) => (c === '.' ? null : <Piece key={`p${x}-${y}`} t={c} x={x} y={y} />)))}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const Arrow = () => (
|
||||
<span aria-hidden style={{ fontSize: 28, fontWeight: 800 }}>
|
||||
→
|
||||
</span>
|
||||
);
|
||||
|
||||
export const quickStart: QuickCard[] = [
|
||||
{
|
||||
pic: <MiniBoard rows={['R.k.', '.ppp']} check={[2, 0]} size={200} />,
|
||||
alt: '흰 룩이 검은 킹을 공격하고, 킹은 자기 폰에 막혀 피할 곳이 없어요',
|
||||
title: '킹을 꼼짝 못 하게',
|
||||
text: '백이 먼저 두고 번갈아 한 수씩 둬요. 상대 킹을 공격해서 피할 곳이 없게 만들면(체크메이트) 이겨요.',
|
||||
},
|
||||
{
|
||||
pic: <MiniBoard rows={['.....', '.....', '..N..', '.....', '.....']} dots={[[1, 0], [3, 0], [0, 1], [4, 1], [0, 3], [4, 3], [1, 4], [3, 4]]} size={150} />,
|
||||
alt: '가운데 나이트 둘레로 ㄱ자 모양 여덟 칸에 점이 찍혀 있어요',
|
||||
title: '기물마다 길이 달라요',
|
||||
text: '기물을 누르면 갈 수 있는 칸에 점이 찍혀요. 그 칸을 누르면 움직여요. 상대 기물이 있는 칸으로 가면 잡아요.',
|
||||
},
|
||||
{
|
||||
pic: (
|
||||
<>
|
||||
<MiniBoard rows={['.', 'P']} size={90} />
|
||||
<Arrow />
|
||||
<MiniBoard rows={['Q', '.']} size={90} />
|
||||
</>
|
||||
),
|
||||
alt: '흰 폰이 끝 줄에 닿아 퀸으로 바뀌어요',
|
||||
title: '폰이 끝까지 가면 변신',
|
||||
text: '폰이 끝 줄에 닿으면 퀸 같은 강한 기물로 바뀌어요(승진). 내 킹이 공격받으면(체크) 꼭 먼저 피하거나 막아야 해요.',
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user