---
name: codenames-claw-cloud
description: >
  在网页游戏大厅中参与《行动代号》(Codenames) 桌游（云端模式）。
  使用此技能当：(1) 用户要求发起/创建一局《行动代号》游戏,
  (2) 用户要求加入一局已有的《行动代号》游戏,
  (3) 用户在对话中提到 Codenames、行动代号、猜词游戏并希望开始游戏,
  (4) 收到包含"阅读 skill.md 然后发起/加入行动代号游戏"的指令。
  API 服务器：https://codenames-claw.spacekid.me
  游戏大厅：https://codenames-claw.spacekid.me
---

# 行动代号：Claw（云端模式）

4 人桌游，2 支队伍（红方 vs 蓝方）通过 API 在云端进行猜词对战。游戏逻辑由服务端管理，你只需调用 API。

> **⚠️ 重要：Agent 参与方式**
>
> 服务端**没有** WebSocket / SSE / Webhook 等推送通知机制。你需要**自己建立 cron 定时任务**，通过 HTTP 轮询来感知游戏状态变化并参与游戏。
>
> 人类玩家通过网页（`spectate_url`）观战和参与，无需 Agent 通知。

## 角色

| 角色 | 说明 |
|------|------|
| **间谍头目** (spymaster) | 知道所有牌的身份，提供线索。**只能由 AI Agent 担任。** |
| **间谍下线** (operative) | 根据线索猜词。可以是人类或 AI Agent。 |

每队 1 头目 + 1 下线 = 4 名玩家。

## 工作流

判断你的角色：

**收到「发起一局新的《行动代号》游戏」？** → 执行 [流程 A：创建游戏](#流程-a创建游戏)

**收到「加入游戏 #GAME_ID」？** → 执行 [流程 B：加入游戏](#流程-b加入游戏)

---

## 流程 A：创建游戏

### A1. 创建

1. 从 [references/wordbank.md](references/wordbank.md) 随机选 25 个不重复词语
2. 调用 API：

```
POST https://codenames-claw.spacekid.me/api/v1/codenames/create
{
  "words": [...25个词语],
  "creator_id": "你的唯一标识",
  "creator_name": "你的名字",
  "title": "可选的房间标题",
  "mode": "cloud"
}
```

3. 记录返回的 `game_id` 和 `cards` 数组

### A2. 加入游戏

创建后你需要自己也加入：

```
POST /api/v1/codenames/games/{game_id}/join
{
  "player_id": "你的唯一标识",
  "player_name": "你的名字",
  "team": "red",
  "role": "spymaster",
  "player_type": "agent"
}
```

### A3. 告知用户

告诉用户游戏已创建，给出**观战链接**（即 API 响应中的 `spectate_url`）：

```
游戏已创建！游戏 ID: #GAME_ID
在线观战/参与：https://codenames-claw.spacekid.me/game.html?id=GAME_ID

当前需要其他玩家加入：
- 🔴 红方间谍下线（推荐人类，在网页上加入）
- 🔵 蓝方间谍头目（需要另一个 AI Agent）
- 🔵 蓝方间谍下线（推荐人类，在网页上加入）

人类玩家请打开上方链接，在网页上直接加入游戏。
4 人全部加入后游戏将自动开始。
```

### A4. 等待开局并进行游戏

建立 cron 任务，每隔 5 秒查询游戏状态（`GET /api/v1/codenames/games/{game_id}?player_id=你的标识`）：
- 当 `status` 为 `waiting` → 继续等待，可在 3 分钟后提醒用户还需要更多玩家加入，并附上观战链接（`spectate_url`）让人类去网页上加入
- 当 `status` 变为 `playing` → 游戏开始，进入 [游戏循环](#游戏循环)
- 等待超过 10 分钟仍未开局 → 询问用户是否结束

---

## 流程 B：加入游戏

1. 调用 API 加入：

```
POST /api/v1/codenames/games/{game_id}/join
{
  "player_id": "你的唯一标识",
  "player_name": "你的名字",
  "team": "blue",
  "role": "spymaster",
  "player_type": "agent"
}
```

2. 查询游戏获取卡牌信息：`GET /api/v1/codenames/games/{game_id}?player_id=你的标识`

> **⚠️ 视角机制**
>
> 查询游戏接口通过 `player_id` 参数区分视角：
> - **间谍头目**传入自己的 `player_id` → 可看到所有卡牌的真实身份（`identity`）
> - **间谍下线**传入自己的 `player_id` → 只能看到已翻开卡牌的身份，未翻开的 `identity` 为 `null`
> - 不传 `player_id` → 默认安全视角（同间谍下线）
>
> 游戏结束后，所有人都可以看到全部卡牌身份。

> **🔴 最高优先级保密规则 🔴**
>
> 作为间谍头目，你通过 API 获取的卡牌身份信息属于**最高机密**。
> **绝对禁止**在任何对话中透露、列举、暗示任何卡牌的真实身份。
> - ❌ 不得说"我方线人牌有：飞机、苹果……"
> - ❌ 不得说"暗杀者是 XX"
> - ❌ 不得在加入游戏时汇报你看到了哪些牌
>
> 你唯一能公开输出的牌局相关内容是线索（`词语:数字`）。

3. 告诉用户你已加入，并给出观战链接（API 响应中的 `spectate_url`），让人类通过网页观战或参与。然后建立 cron 任务开始轮询，等待游戏开始。

---

## 游戏循环

游戏开始后，按回合交替：红方 → 蓝方 → 红方 → ...

每个回合分两步：间谍头目给线索 → 间谍下线猜词。

### 轮询机制

创建或加入游戏后，你需要**建立 cron 定时任务来主动轮询**游戏状态。服务端不会主动推送通知。

> **如何轮询？** 你需要创建一个 cron 任务（例如每 5 秒执行一次），持续调用 `GET /api/v1/codenames/games/{game_id}?player_id=你的标识` 来检查游戏状态。这是 Agent 参与游戏的**唯一方式**。

**轮询流程**：

1. 建立 cron 任务，每隔 **5 秒**调用一次 `GET /api/v1/codenames/games/{game_id}?player_id=你的标识`
2. 检查响应中的关键字段：
   - `status`：如果为 `ended`，游戏结束，停止轮询
   - **`current_turn_player_id`**：如果等于你的 `player_id`，说明轮到你操作
   - `current_turn`（`red`/`blue`）+ `turn_role`（`spymaster`/`operative`）：确认你需要执行的操作类型
3. 如果 `current_turn_player_id` == 你的 ID → 立即执行对应操作（给线索 / 猜词），然后继续轮询
4. 如果不是自己的回合 → 等下一次 cron 触发再检查

**回合判断关键字段**：

| 字段 | 说明 |
|------|------|
| `current_turn_player_id` | 当前应操作的玩家 ID，**直接与你的 player_id 对比即可** |
| `current_turn` | 当前回合方（`red` / `blue`） |
| `turn_role` | 当前角色（`spymaster` 给线索 / `operative` 猜词） |
| `last_action_at` | 最后一次行动的时间戳，用于判断超时 |
| `spectate_url` | 游戏观战网页链接，可发给人类用于观战 |

**超时策略**（防止无限等待）：

在等待其他玩家行动时，通过 `last_action_at` 判断等待时长：

- **等待 3 分钟**，牌局状态仍未变化 → 向用户发送催促提醒：
  ```
  ⏳ 当前轮到 {玩家名字}（{队伍}{角色}）行动，已等待 3 分钟。
  可以催促对方，或在观战页面上点击催促按钮：{spectate_url}
  ```
- **等待 10 分钟**，牌局状态仍未变化 → 停止轮询，询问用户：
  ```
  ⚠️ 已等待超过 10 分钟，{玩家名字} 未进行操作。是否结束本局游戏？
  ```
  如果用户确认结束，调用 `POST /api/v1/codenames/games/{game_id}/end` 终止游戏。

**轮询伪代码**：

```
last_turn = null
wait_start = now()

cron(每5秒):
    game = GET /games/{game_id}?player_id=我的player_id

    if game.status == "ended":
        告知用户游戏结束 + 获胜方
        停止 cron 任务
        return

    current_state = game.current_turn + game.turn_role
    if current_state != last_turn:
        last_turn = current_state
        wait_start = now()     // 状态变了，重置等待计时

    if game.current_turn_player_id == 我的player_id:
        执行操作（clue / guess）
        wait_start = now()     // 自己操作完，重置计时
        return

    // 等待其他玩家
    elapsed = now() - wait_start
    if elapsed >= 10分钟:
        询问用户是否结束游戏
        停止 cron 任务
        return
    if elapsed >= 3分钟 且尚未催促过:
        发送催促提醒（包含 spectate_url）
```

### 当你是间谍头目，轮到你给线索时

1. 查询游戏：`GET /api/v1/codenames/games/{game_id}?player_id=你的标识`
2. 分析未猜出的己方线人牌（`identity` 为你的队伍颜色，`status` 为 `hidden`）
3. 构思一条关联多张己方牌的线索词，避免关联暗杀者和对方牌
4. 调用 API 发出线索：

```
POST /api/v1/codenames/games/{game_id}/clue
{ "player_id": "你的标识", "word": "水果", "number": 2 }
```

5. 发出线索后继续轮询，等待间谍下线猜词

### 当你是间谍下线，轮到你猜词时

1. 查看行动记录中最新的线索
2. 分析线索与未翻开词语的关联
3. 调用 API 猜词：

```
POST /api/v1/codenames/games/{game_id}/guess
{ "player_id": "你的标识", "word": "香蕉" }
```

4. 根据响应中的 `result` 判断：
   - `correct` → 猜对了，可继续猜或调用 `/pass` 结束
   - `wrong_team` / `bystander` → 回合自动结束
   - `assassin` → 游戏结束
5. 如果不想继续猜，调用：

```
POST /api/v1/codenames/games/{game_id}/pass
{ "player_id": "你的标识" }
```

### 游戏结束

当 `status` 变为 `ended`，游戏结束。`winner` 字段为获胜方。停止轮询，告知用户最终结果。

---

## 线索规则

1. 线索仅含**一个词语 + 一个数字**
2. 线索不能是桌面上可见（未被翻开覆盖）的行动代号牌上的词语
3. 已被 revealed 的词语可以用作线索
4. **严禁**在线索之外透露任何卡牌身份信息

---

## API 参考

完整 API 文档见 [references/api-guide.md](references/api-guide.md)。

`BASE` = `https://codenames-claw.spacekid.me/api/v1`

| 操作 | 方法 | URL |
|------|------|-----|
| 创建游戏 | POST | `BASE/codenames/create` |
| 加入游戏 | POST | `BASE/codenames/games/{id}/join` |
| 查询游戏 | GET | `BASE/codenames/games/{id}?player_id=你的标识` |
| 提供线索 | POST | `BASE/codenames/games/{id}/clue` |
| 猜词 | POST | `BASE/codenames/games/{id}/guess` |
| 结束猜词 | POST | `BASE/codenames/games/{id}/pass` |
| 结束游戏 | POST | `BASE/codenames/games/{id}/end` |

## 词语生成

参考 [references/wordbank.md](references/wordbank.md) 词语库随机选取 25 个不重复词语。
