import type { RawLevelJson } from "./LevelTypes"; export interface ValidationResult { valid: boolean; errors: string[]; warnings: string[]; } /** * 校验 RawLevelJson 关卡数据完整性和合理性 */ export function validateLevel(raw: unknown): ValidationResult { const errors: string[] = []; const warnings: string[] = []; if (typeof raw !== "object" || raw === null) { return { valid: false, errors: ["关卡数据不是有效对象"], warnings }; } const data = raw as Partial; // 1. 基础结构检查 if (typeof data.row !== "number" || data.row <= 0) { errors.push(`row 必须为正整数,当前值: ${data.row}`); } if (typeof data.col !== "number" || data.col <= 0) { errors.push(`col 必须为正整数,当前值: ${data.col}`); } if (!Array.isArray(data.arrows) || data.arrows.length === 0) { errors.push("arrows 必须为非空数组"); return { valid: false, errors, warnings }; } const rows = data.row ?? 0; const cols = data.col ?? 0; // 2. 逐条路径校验 const allOccupiedCells = new Set(); for (let i = 0; i < data.arrows.length; i++) { const path = data.arrows[i]; if (!Array.isArray(path)) { errors.push(`arrows[${i}] 不是数组`); continue; } if (path.length < 2) { errors.push(`arrows[${i}] 至少需要 2 个点,当前 ${path.length} 个`); continue; } // 3. 坐标越界检查 for (let j = 0; j < path.length; j++) { const point = path[j]; if (!Array.isArray(point) || point.length < 2) { errors.push(`arrows[${i}][${j}] 格式错误,应为 [x, y]`); continue; } const [x, y] = point as [number, number]; if (typeof x !== "number" || typeof y !== "number") { errors.push(`arrows[${i}][${j}] 坐标必须为数字`); continue; } if (x < 0 || x >= cols) { errors.push(`arrows[${i}][${j}] x=${x} 越界 (合法范围: 0~${cols - 1})`); } if (y < 0 || y >= rows) { errors.push(`arrows[${i}][${j}] y=${y} 越界 (合法范围: 0~${rows - 1})`); } } // 4. 路径占据的格子(检测重叠) const pathCells = new Set(); for (let j = 0; j < path.length; j++) { const [x, y] = path[j] as [number, number]; if (typeof x === "number" && typeof y === "number") { const idx = y * cols + x; if (allOccupiedCells.has(idx)) { warnings.push(`arrows[${i}] 点 (${x},${y}) 与其他路径重叠`); } pathCells.add(idx); } } pathCells.forEach(c => allOccupiedCells.add(c)); } // 5. 可解性检查(简单版:至少一条路径的 head 朝向边界无阻挡) if (errors.length === 0 && data.arrows.length > 1) { // 简化:检查是否有路径的最后一个点在边界 let hasEdgePath = false; for (const path of data.arrows) { if (path.length < 2) continue; const last = path[path.length - 1] as [number, number]; const [x, y] = last; if (x === 0 || x === cols - 1 || y === 0 || y === rows - 1) { hasEdgePath = true; break; } } if (!hasEdgePath) { warnings.push("没有路径的箭头头部在棋盘边界,可能无法直接消除任何路径"); } } return { valid: errors.length === 0, errors, warnings, }; }