{
  "_comment": "PGS (PlayCraft Gameplay Schema) 参考骨架 - 画线解谜玩法。Agent 以此为起点编写或 fork 玩法规则。",
  "version": "1.0",
  "gameplayType": "draw_line_puzzle",
  "entities": {
    "scene": {
      "type": "spatial",
      "description": "关卡场景空间",
      "fields": {
        "width": { "type": "number", "default": 800 },
        "height": { "type": "number", "default": 600 },
        "startPoint": { "type": "vec2", "description": "角色初始位置" },
        "endPoint": { "type": "vec2", "description": "目标位置（宝箱/出口）" },
        "obstacles": {
          "type": "rect[]",
          "description": "障碍物列表，每个为 {x, y, width, height}，线段不能穿过障碍"
        }
      }
    },
    "drawnLines": {
      "type": "entity-list",
      "description": "玩家已绘制的所有线段集合",
      "item": {
        "id": { "type": "string" },
        "points": { "type": "vec2[]", "description": "折线路径点数组，首尾相连" },
        "length": { "type": "number", "description": "该段折线的总长度（px）" }
      }
    },
    "character": {
      "type": "agent",
      "description": "被引导的角色",
      "fields": {
        "position": { "type": "vec2" },
        "state": { "type": "enum", "values": ["idle", "moving", "arrived", "blocked"], "default": "idle" },
        "pathProgress": { "type": "number", "description": "沿当前路径的移动进度（0~1）" }
      }
    },
    "target": {
      "type": "point",
      "description": "目标位置（角色需要到达的地点）",
      "fields": {
        "position": { "type": "vec2" },
        "radius": { "type": "number", "default": 30, "description": "到达判定半径" }
      }
    },
    "inkUsed": {
      "type": "counter",
      "initial": 0,
      "description": "已使用的墨水长度（所有线段总长度之和）"
    },
    "timeLeft": {
      "type": "counter",
      "description": "剩余时间（ms，仅 timeLimitEnabled 时有效）"
    }
  },
  "rules": {
    "draw_line": {
      "description": "玩家在空白区域绘制线段",
      "steps": [
        "1. startLine(point)：记录起始点，创建新线段",
        "2. extendLine(point)：追加新折点，检测是否与障碍物相交",
        "   - 若新线段与任何 obstacle 矩形相交：拒绝追加，标记 inkExceeded=false, blocked=true",
        "   - 若 inkUsed + newSegmentLength > maxInkLength：拒绝追加，标记 inkExceeded=true",
        "   - 否则：追加折点，更新 inkUsed",
        "3. finishLine()：封存当前线段"
      ]
    },
    "validate_path": {
      "description": "检查绘制的路径是否能引导角色从 startPoint 到 endPoint",
      "conditions": [
        "路径起点距离 scene.startPoint ≤ snapRadius（30px）",
        "路径终点距离 scene.endPoint ≤ snapRadius（30px）",
        "路径所有线段不与任何 obstacle 相交"
      ]
    },
    "execute": {
      "description": "路径验证通过后，角色沿路径移动",
      "speed": "characterSpeed（px/s，由 config 覆盖）",
      "interpolation": "线性插值沿折线路径逐段移动"
    },
    "win_check": {
      "description": "角色到达 target 判定圆内时触发胜利"
    },
    "line_limit": {
      "description": "墨水限制：inkUsed 超过 maxInkLength 时无法继续绘制"
    }
  },
  "algorithm": {
    "description": "主流程：draw → validate → execute → win",
    "steps": [
      "1. 玩家触摸开始：startLine(touchPoint)",
      "2. 玩家拖动：extendLine(touchPoint)，实时检测障碍碰撞和墨水限制",
      "3. 玩家抬起手指：finishLine()",
      "4. validatePath()：检查路径是否有效",
      "5. 若有效：character.state = moving，开始 executeCharacterMove(deltaMs) 循环",
      "6. 每帧：executeCharacterMove(deltaMs) 更新 character.position 和 pathProgress",
      "7. checkWin()：若 character.position 进入 target 半径 → win=true"
    ]
  },
  "winCondition": {
    "type": "character_reaches_target",
    "description": "角色到达目标点判定圆内"
  },
  "loseCondition": {
    "type": "time_limit_or_ink_exhausted",
    "description": "（时限模式）时间耗尽前角色未到达目标；或墨水耗尽无法画出有效路径（软失败，需玩家重试）"
  },
  "testCases": [
    {
      "id": "tc_draw_path_and_win",
      "description": "正常通关：画出有效路径，角色到达目标",
      "initialState": {
        "scene": {
          "width": 800, "height": 600,
          "startPoint": { "x": 100, "y": 300 },
          "endPoint": { "x": 700, "y": 300 },
          "obstacles": []
        },
        "drawnLines": [],
        "character": { "position": { "x": 100, "y": 300 }, "state": "idle", "pathProgress": 0 },
        "target": { "position": { "x": 700, "y": 300 }, "radius": 30 },
        "inkUsed": 0
      },
      "actions": [
        { "type": "startLine", "point": { "x": 100, "y": 300 } },
        { "type": "extendLine", "point": { "x": 400, "y": 300 } },
        { "type": "extendLine", "point": { "x": 700, "y": 300 } },
        { "type": "finishLine" },
        { "type": "tick", "deltaMs": 7000 }
      ],
      "expectedState": {
        "character.state": "arrived",
        "win": true
      }
    },
    {
      "id": "tc_obstacle_blocks_line",
      "description": "障碍阻挡：线段与障碍物相交时被拒绝，不能继续绘制",
      "initialState": {
        "scene": {
          "width": 800, "height": 600,
          "startPoint": { "x": 100, "y": 300 },
          "endPoint": { "x": 700, "y": 300 },
          "obstacles": [
            { "x": 300, "y": 200, "width": 100, "height": 200 }
          ]
        },
        "drawnLines": [],
        "character": { "position": { "x": 100, "y": 300 }, "state": "idle", "pathProgress": 0 },
        "target": { "position": { "x": 700, "y": 300 }, "radius": 30 },
        "inkUsed": 0
      },
      "actions": [
        { "type": "startLine", "point": { "x": 100, "y": 300 } },
        { "type": "extendLine", "point": { "x": 500, "y": 300 } }
      ],
      "expectedState": {
        "lastExtendResult.blocked": true,
        "drawnLines[0].points.length": { "max": 2 },
        "win": false
      }
    },
    {
      "id": "tc_ink_too_long",
      "description": "墨水耗尽：绘制超过最大墨水长度时被拒绝",
      "initialState": {
        "scene": {
          "width": 800, "height": 600,
          "startPoint": { "x": 100, "y": 300 },
          "endPoint": { "x": 700, "y": 300 },
          "obstacles": []
        },
        "drawnLines": [],
        "character": { "position": { "x": 100, "y": 300 }, "state": "idle", "pathProgress": 0 },
        "target": { "position": { "x": 700, "y": 300 }, "radius": 30 },
        "inkUsed": 480,
        "config": { "maxInkLength": 500 }
      },
      "actions": [
        { "type": "startLine", "point": { "x": 100, "y": 300 } },
        { "type": "extendLine", "point": { "x": 700, "y": 300 } }
      ],
      "expectedState": {
        "lastExtendResult.inkExceeded": true,
        "win": false
      }
    }
  ]
}
