// 1차 mock 데이터 — Firebase 연결 전까지 사용. // ⚠️ AI 예측은 플레이스홀더. 발행 전 GPT/Claude/Gemini 실제 출력(JSON)으로 교체할 것. // 경기 일정은 lib/schedule.ts(Group A 정식 일정, KST)에서 자동 반영. // // 멀티 경기(06.09 회의): 모든 경기에 3 AI 예측 + Crowd 가 있어야 대시보드/상세가 깨지지 않는다. // 체코전(FEATURED)은 큐레이션 값, 그 외 경기는 matchId 시드 결정론적 생성 (Math.random 미사용). import type { AIPrediction, CrowdStats, Match, ModelName, Outcome } from "./types"; import { FEATURED, GROUP_A } from "./schedule"; export const MATCH = FEATURED; // 대표 경기 = 한국 vs 체코 (schedule SSOT) // 데모용: 실제 운영은 투표 오픈 = 킥오프 D-2(opensAt). 팀 데모에서 픽이 막히지 않게 // true면 오픈 게이트를 무시하고 즉시 참여 가능. 운영 배포 시 false 로. export const DEMO_FORCE_OPEN = true; // 체코전 큐레이션 예측 (D3: reason 짧고 또렷이) const FEATURED_PREDICTIONS: AIPrediction[] = [ { matchId: MATCH.matchId, model: "GPT", outcome: "TEAM_A_WIN", scoreA: 2, scoreB: 1, confidencePct: 62, reasonShort: "한국 측면 스피드가 한 끗 위", generatedAt: "2026-06-08", }, { matchId: MATCH.matchId, model: "Claude", outcome: "DRAW", scoreA: 1, scoreB: 1, confidencePct: 41, reasonShort: "중원 힘싸움이 끝까지 팽팽", generatedAt: "2026-06-08", }, { matchId: MATCH.matchId, model: "Gemini", outcome: "TEAM_B_WIN", scoreA: 0, scoreB: 2, confidencePct: 28, reasonShort: "체코 역습 한 방이 매섭다", generatedAt: "2026-06-08", }, ]; // ── 결정론적 생성 (matchId 시드) ────────────────────────────── function hash(s: string): number { let h = 2166136261; for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); } return h >>> 0; } const MODELS: ModelName[] = ["GPT", "Claude", "Gemini"]; // AI 셋이 갈리도록 만든 결과 조합 (홈/원정 균형) const OUTCOME_SETS: Outcome[][] = [ ["TEAM_A_WIN", "DRAW", "TEAM_B_WIN"], ["TEAM_A_WIN", "TEAM_A_WIN", "DRAW"], ["TEAM_B_WIN", "DRAW", "TEAM_A_WIN"], ["TEAM_A_WIN", "DRAW", "TEAM_A_WIN"], ["DRAW", "TEAM_B_WIN", "TEAM_A_WIN"], ]; const SCORE_BY_OUTCOME: Record = { TEAM_A_WIN: [[2, 1], [1, 0], [2, 0], [3, 1]], DRAW: [[1, 1], [0, 0], [2, 2]], TEAM_B_WIN: [[0, 1], [1, 2], [0, 2], [1, 3]], }; function reasonFor(match: Match, outcome: Outcome, seed: number): string { const A = match.teamA.shortName; const B = match.teamB.shortName; const win = outcome === "TEAM_A_WIN" ? A : outcome === "TEAM_B_WIN" ? B : null; const winPool = win ? [ `${win} 측면 스피드가 한 끗 위`, `${win} 세트피스 한 방이면 끝`, `${win} 초반 기세에서 앞선다`, `${win} 결정력이 승부를 가른다`, ] : []; const drawPool = [ "중원 힘싸움이 끝까지 팽팽", "양 팀 다 한 방 있는 진검승부", "first goal이 모든 걸 가른다", ]; const pool = win ? winPool : drawPool; return pool[seed % pool.length]; } function generatePredictions(match: Match): AIPrediction[] { const base = hash(match.matchId); const outcomes = OUTCOME_SETS[base % OUTCOME_SETS.length]; return MODELS.map((model, i) => { const seed = hash(match.matchId + model); const outcome = outcomes[i]; const scores = SCORE_BY_OUTCOME[outcome]; const [scoreA, scoreB] = scores[seed % scores.length]; const confidencePct = outcome === "DRAW" ? 33 + (seed % 13) : 46 + (seed % 25); // draw 33~45, win 46~70 return { matchId: match.matchId, model, outcome, scoreA, scoreB, confidencePct, reasonShort: reasonFor(match, outcome, seed), generatedAt: "2026-06-08", }; }); } export function getPredictions(match: Match): AIPrediction[] { if (match.matchId === FEATURED.matchId) return FEATURED_PREDICTIONS; return generatePredictions(match); } export function getCrowd(match: Match): CrowdStats { if (match.matchId === FEATURED.matchId) { return { matchId: MATCH.matchId, total: 1284, teamAWin: 616, draw: 347, teamBWin: 321 }; } const base = hash(match.matchId + "crowd"); const total = 240 + (base % 1400); const a = 28 + (base % 40); // 28~67% const d = 18 + ((base >> 3) % 22); // 18~39% const teamAWin = Math.round((total * a) / 100); const draw = Math.round((total * Math.min(d, 100 - a - 5)) / 100); const teamBWin = total - teamAWin - draw; return { matchId: match.matchId, total, teamAWin, draw, teamBWin }; } // 경기별 공유 딥링크 (D8) const ORIGIN = "https://triplepick-web.vercel.app"; export function matchUrl(matchId: string): string { return `${ORIGIN}/match/${matchId}`; } // ── 하위 호환 (체코전 단일 참조용) ── export const AI_PREDICTIONS: AIPrediction[] = FEATURED_PREDICTIONS; export const CROWD: CrowdStats = getCrowd(FEATURED); export const LANDING_URL = matchUrl(FEATURED.matchId); // schedule re-export (페이지에서 한 곳에서 import) export { GROUP_A };