{
  "_comment": "PGS (PlayCraft Gameplay Schema) 参考骨架 - 球排序核心玩法。Agent 以此为起点编写或 fork 玩法规则。",
  "version": "1.0",
  "gameplayType": "ball_sort",
  "entities": {
    "tube": {
      "type": "container",
      "count": 8,
      "capacity": 4,
      "description": "球的容器（试管），默认 8 根，每根最多容纳 4 个球（由 config 覆盖）"
    },
    "ball": {
      "type": "stackable-item",
      "colorCount": 6,
      "description": "彩色球，共 6 种颜色，每种颜色有 4 个球（恰好填满 1 根管子）"
    },
    "emptyTubes": {
      "type": "counter",
      "initial": 2,
      "description": "额外的空管子数量，作为缓冲区（由 config 覆盖）"
    },
    "moves": {
      "type": "counter",
      "initial": 0,
      "description": "已使用步数（无限制模式）"
    }
  },
  "rules": {
    "move_top_ball": {
      "description": "只能移动管子最顶层的球",
      "source": "top_of_tube_only"
    },
    "valid_move_check": {
      "description": "合法移动的两个条件：1. 目标管顶部颜色与被移动球相同，或目标管为空；2. 目标管未满",
      "conditions": [
        "same_color_or_empty_target",
        "capacity_check"
      ],
      "rejectIfFail": true
    },
    "optimization": {
      "description": "优化规则：不允许将一根颜色单一的管子的球移入另一根空管（无意义移动）",
      "skipPureTubeToEmpty": true
    }
  },
  "algorithm": {
    "description": "主循环：选择 → 移动 → 胜利/死锁检测",
    "steps": [
      "1. 玩家点击源管子，选中最顶层的球",
      "2. 玩家点击目标管子",
      "3. 执行 valid_move_check：若不合法则取消选中并提示",
      "4. 合法则将球从源管顶部移入目标管顶部，moves + 1",
      "5. 检测胜利条件：每根管子内的球颜色全部相同（或管子为空）",
      "6. 检测死锁：遍历所有合法移动，若为空则触发失败/提示"
    ]
  },
  "winCondition": {
    "type": "all_tubes_sorted",
    "description": "每根非空管子内的球颜色完全相同（一根管子一种颜色）"
  },
  "loseCondition": {
    "type": "deadlock",
    "description": "当前棋盘上不存在任何合法移动（死锁）"
  },
  "testCases": [
    {
      "id": "tc_valid_move_same_color",
      "description": "合法移动：目标管顶部颜色与被移动球相同",
      "initialState": {
        "tubes": [
          { "id": 0, "balls": ["red", "blue", "red"] },
          { "id": 1, "balls": ["red"] },
          { "id": 2, "balls": [] }
        ],
        "tubeCapacity": 4,
        "moves": 0
      },
      "action": { "type": "move", "from": 0, "to": 1 },
      "expectedState": {
        "tubes": [
          { "id": 0, "balls": ["red", "blue"] },
          { "id": 1, "balls": ["red", "red"] },
          { "id": 2, "balls": [] }
        ],
        "moves": 1,
        "win": false,
        "deadlock": false
      }
    },
    {
      "id": "tc_valid_move_to_empty",
      "description": "合法移动：移入空管",
      "initialState": {
        "tubes": [
          { "id": 0, "balls": ["blue", "red"] },
          { "id": 1, "balls": [] },
          { "id": 2, "balls": ["blue"] }
        ],
        "tubeCapacity": 4,
        "moves": 0
      },
      "action": { "type": "move", "from": 0, "to": 1 },
      "expectedState": {
        "tubes": [
          { "id": 0, "balls": ["blue"] },
          { "id": 1, "balls": ["red"] },
          { "id": 2, "balls": ["blue"] }
        ],
        "moves": 1,
        "win": false
      }
    },
    {
      "id": "tc_invalid_move_wrong_color",
      "description": "非法移动：目标管顶部颜色不同",
      "initialState": {
        "tubes": [
          { "id": 0, "balls": ["red"] },
          { "id": 1, "balls": ["blue"] }
        ],
        "tubeCapacity": 4,
        "moves": 0
      },
      "action": { "type": "move", "from": 0, "to": 1 },
      "expectedState": {
        "tubes": [
          { "id": 0, "balls": ["red"] },
          { "id": 1, "balls": ["blue"] }
        ],
        "moves": 0,
        "moveRejected": true
      }
    },
    {
      "id": "tc_invalid_move_full_tube",
      "description": "非法移动：目标管已满（容量 4，已有 4 球）",
      "initialState": {
        "tubes": [
          { "id": 0, "balls": ["red"] },
          { "id": 1, "balls": ["red", "red", "red", "red"] }
        ],
        "tubeCapacity": 4,
        "moves": 2
      },
      "action": { "type": "move", "from": 0, "to": 1 },
      "expectedState": {
        "moves": 2,
        "moveRejected": true
      }
    },
    {
      "id": "tc_win",
      "description": "胜利：所有管子排序完成",
      "initialState": {
        "tubes": [
          { "id": 0, "balls": ["red", "red", "red"] },
          { "id": 1, "balls": ["blue", "blue", "blue", "red"] },
          { "id": 2, "balls": [] }
        ],
        "tubeCapacity": 4,
        "moves": 5
      },
      "action": { "type": "move", "from": 1, "to": 0 },
      "expectedState": {
        "tubes": [
          { "id": 0, "balls": ["red", "red", "red", "red"] },
          { "id": 1, "balls": ["blue", "blue", "blue"] },
          { "id": 2, "balls": [] }
        ],
        "moves": 6,
        "win": false
      }
    },
    {
      "id": "tc_deadlock_detection",
      "description": "死锁检测：没有任何合法移动",
      "initialState": {
        "tubes": [
          { "id": 0, "balls": ["red", "blue"] },
          { "id": 1, "balls": ["blue", "red"] }
        ],
        "tubeCapacity": 2,
        "moves": 10
      },
      "action": { "type": "check" },
      "expectedState": {
        "deadlock": true,
        "validMoves": []
      }
    }
  ]
}
