---
name: ball_sort.aigameplay
description: 球排序核心玩法封包 - 水管/试管球排序的 PGS 实现，玩家通过移动彩色球让每个容器中的球颜色一致
triggers: 球排序,ball sort,水排序,water sort,试管,tube,容器,排序,sorting,puzzle,玩法,gameplay
---

# ball_sort.aigameplay

球排序（水排序）核心玩法封包。实现标准球排序规则的 PGS JSON 和 reducer，玩家将球从一个容器移到另一个容器，目标是让每个容器中的球颜色完全相同。

## Recipe

- **玩法类型**：ball_sort（球排序/水排序）
- **参考文件**：
  - `ref/pgs-schema.json`：PGS (PlayCraft Gameplay Schema) 规则骨架（entities、rules、algorithm、testCases）
  - `ref/reducer.template.ts`：reducer 接口约束模板（`init` / `dispatch` / `getState` + 分步函数）
- **核心规则**：
  - `tubeCapacity`: 4（每个容器最多容纳 4 个球）
  - `moveRule`: top_ball_only（只能移动最顶层的球）
  - `validMoveCheck`: same_color_or_empty_target（目标容器顶部颜色相同或容器为空）+ capacity_check（目标未满）
  - `emptyTubeCount`: 2（额外提供 2 个空容器作为缓冲）
- **PGS Agent 操作流程**：
  1. 读取 `ref/pgs-schema.json` 作为规则骨架起点
  2. 读取 `ref/reducer.template.ts` 了解接口约束
  3. 实现 `reducer.ts`，导出 `init(config)`、`dispatch(state, action)`、`getState(state)` 以及**全部分步函数**
  4. 运行 `ref/pgs-schema.json` 中的所有 `testCases`，全部通过后才算完成
  5. 更新 manifest 的 `generation.pgsVersion` 和 `result.outputFile`
- **可 fork 变体**：
  - 更多颜色：修改 `colorCount` 提升难度
  - 更大容量：修改 `tubeCapacity` 改变玩法深度
  - 限步模式：添加 `maxMoves` 规则限制操作次数

## Result

- **产物文件**：`reducer.ts`
- **格式**：TypeScript，导出 `init(config)`、`dispatch(state, action)`、`getState(state)` + 分步函数接口
- **验证**：所有 testCases 必须通过

## 渲染集成（⚠️ 场景层必读）

### 设计原则：逻辑与动画分离

reducer 提供两套接口：

| 接口 | 适用场景 | 特点 |
|------|---------|------|
| `dispatch()` | 无头测试、AI 模拟、跳过动画 | 一步到位，只返回最终状态 |
| 分步函数 | **渲染层/GameScene** | 逐步执行，返回中间过程数据 |

### ⚠️ 禁止在渲染层使用 dispatch() 然后全量重建容器

正确做法是**分步驱动动画链**：

```ts
// ✅ 正确做法：逐步调用分步函数，每步之间插入动画
// Step 1: 验证移动是否合法
const valid = isValidMove(state, fromTube, toTube);
if (!valid) { playShakeAnimation(fromTube); return; }

// Step 2: 执行移动，获取被移动的球信息
const { ball, newState } = moveBall(state, fromTube, toTube);
await playBallMoveAnimation(ball, fromTube, toTube);  // 只对该球做弧线移动动画

// Step 3: 检测胜利
const win = checkWin(newState);
if (win) { playWinAnimation(); return; }

// Step 4: 检测死锁
const validMoves = findValidMoves(newState);
if (validMoves.length === 0) { playDeadlockAnimation(); }
```

### 分步函数返回值与动画映射

| 分步函数 | 返回值 | 场景层动画 |
|---------|--------|-----------|
| `isValidMove()` | `boolean` | false → 播放容器抖动/禁止提示动画 |
| `moveBall()` | `{ ball, fromTube, toTube, newState }` | 对该球播放弧线移动 Tween 到目标容器顶部 |
| `checkWin()` | `boolean` | true → 播放通关庆祝动画 |
| `findValidMoves()` | `Move[]` | 空数组 → 触发死锁提示（Hint 按钮高亮） |

## Binding

- **Binding Role**：`gameplayRule`
- **挂载目标**：`game/gameplay/ball_sort/reducer.ts`
- **引用类型**：`gameplay-module-reference`
- **接口契约**：`init(config)` / `dispatch(state, action)` / `getState(state)` / `isValidMove()` / `moveBall()` / `checkWin()` / `findValidMoves()`

## Skill Definition

tools:
  - bash
  - write
  - read
prompt_extension: |
  You are implementing a ball sort (water sort) core gameplay module following the PGS specification.
  Write the PGS JSON with entities, rules, algorithm, and testCases.
  Implement the reducer.ts with:
    - init/dispatch/getState (一体化接口，用于测试)
    - isValidMove/moveBall/checkWin/findValidMoves (分步函数，用于渲染层动画驱动)
  Key constraints:
    - Only the top ball of each tube can be moved
    - A ball can only move to a tube if the top color matches OR the tube is empty
    - A tube can only receive a ball if it has capacity remaining
    - Win condition: every tube contains balls of only one color (or is empty)
    - Lose/deadlock: no valid moves exist
  The step-by-step functions are CRITICAL for smooth ball move animations.
  Run the testCases to verify the logic before finalizing.
  Apply the Binding to wire the reducer into the game's gameplay module path.
