Skip to main content
Glama

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
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • 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 }] };
      }
    }
  • Input schema definition for analyze_difficulty tool - empty object since it requires no parameters.
    inputSchema: {
      type: 'object',
      properties: {}
    },
  • 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 }] };
    }
  • 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;
    }
  • 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;
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/wmoten/ice-puzzle-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server