---
name: match_engine.aicomponent
description: 通用篮子匹配引擎。纯函数实现，无 Phaser 依赖。提供同类聚合插入、N消检测、移除、满溢判断。通过 groupKey 回调函数确定匹配分组，支持单维度（颜色）和多维度（颜色×形状）匹配。
triggers: 需要篮子匹配逻辑、三消检测、同类聚合插入时触发。
---

# 通用篮子匹配引擎（Match Engine）

## 说明

纯逻辑组件，**零 Phaser 依赖**，可独立单元测试。提供篮子中物品的核心操作：

- **同类聚合插入**：新物品插到最后一个同类物品旁边（非追加末尾）
- **N 消检测**：检查是否有 N 个同类物品，返回匹配列表
- **移除**：从篮子中删除匹配物品
- **满溢判断**：当前数量是否达到容量上限

## Scaffold

| 目标路径 | 来源 | 说明 |
|---------|-----|------|
| `src/game/logic/MatchEngine.ts` | `ref/MatchEngine.ts` | 匹配引擎 |

## Recipe

| 决策 | 原因 |
|------|------|
| **纯函数 + 零依赖** | 匹配逻辑不依赖 Phaser 或 DOM，可在 Node.js 中单元测试；同一套 MatchEngine 可以在 Phaser 和 Three.js 渲染后端复用 |
| **静态方法类** | MatchEngine 是无状态算法集合，不需要实例化；静态方法调用更简洁，不需要传递 this |
| **groupKey 回调** | 分组依据（颜色 / 颜色+形状）由调用方决定，MatchEngine 不绑定具体字段；泛化设计支持多维度匹配扩展 |
| **同类聚合插入** | 新物品插到最后一个同类旁边（非追加末尾），符合三消类游戏标准交互模式，视觉反馈更直观 |

## Adapter

- **Role**: `matchEngine` — 通用篮子同类聚合匹配引擎（纯逻辑，零渲染依赖）
- **Provides**: `MatchEngine` 静态类（`insertToTray()`、`checkMatch()`、`removeFromTray()`、`isTrayFull()`），`TrayItem` 类型
- **Requires**: 无（纯 TypeScript，零外部依赖）
- **Consumed by**: `game_scene.aicomponent`（pick_and_match 玩法中管理篮子逻辑）、`gameplay_unit_test.aivalidator`（匹配逻辑单元测试）
- **Integration point**: `src/game/logic/MatchEngine.ts` → `Game.ts` 在物品插入/消除时调用

## Imports

无（纯 TypeScript，零外部依赖）

## 关键接口

```typescript
interface TrayItem {
  id: string;
  color: string;  // 或任意分组键
}

class MatchEngine {
  static insertToTray(item: TrayItem, tray: TrayItem[]): { newTray: TrayItem[]; insertIndex: number };
  static checkMatch(tray: TrayItem[], matchCount?: number): TrayItem[];
  static removeFromTray(tray: TrayItem[], matchedIds: string[]): TrayItem[];
  static isTrayFull(tray: TrayItem[], capacity: number): boolean;
}
```

## 配置

| 参数 | 默认 | 说明 |
|------|------|------|
| matchCount | 3 | 消除所需的同类数量 |
| groupKey | `item.color` | 分组依据字段 |

## 使用示例

```typescript
// 插入
const { newTray, insertIndex } = MatchEngine.insertToTray(item, tray);

// 检测匹配
const matched = MatchEngine.checkMatch(newTray);
if (matched.length > 0) {
  const finalTray = MatchEngine.removeFromTray(newTray, matched.map(m => m.id));
}

// 满溢检查
if (MatchEngine.isTrayFull(newTray, 7)) {
  // 游戏失败
}
```
