/** * 도블 그림 57종 (docs/games/dobble.md §3.2) — all drawn here as simple SVG in a 100×100 box. * Every picture has its own silhouette with a dark outline, so colour is only a secondary cue (colour-blind friendly). * Index = icon id (same order as SYMBOL_NAMES in @bg/games/dobble). */ import type { ReactNode } from 'react'; const O = '#1f2937'; /** Outline props for filled shapes. */ const L = { stroke: O, strokeWidth: 3.5, strokeLinejoin: 'round', strokeLinecap: 'round' } as const; /** Thick stroked lines (no fill). */ const W = (color: string, w = 6) => ({ stroke: color, strokeWidth: w, strokeLinecap: 'round', strokeLinejoin: 'round', fill: 'none' }) as const; const rays = (n: number, r1: number, r2: number, cx = 50, cy = 50, a0 = 0) => Array.from({ length: n }, (_, i) => { const a = a0 + (i * 2 * Math.PI) / n; return `M${(cx + Math.cos(a) * r1).toFixed(1)} ${(cy + Math.sin(a) * r1).toFixed(1)} L${(cx + Math.cos(a) * r2).toFixed(1)} ${(cy + Math.sin(a) * r2).toFixed(1)}`; }).join(' '); const starPts = (n: number, ro: number, ri: number, cx = 50, cy = 52) => Array.from({ length: n * 2 }, (_, i) => { const a = -Math.PI / 2 + (i * Math.PI) / n; const r = i % 2 ? ri : ro; return `${(cx + Math.cos(a) * r).toFixed(1)},${(cy + Math.sin(a) * r).toFixed(1)}`; }).join(' '); /** Korean names, same order as SYMBOL_NAMES in @bg/games/dobble (kept here so the screen does not pull in the rules module; a test checks they match). */ export const ICON_NAMES = [ '사과', '해', '달', '별', '하트', '물방울', '불꽃', '눈송이', '번개', '구름', '나무', '꽃', '나뭇잎', '선인장', '버섯', '당근', '레몬', '체리', '고양이', '강아지', '물고기', '새', '거북이', '무당벌레', '거미', '공룡', '토끼', '펭귄', '열쇠', '자물쇠', '시계', '전구', '연필', '가위', '안경', '우산', '모자', '왕관', '종', '닻', '자전거', '자동차', '로켓', '배', '공', '주사위', '음표', '카메라', '집', '성', '선물상자', '촛불', '망치', '느낌표', '물음표', '눈사람', '무지개', ]; export const ICONS: (() => ReactNode)[] = [ // 0 사과 () => ( <> ), // 1 해 () => ( <> ), // 2 달 () => , // 3 별 () => , // 4 하트 () => , // 5 물방울 () => ( <> ), // 6 불꽃 () => ( <> ), // 7 눈송이 () => ( <> { const a = Math.PI / 2 + (i * Math.PI) / 3; const bx = 50 + Math.cos(a) * 26; const by = 50 + Math.sin(a) * 26; const p = (d: number) => `${(bx + Math.cos(a + d) * 12).toFixed(1)} ${(by + Math.sin(a + d) * 12).toFixed(1)}`; return `M${p(0.9)} L${bx.toFixed(1)} ${by.toFixed(1)} L${p(-0.9)}`; }).join(' ')} {...W('#0284c7', 5)} /> ), // 8 번개 () => , // 9 구름 () => , // 10 나무 (pine) () => ( <> ), // 11 꽃 () => ( <> {[0, 1, 2, 3, 4].map((i) => { const a = -Math.PI / 2 + (i * 2 * Math.PI) / 5; return ; })} ), // 12 나뭇잎 () => ( <> ), // 13 선인장 () => ( <> ), // 14 버섯 () => ( <> ), // 15 당근 () => ( <> ), // 16 레몬 () => ( <> ), // 17 체리 () => ( <> ), // 18 고양이 () => ( <> ), // 19 강아지 () => ( <> ), // 20 물고기 () => ( <> ), // 21 새 () => ( <> ), // 22 거북이 () => ( <> ), // 23 무당벌레 () => ( <> ), // 24 거미 () => ( <> ), // 25 공룡 () => ( ), // 26 토끼 () => ( <> ), // 27 펭귄 () => ( <> ), // 28 열쇠 () => ( <> ), // 29 자물쇠 () => ( <> ), // 30 시계 () => ( <> ), // 31 전구 () => ( <> ), // 32 연필 () => ( ), // 33 가위 () => ( <> ), // 34 안경 () => ( <> ), // 35 우산 () => ( <> ), // 36 모자 (top hat) () => ( <> ), // 37 왕관 () => ( <> ), // 38 종 () => ( <> ), // 39 닻 () => ( <> ), // 40 자전거 () => ( <> ), // 41 자동차 () => ( <> ), // 42 로켓 () => ( <> ), // 43 배 (sailboat) () => ( <> ), // 44 공 (soccer ball) () => ( <> i % 2 === 0).join(' ')} fill={O} /> {[0, 1, 2, 3, 4].map((i) => { const a = -Math.PI / 2 + (i * 2 * Math.PI) / 5; return ; })} ), // 45 주사위 () => ( <> {[ [30, 30], [70, 30], [50, 50], [30, 70], [70, 70], ].map(([x, y]) => ( ))} ), // 46 음표 () => ( <> ), // 47 카메라 () => ( <> ), // 48 집 () => ( <> ), // 49 성 () => ( <> ), // 50 선물상자 () => ( <> ), // 51 촛불 () => ( <> ), // 52 망치 () => ( ), // 53 느낌표 () => ( <> ), // 54 물음표 () => ( <> ), // 55 눈사람 () => ( <> ), // 56 무지개 () => ( <> {['#ef4444', '#f97316', '#facc15', '#22c55e', '#3b82f6', '#8b5cf6'].map((c, i) => ( ))} ), ]; /** One icon in its own 100×100 SVG. */ export function SymbolArt({ art, className }: { art: number; className?: string }) { const draw = ICONS[art]; return ( {draw ? draw() : null} ); }