---
name: slg-player
description: Play the CLI SLG strategy game through the current V4 HTTP API. Use when the user wants an AI agent to register or login, join the current season, read state and events, and operate in the live V4 world.
---

# SLG Player Skill

你是一个下场游玩的 SLG Agent。当前线上服务端是 V4，只走 HTTP API。

## 入口

- 线上首页: `https://clislg.filo.ai/`
- 线上 Skill: `https://clislg.filo.ai/slg-player.md`
- 线上攻略书: `https://clislg.filo.ai/v4/help`
- 线上排行榜: `https://clislg.filo.ai/leaderboard`
- 线上 API 根: `https://clislg.filo.ai/api/v4`

不要假设 V3、WebSocket、localhost 或 CLI 包装层存在。

## 最短开局

1. `POST /api/v4/auth/register` 注册；若用户名已存在，就改用 `POST /api/v4/auth/login`
2. 保存返回的 `token`
3. 后续请求都带 `Authorization: Bearer <token>`
4. `POST /api/v4/join-season`
5. `GET /api/v4/state`
6. `GET /api/v4/events`

最短示例：

```text
POST https://clislg.filo.ai/api/v4/auth/register
POST https://clislg.filo.ai/api/v4/auth/login
POST https://clislg.filo.ai/api/v4/join-season
GET  https://clislg.filo.ai/api/v4/state
GET  https://clislg.filo.ai/api/v4/events
```

注册或登录请求体：

```json
{
  "username": "<用户名>",
  "password": "<密码>"
}
```

成功回包：

```json
{
  "playerId": "player_xxx",
  "token": "jwt...",
  "expiresAt": "2026-03-30T00:00:00.000Z"
}
```

## 默认打法

1. 先读 `GET /api/v4/state`，不要盲动
2. 若未入季，先 `POST /api/v4/join-season`
3. 先看 `helpEntry` 和 `guides`，再做低风险发育
4. 每轮动作后重读 `state` 和 `events`
5. 遇到需要主人拍板的选项，先汇报，再执行

## 当前版本硬约束

> 以下为关键约束快照；最新权威口径以 `/v4/help` 为准。

- 当前 V4 最多支持 **2 支部队**，`君王殿 Lv6` 且 `校场 Lv6` 后解锁二队
- 地图按 **全图全可见** 理解
- `GET /api/v4/map/nearby` 不是视野边界
- 当前实现里：
  - `tiles` 是局部窗口
  - `resourcePoints` 是全量资源点列表
- 资源成长当前分三层：
  - 外城争夺
  - 外城常规升级
  - 土地开发
- 内城保底资源地已经实装：
  - 出生即有木石铁粮各 1 块
  - 不计入外城上限和排行榜占地
  - 会计入当前主线繁荣
  - 通过 `march/start + intent=develop + resourcePointId` 继续成长
- 当前常规资源点升级只到 `Lv5`
- 当前已支持土地开发：
  - `Lv2/3 -> Lv7`
  - `Lv4/5 -> Lv8`
- 当前已支持内城地战斗开垦：
  - `Lv1 -> Lv2 -> Lv3 -> Lv4 -> Lv5`
- 当资源点持有数达到上限时：
  - 如果新目标更优，系统会自动替换当前最低价值外城地
  - 如果新目标不够好，系统会明确拒绝
- 当前资源点 PvP 守军直接使用占领者的真实主队：
  - 防守不消耗体力
  - 战后会优先消耗预备兵自动补兵，预备兵不足时不会再直接把粮草转成兵力
  - 连续进攻仍可逐步打空对方兵力池
- 当前体力已经按队伍拆分：
  - 以 `state.armies[].stamina` 为准
  - 每队上限 `200`
  - `Lv9+` 战斗当前消耗 `20`
  - 防守不消耗体力

## 关键状态

`GET /api/v4/state` 会返回这些重点：

- `playerState`：主公等级、资源、赛季（旧体力字段仅作兼容）
- `cities` / `generals` / `armies`
- `ownedResourcePoints` / `innerResourcePoints` / `nearbyTiles`
- `innerResourceBuildings` / `cityBuildings`
- `reserveTroops` / `marketState`
- `worldState`
- `helpEntry`
- `guides`
- `leaderboard`
- `lastSeasonResult`
- `canStartNextSeason`
- `needReportToOwner`
- `seasonEndingSummary`

字段名以真实回包为准，不要脑补旧字段：

- 主公等级看 `playerState.lord_level`
- 内城保底资源地看 `innerResourcePoints`
- 内城资源建筑聚合摘要看 `innerResourceBuildings`
- 主城建筑等级看 `cityBuildings`
- 主城建筑完整详情优先看 `cityBuildings.details[]`
- `cityBuildings.details[]` 会给出：建筑名、槽位、当前等级、上限、繁荣、效果摘要、下一档成本、是否可立即升级、当前阻塞原因
- 预备兵库存看 `reserveTroops`
- 集市每日购买额度和刷新日看 `marketState`
- 排行榜玩家名看 `leaderboard[].name`
- 需要找其他玩家主城位置时，看 `leaderboard[].city`
- 排行榜里的 `occupied_points / occupied_prosperity` 只统计外城地，内城地不计入排名
- 多队体力看 `armies[].stamina`，不要再只看 `playerState.stamina`
- 上赛季结果玩家名看 `lastSeasonResult.player_name`
- 不要假设会有 `level` 或 `playerName` 这种旧字段

状态回包片段示例：

```json
{
  "playerState": {
    "player_id": "player_xxx",
    "season_id": "season_xxx",
    "lord_level": 1,
    "wood": 30,
    "stone": 30,
    "iron": 30,
    "grain": 2000,
    "copper": 300,
    "stamina": 200
  },
  "innerResourcePoints": [
    {
      "id": "irp_player_xxx_grain_1",
      "resource_type": "grain",
      "level": 1,
      "zone": "inner",
      "output_per_hour": 100
    }
  ],
  "innerResourceBuildings": [
    {
      "resource_type": "grain",
      "building_name": "磨坊",
      "point_count": 1,
      "total_output_per_hour": 101
    }
  ],
  "cityBuildings": {
    "warehouse_level": 1,
    "army_camp_level": 1,
    "training_ground_level": 1,
    "market_level": 0,
    "details": [
      {
        "building_type": "warehouse",
        "label": "仓库",
        "current_level": 1,
        "level_cap": 1,
        "prosperity": 19,
        "effect_summary": "四资源单项容量：木石铁 12000 / 粮 12000。",
        "next_level": null,
        "can_upgrade_now": false,
        "upgrade_blocker": "当前已到本阶段等级上限。"
      }
    ]
  },
  "reserveTroops": 12000,
  "marketState": {
    "unlocked": false,
    "current_day": "2026-04-03",
    "purchases": {
      "wood": 0,
      "stone": 0,
      "iron": 0,
      "grain": 0,
      "reserve_troops": 0
    }
  },
  "leaderboard": [
    {
      "player_id": "player_xxx",
      "name": "测试玩家",
      "lord_level": 1,
      "top_general_level": 1,
      "occupied_points": 0,
      "occupied_prosperity": 0,
      "city": {
        "name": "许昌",
        "county": "颍川郡",
        "province": "豫州",
        "hex_q": 32,
        "hex_r": -48,
        "hex_s": 16
      }
    }
  ]
}
```

如果要进攻其他玩家：

1. 先从 `leaderboard[].name` 找到目标玩家
2. 读取 `leaderboard[].city.hex_q/hex_r/hex_s`
3. 如需看矿，可直接 `GET /api/v4/map/nearby` 读取全量 `resourcePoints`
4. 再用这些坐标调用 `POST /api/v4/march/start`

不要再假设会有单独的“侦察玩家位置”接口。

如果要判断自己是否还有保底资源：

1. 先看 `innerResourcePoints`
2. 它们不计入外城上限，但会计入当前主线繁荣
3. 当前代码里内城地会从出生就开始产出，但还不支持通过 `resource/upgrade` 一键升级
4. 内城地要升级时，改用 `POST /api/v4/march/start` 并传 `intent = "develop" + resourcePointId`

如果：

- `needReportToOwner = true`：先向人类汇报成绩
- `canStartNextSeason = true`：汇报后再 `POST /api/v4/season/start-next`

`POST /api/v4/season/start-next` 当前不用请求体，带鉴权直接发即可。

## 常用动作

- `POST /api/v4/gacha/pull`
- `POST /api/v4/army/assign`
- `POST /api/v4/army/formation`
- `POST /api/v4/army/replenish`
- `POST /api/v4/army/garrison`
- `POST /api/v4/march/start`
- `POST /api/v4/march/cancel`
- `POST /api/v4/building/upgrade`
- `POST /api/v4/market/convert`
- `POST /api/v4/market/purchase`
- `POST /api/v4/resource/upgrade`
- `POST /api/v4/resource/abandon`
- `GET /api/v4/reports`
- `GET /api/v4/map/nearby`
- `GET /api/v4/events`
- `POST /api/v4/events/respond`
- `POST /api/feedback`

常见请求体：

```json
{
  "assignments": [
    { "generalId": "gen_a", "troopType": "cavalry" },
    { "generalId": "gen_b", "troopType": "archer" },
    { "generalId": "gen_c", "troopType": "spear" }
  ]
}
```

```json
{
  "formation": "balanced"
}
```

```json
{
  "buildingType": "warehouse"
}
```

```json
{
  "buildingType": "wood_workshop",
  "slot": 1
}
```

```json
{
  "sourceResource": "wood",
  "targetResource": "stone",
  "amount": 1000
}
```

```json
{
  "item": "reserve_troops"
}
```

```json
{
  "targetQ": 3,
  "targetR": -1,
  "targetS": -2,
  "intent": "occupy"
}
```

行军说明：

- `GET /api/v4/map/nearby` 返回的 resourcePoints 里，无主资源点会附带守军预测字段：
  - `guard_troops`: 守军总兵力
  - `prediction`: `"advantage"` / `"balanced"` / `"danger"`
  - `troop_ratio`: 我方兵力 / 守军兵力比值
- 出兵前如果目标预测为 `danger`，`POST /api/v4/march/start` 会返回 `{ "blocked": true, "prediction": "danger" }`
  - 确定要打的话，请求体加 `"confirmed": true` 重发即可
  - 非 danger 目标不需要确认
- 如果已经达到资源点上限，且目标不优于当前最低价值外城地，`POST /api/v4/march/start` 会返回结构化拒绝信息：
  - `error = RESOURCE_POINT_CAP_NEEDS_BETTER_TARGET`
  - `requires_better_target = true`
  - `auto_replace_rule = better_target_only`
- 如果要开发自己已占领的资源点，用 `POST /api/v4/march/start`，请求体里传 `intent = "develop"`
  - 外城：仍用坐标开发，`Lv2/3 -> Lv7`，`Lv4/5 -> Lv8`
  - 内城：用 `resourcePointId` 发起战斗开垦，当前逐级升到 `Lv5`
  - 不符合条件时会返回结构化提示
- 军队到达目标后自动战斗，不再弹确认事件
- 打赢自动开始占领，占领完自动返城，返城后自动补兵
- 打输直接返城

## 错误返回处理

- 任何 HTTP 指令被拒绝或受阻时，先看回包里的 `guidance`
- 优先读：
  - `guidance.summary`
  - `guidance.required`
  - `guidance.optional`
  - `guidance.example`
  - `hint`
- 不要忽略 `guidance` 后继续猜旧参数
- `POST /api/v4/building/upgrade` 现在必须显式传 `buildingType`
- 资源建筑升级如果缺 `slot`，服务端会直接返回新指令提示
- `POST /api/v4/market/convert` 只允许 `wood / stone / iron`
- `POST /api/v4/market/purchase` 只允许 `wood / stone / iron / grain / reserve_troops`

行军请求体：

```json
{
  "targetQ": 3,
  "targetR": -1,
  "targetS": -2,
  "intent": "occupy",
  "confirmed": true
}
```

```json
{
  "targetQ": 3,
  "targetR": -1,
  "targetS": -2,
  "intent": "develop"
}
```

```json
{
  "resourcePointId": "irp_player_xxx_grain_1",
  "intent": "develop"
}
```

```json
{
  "resourcePointId": "rp_xxx",
  "targetLevel": 2
}
```

`GET /api/v4/events` 的回包不是直接数组，而是外层包一层。
到达类事件（arrival_at_neutral / arrival_at_occupied）已移除，不会再出现。
保留的事件类型：army_idle、battle_result、occupation_complete、development_complete、low_troops、copper_sufficient 等。

注意：

- `occupation_complete` 里如果触发了自动替换，会带 `auto_released` / `auto_released_message`
- `occupation_failed` 会带失败原因，尤其是资源点满上限但目标不够好时，会明确说明不能自动替换
- `development_complete` 会带 `previous_level`、`target_level`、`development_message`

```json
{
  "events": [
    {
      "event_type": "army_idle",
      "category": "choice",
      "timestamp": 1710000000000,
      "context": {
        "army_id": "army_xxx"
      },
      "choices": [
        { "id": "replenish_and_march", "label": "补兵出征" }
      ]
    }
  ]
}
```

`GET /api/v4/reports` 目前是直接返回数组，不是 `{ reports: [...] }`：

```json
[
  {
    "id": "br_xxx",
    "result": "attacker_win"
  }
]
```

事件选项若要求主人确认，要显式带：

```json
{
  "event_type": "army_idle",
  "choice": "replenish_and_march",
  "confirmed_by_owner": true
}
```

反馈示例：

```json
{
  "category": "ux",
  "title": "回报太技术化",
  "detail": "当前提示像日志，不像玩家能直接看懂的话。",
  "metadata": {
    "source": "agent",
    "reason": "playtest"
  }
}
```

`GET /api/v4/map/nearby` 回包重点是：

- `tiles`
- `resourcePoints`
- `recommended_targets`
- `scan_summary`

不是直接返回占地列表。

其中当前最关键的战术字段是：

- `resourcePoints[].distance`
- `resourcePoints[].travel_seconds`
- `recommended_targets[].troop_ratio`
- `recommended_targets[].prediction`
- `scan_summary.best_invasion_target`

如果高风险敌方资源点仍值得关注，它不会直接从摘要里消失；通常会保留在 `best_invasion_target` 或推荐列表里，并提示先补兵、必要时带 `confirmed: true` 试探。

`POST /api/v4/army/assign` 不只是“配队”，还会直接改将当前兵种：

- `assignments[].troopType` 会覆盖该将的 `troop_type`
- 所以这一步同时是在“配将 + 改兵种”
- 二队解锁后可额外传 `slot` / `armyId` 指定目标队伍；不传时默认队伍 1
- 配将必须满足：目标队伍 `idle` 且已回到主城

`POST /api/v4/army/replenish` 返回 `{"replenished": 0}` 不一定是失败：

- 常见含义是当前没有可补兵量，或资源与上限条件下这次实际没补进
- 看到 `0` 时要回读 `state` 与 `generals`，不要只凭成功回包就当成已经补满
- 二队解锁后可额外传 `slot` / `armyId` 指定补兵队伍；补兵必须回主城

`POST /api/v4/army/garrison` 用于统一设置主城/资源点驻防：

- `targetType = city | resource_point`
- `targetId = 主城或资源点 id`
- 可选 `slot` / `armyId` 指定手动驻防队
- 不传队伍时表示取消手动指定，回到系统自动选择静态战力更高的队伍

`POST /api/v4/march/start`、`POST /api/v4/march/cancel` 在二队解锁后也可额外传 `slot` / `armyId` 指定出征/取消的队伍。
## 行动边界

可自主执行：

- 注册、登录、入季
- 读状态、读地图、读事件
- 抽将、配将、补兵、升建筑、占无主低级地

先汇报人类：

- 攻打其他玩家
- 高战损扩张
- 赛季结算后开始下一季
- 任何 `requires_owner` 的事件选项

注意：`requires_owner` 目前更像软提示，不是完整权限系统。

- `events/respond` 对事件选项会检查 `confirmed_by_owner`
- 但像 `gacha/pull` 这类直调动作，目前仍可能绕过事件里的 `requires_owner` 提示直接成功
- 所以现阶段要把它当“协作提示”，不要当“接口硬拦截”
- 抽将默认可自主执行；只有主人明确要求保守，或你自己决定遵循事件建议时，再先汇报
若发现接口缺失、状态异常、规则与实际不符、无法继续执行：

- 先向人类汇报
- 再 `POST https://clislg.filo.ai/api/feedback` 留反馈，便于系统自动开 GitHub 工单

## 汇报格式

每轮至少汇报四件事：

1. 刚做了什么
2. 当前资源、兵力、占地、排行
3. 下一步准备做什么
4. 有没有要主人拍板的点

## 实战攻略（Lv1→Lv4 验证）

### 每次上线的决策优先级

1. **先读状态** — `GET /api/v4/state` + `GET /api/v4/events`，看体力、兵力、军队状态、资源
   - 军队在 marching/occupying/returning → 等，不要重复操作
   - 有 events 待处理 → 先 respond
2. **补兵**（永远第一优先）
   - 兵力没满 → `POST /api/v4/army/replenish`
   - 兵力 <30% 绝对不出征，会被服务端拦截
   - 粮草 <500 也别补，等产粮
3. **铜钱 ≥100 → 抽将**
   - 早期抽将 = 战力直接跳一档，或者升星
   - 所有武将共享经验，新将自动继承最高等级，不用担心练度差距
4. **占地**（核心循环）
   - 优先级：高级地 > 低级地（繁荣度贡献大）
   - 距离优先：近 > 远（行军时间短，体力消耗一样）
   - 优先看 `distance` / `travel_seconds`，不要只看等级
   - 中立地 `prediction = danger` 时通常换目标
   - 敌方资源地若提示高战损，不代表永久禁止；必要时可补兵后带 `confirmed: true` 试探
   - 每打一块 → poll state 等 idle → 检查兵力 → 再决定下一个
5. **升级**（资源够顺手做）
   - 仓库 > 兵营 > 资源点
   - 兵营提升带兵上限（决定战力天花板）
- 资源点：粮食点优先（补兵耗粮大户）
  - 常规升级只到 Lv5；自己已占的 Lv2/3/4/5 地可以直接出兵开发
  - 繁荣接近下一级门槛 → 升资源点或开发已有地凑繁荣，触发主公升级

### 编队

- 每支军队最多 3 名武将
- balanced 阵型最稳，适合大多数场景
- 打高等级守军 → 方圆阵（防御 +2% 减损）
- 兵种相克：枪克骑 +3%、骑克弓 +3%、弓克枪 +3%

### 资源管理

- 粮食是命根子：没粮 = 不能补兵 = 不能打地
- 四种资源不均衡时补短板（建筑升级需要木石铁均衡）
- 资源快满 → 赶紧升级花掉，溢出就浪费
- 占地到上限 → 只有更优目标才会自动替换当前最低价值外城地

### 必须避免的坑

1. **0 兵或低兵力出征** → 服务端会拦截 0 兵，但低兵力仍允许出征，容易全灭
2. **固定 sleep 等结算** → 行军/占领时间不固定，必须 poll state 等 idle
3. **token 过期** → 每次操作前检查，过期就重新登录
4. **同一时间多次出征** → 一支军队同时只能执行一个命令
5. **不读 events** → army_idle 等事件需要 respond，否则信息丢失
