78 lines
3.2 KiB
TypeScript
78 lines
3.2 KiB
TypeScript
/** 체스 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: '폰이 끝 줄에 닿으면 퀸 같은 강한 기물로 바뀌어요(승진). 내 킹이 공격받으면(체크) 꼭 먼저 피하거나 막아야 해요.',
|
||
},
|
||
];
|