analyze_difficulty
Analyze current level difficulty and characteristics to assess puzzle complexity and design quality for ice puzzle creation.
Instructions
Analyze current level difficulty and characteristics
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/tools/solver-tools.ts:305-402 (handler)Complete implementation of the analyze_difficulty tool including registration, input schema, and handler. The handler analyzes difficulty by checking solvability, move count, mechanics used, direction balance, quadrants visited, dead ends, and calculates estimated difficulty rating (tutorial/easy/medium/hard/expert).{ name: 'analyze_difficulty', description: 'Analyze the difficulty and characteristics of the current level', inputSchema: { type: 'object', properties: {} }, handler: async () => { const error = checkActiveDraft(); if (error) { return { content: [{ type: 'text', text: error }] }; } const draft = draftStore.getCurrentDraft()!; const puzzleData = draftStore.exportPuzzleData(); if (!puzzleData) { return { content: [{ type: 'text', text: 'Failed to export puzzle data' }] }; } // Solve to get the solution const solverResult = solve(puzzleData); let output = `Difficulty Analysis\n${'='.repeat(50)}\n\n`; // Solvability output += `Solvable: ${solverResult.solvable ? '✓ Yes' : '❌ No'}\n`; if (!solverResult.solvable) { output += `\nCannot analyze unsolvable level.\n`; return { content: [{ type: 'text', text: output }] }; } const moves = solverResult.moves!; const solution = solverResult.solution!; // Move count output += `Move count: ${moves}\n\n`; // Mechanics used const mechanics = detectMechanics(draft); output += `Mechanics used (${mechanics.length}):\n`; mechanics.forEach(m => output += ` • ${m}\n`); output += `\n`; // Direction balance const directionBalance: Record<Direction, number> = { up: 0, down: 0, left: 0, right: 0 }; solution.forEach(dir => { directionBalance[dir]++; }); output += `Direction balance:\n`; output += ` Up: ${directionBalance.up} (${Math.round(directionBalance.up / moves * 100)}%)\n`; output += ` Down: ${directionBalance.down} (${Math.round(directionBalance.down / moves * 100)}%)\n`; output += ` Left: ${directionBalance.left} (${Math.round(directionBalance.left / moves * 100)}%)\n`; output += ` Right: ${directionBalance.right} (${Math.round(directionBalance.right / moves * 100)}%)\n`; output += `\n`; // Quadrants visited const quadrants = new Set<number>(); let pos = { ...draft.startPosition }; quadrants.add(getQuadrant(pos.x, pos.y, draft.gridWidth, draft.gridHeight)); for (const dir of solution) { const result = slideWithHazards(pos, dir, puzzleData, [], [], false); pos = result.position; quadrants.add(getQuadrant(pos.x, pos.y, draft.gridWidth, draft.gridHeight)); } output += `Quadrants visited: ${Array.from(quadrants).sort().join(', ')} (${quadrants.size}/4)\n`; output += `\n`; // Dead ends const deadEndCount = countDeadEnds(draft, puzzleData); output += `Dead ends (3+ blocked directions): ${deadEndCount}\n`; output += `\n`; // Estimated difficulty let difficulty: 'tutorial' | 'easy' | 'medium' | 'hard' | 'expert'; if (moves <= 6) difficulty = 'tutorial'; else if (moves <= 12) difficulty = 'easy'; else if (moves <= 17) difficulty = 'medium'; else if (moves <= 24) difficulty = 'hard'; else difficulty = 'expert'; // Adjust based on mechanics if (mechanics.length >= 3 && difficulty === 'easy') difficulty = 'medium'; if (mechanics.length >= 4 && difficulty === 'medium') difficulty = 'hard'; output += `Estimated difficulty: ${difficulty.toUpperCase()}\n`; output += ` (tutorial: 2-6 moves, easy: 6-12, medium: 10-17, hard: 14-24, expert: 18+)\n`; return { content: [{ type: 'text', text: output }] }; } }
- src/tools/solver-tools.ts:308-311 (schema)Input schema definition for analyze_difficulty tool - empty object since it requires no parameters.inputSchema: { type: 'object', properties: {} },
- src/tools/solver-tools.ts:312-401 (handler)Core handler logic for analyze_difficulty: runs solver to get optimal solution, calculates metrics (moves, mechanics, directions, quadrants, dead ends), and estimates difficulty level based on move count and mechanics complexity.handler: async () => { const error = checkActiveDraft(); if (error) { return { content: [{ type: 'text', text: error }] }; } const draft = draftStore.getCurrentDraft()!; const puzzleData = draftStore.exportPuzzleData(); if (!puzzleData) { return { content: [{ type: 'text', text: 'Failed to export puzzle data' }] }; } // Solve to get the solution const solverResult = solve(puzzleData); let output = `Difficulty Analysis\n${'='.repeat(50)}\n\n`; // Solvability output += `Solvable: ${solverResult.solvable ? '✓ Yes' : '❌ No'}\n`; if (!solverResult.solvable) { output += `\nCannot analyze unsolvable level.\n`; return { content: [{ type: 'text', text: output }] }; } const moves = solverResult.moves!; const solution = solverResult.solution!; // Move count output += `Move count: ${moves}\n\n`; // Mechanics used const mechanics = detectMechanics(draft); output += `Mechanics used (${mechanics.length}):\n`; mechanics.forEach(m => output += ` • ${m}\n`); output += `\n`; // Direction balance const directionBalance: Record<Direction, number> = { up: 0, down: 0, left: 0, right: 0 }; solution.forEach(dir => { directionBalance[dir]++; }); output += `Direction balance:\n`; output += ` Up: ${directionBalance.up} (${Math.round(directionBalance.up / moves * 100)}%)\n`; output += ` Down: ${directionBalance.down} (${Math.round(directionBalance.down / moves * 100)}%)\n`; output += ` Left: ${directionBalance.left} (${Math.round(directionBalance.left / moves * 100)}%)\n`; output += ` Right: ${directionBalance.right} (${Math.round(directionBalance.right / moves * 100)}%)\n`; output += `\n`; // Quadrants visited const quadrants = new Set<number>(); let pos = { ...draft.startPosition }; quadrants.add(getQuadrant(pos.x, pos.y, draft.gridWidth, draft.gridHeight)); for (const dir of solution) { const result = slideWithHazards(pos, dir, puzzleData, [], [], false); pos = result.position; quadrants.add(getQuadrant(pos.x, pos.y, draft.gridWidth, draft.gridHeight)); } output += `Quadrants visited: ${Array.from(quadrants).sort().join(', ')} (${quadrants.size}/4)\n`; output += `\n`; // Dead ends const deadEndCount = countDeadEnds(draft, puzzleData); output += `Dead ends (3+ blocked directions): ${deadEndCount}\n`; output += `\n`; // Estimated difficulty let difficulty: 'tutorial' | 'easy' | 'medium' | 'hard' | 'expert'; if (moves <= 6) difficulty = 'tutorial'; else if (moves <= 12) difficulty = 'easy'; else if (moves <= 17) difficulty = 'medium'; else if (moves <= 24) difficulty = 'hard'; else difficulty = 'expert'; // Adjust based on mechanics if (mechanics.length >= 3 && difficulty === 'easy') difficulty = 'medium'; if (mechanics.length >= 4 && difficulty === 'medium') difficulty = 'hard'; output += `Estimated difficulty: ${difficulty.toUpperCase()}\n`; output += ` (tutorial: 2-6 moves, easy: 6-12, medium: 10-17, hard: 14-24, expert: 18+)\n`; return { content: [{ type: 'text', text: output }] }; }
- src/tools/solver-tools.ts:22-47 (helper)Helper function detectMechanics() - scans the draft for level mechanics including lava, hot_coals, thin_ice, pushable_rocks, warps, and pressure_plate_barrier combinations.function detectMechanics(draft: any): string[] { const mechanics: string[] = []; // Check obstacles const hasLava = draft.obstacles.some((o: any) => o.type === 'lava'); const hasHotCoals = draft.obstacles.some((o: any) => o.type === 'hot_coals' || o.type === 'spike'); if (hasLava) mechanics.push('lava'); if (hasHotCoals) mechanics.push('hot_coals'); // Check special elements if (draft.thinIceTiles && draft.thinIceTiles.length > 0) { mechanics.push('thin_ice'); } if (draft.pushableRocks && draft.pushableRocks.length > 0) { mechanics.push('pushable_rocks'); } if (draft.warpPairs && draft.warpPairs.length > 0) { mechanics.push('warps'); } if (draft.pressurePlate && draft.barrier) { mechanics.push('pressure_plate_barrier'); } return mechanics; }
- src/tools/solver-tools.ts:59-91 (helper)Helper function countDeadEnds() - counts tiles with 3+ blocked directions to identify dead ends in the puzzle layout.function countDeadEnds(draft: any, puzzleData: any): number { let deadEndCount = 0; for (let y = 0; y < draft.gridHeight; y++) { for (let x = 0; x < draft.gridWidth; x++) { const pos = { x, y }; // Skip if there's an obstacle here const hasObstacle = puzzleData.obstacles.some((o: any) => o.x === x && o.y === y); if (hasObstacle) continue; // Count how many directions lead to walls or hazards let blockedDirections = 0; for (const dir of ALL_DIRECTIONS) { const result = slideWithHazards(pos, dir, puzzleData, [], [], false); // Consider blocked if we didn't move or hit something deadly immediately if ((result.position.x === x && result.position.y === y) || result.hitLava || (result.hitBarrier && !result.crossedPressurePlate)) { blockedDirections++; } } if (blockedDirections >= 3) { deadEndCount++; } } } return deadEndCount; }