# AI 方案

> 一个方案就是一套完整的 AI。在远见里一键切换，正在打的这一局也能立刻换人接管；导出 zip 分享给别人，导入别人的方案来测，每个方案的战绩自动统计。

来源: https://war3ai.com/docs/schemes/

一个**方案** = 一套完整的 AI：一个文件夹 + 一份说明书 `scheme.json` + 代码。每个游戏实例选一个方案；在远见里一键切换，**正在打的这一局也能立刻换人接管**。

别人分享的方案导入进来是**单独的一块**，和你自己的方案互不影响；想改就「复制到我的」。

```text
schemes/
  mine/<id>/          我的方案：自己写的，或从别的方案复制来改的（随便改，下一局生效）
  installed/<id>/     已安装：别人分享的 zip 解开在这里（第一次运行前要确认信任）
brains/xwar3/         内置：参考大脑（完整 AI）
brains/examples/      内置：四个教学示例 hello / rush / macro / micro，玩伴示例 buddy，两个玩法模组（英雄肉鸽、无尽守城）
```

方案不一定是替你打的 AI：`kind: mod` 的方案是一套**玩法规则**，你自己玩，它出题，见 [玩法模组](https://war3ai.com/docs/mods/)。

## 在远见里用

「AI 方案」页（左边栏「系统 → AI 方案」）：

| 操作 | 做什么 |
|---|---|
| 导入方案（zip） | 装进 `installed/`；同一个 id 已经装过会问要不要替换（替换后要重新确认信任） |
| 用到实例… | 选实例 +「立刻生效」（停掉当前 AI，新方案接管这一局）或「下次开始测试生效」 |
| 复制到我的 | 拷一份到 `mine/`，作者记成「我」、版本 0.1.0，并记下复制自哪个方案的哪个版本 |
| 导出 zip | 打包成 `<id>-<版本>.zip`，发给别人就是分享 |
| 打开文件夹 | 在资源管理器里打开方案目录，直接改代码 |
| 信任 | 别人的方案第一次运行前必须点（见下文「信任和安全」） |
| 最近战绩 | 这个方案每一局的胜负、时长、结束原因 |
| 删除 | 只能删「我的」和「已安装」；有实例正在用的不让删 |

实例卡片上也多了一行「AI 方案」：下拉选方案 →「切换（立刻生效）」。实例没在跑时按钮叫「选定」，下次「开始测试」就按它起 AI。

## 说明书 scheme.json

```json
{
  "format": 1,
  "id": "fast-rush",
  "name": "三分钟速攻",
  "version": "1.2.0",
  "author": "某某",
  "description": "一句话说明这个 AI 打什么路子",
  "entry": "rush_bot.py",
  "class": "RushBot",
  "fair": true,
  "hz": 5,
  "races": ["human", "orc"],
  "license": "MIT"
}
```

| 字段 | 必填 | 说明 |
|---|---|---|
| `id` | ✔ | 小写字母、数字、`-`、`_`，2 ~ 41 个字符 |
| `entry` | ✔ | 方案目录里的一个 `.py` 文件（不许绝对路径，不许 `..`） |
| `kind` | | 默认 `bot`（`openwar3.Bot` 的子类，替你打）；`mod` = [玩法模组](https://war3ai.com/docs/mods/)（`openwar3.Mod` 的子类，固定不走公平模式、不按对战规则判胜负） |
| `class` | | 入口文件里的 Bot（或 Mod）子类名；不写就取入口文件里最后一个 `openwar3.Bot` 子类 |
| `fair` | | 默认 `true`：只看得见视野里的东西，和对战平台同一个规则。`false` = 全图可见，也才能用 [JASS 通道](https://war3ai.com/docs/jass/)（玩伴需要） |
| `judge` | | 默认 `true`：按对战规则判胜负。RPG / 玩伴方案写 `false` |
| `hz` | | `on_tick` 每秒调几次，默认 5 |
| `format` | | 说明书格式版本，现在是 1；比本机 OpenWar3 新的会被拒绝并提示更新 |
| 其余 | | `name`、`version`、`author`、`description`、`races`、`license`、`homepage`、`forked_from` 只用于展示 |

方案目录会加进 Python 的模块搜索路径，入口文件可以 `import` 同目录下的其他文件。第三方包（numpy、torch……）不会自动安装 —— 在 `description` 里写清楚需要什么。

**最小的方案，两个文件就够**：

```python
# my_bot.py
from openwar3 import Bot

class MyBot(Bot):
    def on_tick(self, g):
        for w in g.idle_workers():
            mine = g.nearest(g.gold_mines(), w)
            if mine:
                g.gather(w, mine)
```

```json
{"id": "my-first", "name": "我的第一个 AI", "entry": "my_bot.py"}
```

放进 `schemes/mine/my-first/`，远见刷新一下就能看见。更省事的起点：在「内置」里挑一个示例，点「复制到我的」。

## 运行方式和战绩

方案由**方案运行器**跑（远见的「开始测试 / 切换」起的就是它）：

```bash
python tools/run_scheme.py --inst 20 --scheme builtin/micro --hours 6
```

- 每个实例一个常驻的监督进程，**每一局起一个子进程**跑方案：方案代码崩了不连累监督进程；「我的方案」改了代码，下一局自动用新的。
- 每局结束记一行战绩：方案、版本、作者、胜负、原因、游戏时长、出错次数。远见里的胜率就从这里统计。

胜负怎么判：

| 情况 | 记成 |
|---|---|
| 对面建筑全没了 | 胜 |
| 我方建筑全没了（兵还活着也算 —— 对战就是这么判负的） | 负 |
| 我方单位全没了 | 负 |
| 在远见里手动结束 / 停止 | 未定 |
| 切换时这局已经打了 60 游戏秒以上（半路接手） | 单独计数，**不进胜率** |
| 游戏时钟长时间不走 | 未定 |

分出胜负后运行器会关掉结算画面，按「下一局设置」开下一局，方案接着接管 —— 可以挂一整夜攒战绩。暂停不算结束：暂停期间 Bot 照常运行，只有游戏时钟停住。

## 信任和安全

**方案就是代码，运行时拥有和你本人一样的权限**（能读写文件、能联网）。所以：

- `installed/` 里的方案默认**不被信任**，远见和运行器都拒绝运行，直到你点「信任」；
- 替换安装同一个 id 的方案会**重置信任**（新版本等于新代码）；
- 导入时会检查：zip 不超过 50 MB、不超过 2000 个文件；不许绝对路径和 `..`（防止写到方案目录外面）；说明书不合法或入口文件不存在直接拒绝。

> **注意**
>
> 信任之前先「打开文件夹」读一遍代码。只从你信得过的人那里拿方案。

## 接口（给脚本用）

| 接口 | 说明 |
|---|---|
| `GET /api/schemes` | 方案列表 + 战绩 + 各实例选的、正在跑的方案 |
| `GET /api/schemes/results?ref=` | 一个方案最近 30 局 |
| `POST /api/schemes/import` | 导入 zip |
| `GET /api/schemes/export?ref=` | 下载 zip |
| `POST /api/schemes/fork` | 复制到我的 |
| `POST /api/schemes/trust` | 信任 |
| `DELETE /api/schemes?ref=` | 删除（有实例在用时拒绝） |
| `POST /api/instances/{n}/scheme` | 给实例换方案：立刻接管这一局，或下次开始测试生效 |

Python 里直接用库：`from openwar3 import schemes`（`list_schemes`、`install_zip`、`export_zip`、`fork`、`trust`、`stats`……）。

## 以后：方案网站

导出的 zip 就是分享的单位，网站只需要在外面加一层：在远见里一键上传；在网站上下载，走和「导入方案」完全相同的检查、同样要确认信任；可以选择上报战绩，网站按版本汇总胜率。远见里「分享到方案网站」的按钮已经留好了位置。进度见 [路线图](https://war3ai.com/roadmap/)。
