107 lines
4.4 KiB
TypeScript
107 lines
4.4 KiB
TypeScript
/** 장기 30초 규칙 요약 (docs/07 §5). */
|
||
import type { ReactNode } from 'react';
|
||
import type { QuickCard } from '../types';
|
||
|
||
type Side = 'cho' | 'han';
|
||
const INK: Record<Side, string> = { cho: '#1d4ed8', han: '#c0262d' };
|
||
|
||
function octagon(cx: number, cy: number, r: number): string {
|
||
return Array.from({ length: 8 }, (_, i) => {
|
||
const a = Math.PI / 8 + (i * Math.PI) / 4;
|
||
return `${(cx + r * Math.cos(a)).toFixed(3)},${(cy + r * Math.sin(a)).toFixed(3)}`;
|
||
}).join(' ');
|
||
}
|
||
|
||
/** Octagonal piece with its hanja, blue for 초 and red for 한. */
|
||
function Piece({ ch, side, x, y, r = 0.4 }: { ch: string; side: Side; x: number; y: number; r?: number }) {
|
||
return (
|
||
<g>
|
||
<polygon points={octagon(x, y, r)} fill="#fbf3dc" stroke={INK[side]} strokeWidth={0.05} />
|
||
<text x={x} y={y} fill={INK[side]} fontSize={r * 1.05} fontWeight={700} fontFamily="'Noto Serif KR','Noto Serif CJK KR','Batang',serif" textAnchor="middle" dominantBaseline="central">
|
||
{ch}
|
||
</text>
|
||
</g>
|
||
);
|
||
}
|
||
|
||
/** Wooden board of w×h line crossings; `palace` draws the X of a 3×3 palace whose top-left crossing is given. */
|
||
function Board({ w, h, size, palace, children }: { w: number; h: number; size: number; palace?: [number, number]; children: ReactNode }) {
|
||
const pad = 0.55;
|
||
const vw = w - 1 + 2 * pad;
|
||
const vh = h - 1 + 2 * pad;
|
||
const scale = size / Math.max(vw, vh);
|
||
return (
|
||
<svg viewBox={`${-pad} ${-pad} ${vw} ${vh}`} width={vw * scale} height={vh * scale} aria-hidden>
|
||
<rect x={-pad} y={-pad} width={vw} height={vh} rx={0.2} fill="#e8c48a" />
|
||
<g stroke="#7a5228" strokeWidth={0.04}>
|
||
{Array.from({ length: w }, (_, x) => (
|
||
<line key={`v${x}`} x1={x} y1={0} x2={x} y2={h - 1} />
|
||
))}
|
||
{Array.from({ length: h }, (_, y) => (
|
||
<line key={`h${y}`} x1={0} y1={y} x2={w - 1} y2={y} />
|
||
))}
|
||
{palace && (
|
||
<>
|
||
<line x1={palace[0]} y1={palace[1]} x2={palace[0] + 2} y2={palace[1] + 2} />
|
||
<line x1={palace[0] + 2} y1={palace[1]} x2={palace[0]} y2={palace[1] + 2} />
|
||
</>
|
||
)}
|
||
</g>
|
||
{children}
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
const Dot = ({ x, y }: { x: number; y: number }) => <circle cx={x} cy={y} r={0.15} fill="#16a34a" />;
|
||
const Cross = ({ x, y }: { x: number; y: number }) => (
|
||
<g stroke="#dc2626" strokeWidth={0.09}>
|
||
<line x1={x - 0.17} y1={y - 0.17} x2={x + 0.17} y2={y + 0.17} />
|
||
<line x1={x + 0.17} y1={y - 0.17} x2={x - 0.17} y2={y + 0.17} />
|
||
</g>
|
||
);
|
||
|
||
export const quickStart: QuickCard[] = [
|
||
{
|
||
pic: (
|
||
<Board w={3} h={4} size={160} palace={[0, 0]}>
|
||
<circle cx={1} cy={1} r={0.6} fill="rgba(220,38,38,0.2)" stroke="#dc2626" strokeWidth={0.07} />
|
||
<Piece ch="漢" side="han" x={1} y={1} r={0.46} />
|
||
<Piece ch="車" side="cho" x={1} y={3} />
|
||
</Board>
|
||
),
|
||
alt: '파란 차가 궁성 안의 빨간 궁을 곧게 겨누고 있어요(장군)',
|
||
title: '상대 궁을 몰아요',
|
||
text: '초(파랑)가 먼저 두고 번갈아 한 수씩 둬요. 상대 궁이 어디로 피해도 잡히게 만들면(외통) 이겨요.',
|
||
},
|
||
{
|
||
pic: (
|
||
<Board w={5} h={5} size={160}>
|
||
{[[0, 1], [4, 1], [0, 3], [4, 3], [1, 4], [3, 4]].map(([x, y]) => (
|
||
<Dot key={`${x}-${y}`} x={x!} y={y!} />
|
||
))}
|
||
<Cross x={1} y={0} />
|
||
<Cross x={3} y={0} />
|
||
<Piece ch="馬" side="cho" x={2} y={2} />
|
||
<Piece ch="卒" side="cho" x={2} y={1} r={0.3} />
|
||
</Board>
|
||
),
|
||
alt: '가운데 마 둘레에 갈 수 있는 곳이 초록 점으로, 바로 위가 막힌 쪽은 빨간 ✕로 보여요',
|
||
title: '누르면 갈 곳이 보여요',
|
||
text: '기물을 누르면 갈 수 있는 곳에 초록 점이 찍혀요. 기물은 칸이 아니라 선이 만나는 점에 놓여요. 마·상은 바로 앞(멱)이 막히면 못 가요.',
|
||
},
|
||
{
|
||
pic: (
|
||
<Board w={5} h={2} size={220}>
|
||
<Dot x={3} y={1} />
|
||
<Dot x={4} y={1} />
|
||
<path d="M 0 0.55 Q 1.5 -0.4 3 0.6" fill="none" stroke="#7c3aed" strokeWidth={0.07} strokeDasharray="0.14 0.08" />
|
||
<Piece ch="包" side="cho" x={0} y={1} />
|
||
<Piece ch="兵" side="han" x={2} y={1} r={0.3} />
|
||
</Board>
|
||
),
|
||
alt: '포가 기물 하나를 뛰어넘어 그 너머 점으로 가는 모습이에요',
|
||
title: '포는 넘어서 가요',
|
||
text: '포는 꼭 기물 하나를 뛰어넘어서만 움직여요. 장군을 받으면 다른 수보다 먼저 궁을 피하거나 막아야 해요.',
|
||
},
|
||
];
|