AI——电脑对手配置文件
ai/*.txt中的文件是纯文本的电脑对手配置文件。每个配置文件按难度声明建造优先级和单位上限;为一张地图设置 AI 玩家时,引擎会读取它们。OTA 的aiprofile=键决定一张地图开局时使用哪个配置文件,但用户可以在大厅中覆盖它。
Tip
自己试试。
# Plain-text — just read them
ls $(kbot ctx path)/ai/
head -40 $(kbot ctx path)/ai/default.txt
在 kbot 中。 studio 的资源浏览器在 AI Profile 标签页中显示配置文件,kbot mount 则用 describe ai/<name>.txt 打印它(见 kbot 显示什么)。
在 Go 中调用。 使用 kbot-io 的 formats/ai:
import "github.com/coreprime/kbot-io/formats/ai"
raw, _ := os.ReadFile("ai/default.txt")
profile, _ := ai.Parse(raw)
// profile.Preamble: lines before the first plan; profile.Plans: plan.Name,
// plan.Weights, plan.Limits; profile.Diagnostics: lines read unusually.
settings := ai.NewResolver(units).Apply(profile, ai.Easy) // per-unit result
概览
一个配置文件是一串 plan,每个难度级别一个。每个 plan 包含 Weight 和 Limit 指令,其目标是单个单位名(ARMCOM)、一个类别词(PLANT、LEVEL3、SPECIAL)或 ALL。
plan easy
Weight ARM 0.2
Weight CORE 0.2
Weight PLANT 2
Weight CONSTR 3
Weight ARMACK 2
Weight CORACK 2
Limit ARMSILO 1
Limit CORSILO 1
plan medium
Weight ARM 1.0
Weight CORE 1.0
...
指令
| 指令 | 语法 | 含义 |
|---|---|---|
plan <word>… |
plan easy |
开始一个新的 plan 块。其后的 Weight/Limit 行只在该 plan 与游戏难度匹配时生效,直到下一个 plan 为止。 |
Weight <target> <value> |
Weight ARMCK 0.5 |
用这个值乘以目标的建造优先级(见权重如何组合)。 |
Limit <target> <value> |
Limit ARMSILO 1 |
限制电脑玩家拥有该目标的数量:-1 表示不限,0 或其他任何负值表示禁止。 |
TA 3.1c 如何读取这些文本(kbot-io 的 formats/ai 遵循同样的规则):
- 一行按任意空白字符分割成词——空格、制表符、CR、VT、FF——而且
#无论出现在哪里都会结束该行,即使在一个词的中间。 - 第一个词是指令,比较时不区分大小写。第一个词是其他任何内容的行都会被忽略;零售版中的
// comment行就是这样被跳过的。值后面的词也会被忽略,所以结尾的// note无害。 - 值是第三个词的数字前缀。 权重是一个十进制数(
0.5、.5、2、1e1);上限是一个回绕到 32 位的整数。不是数字的值词读作 0,而指令照样生效:零售版krogoth.txt中的Limit CORFORT O(字母 O)禁止了 CORFORT,而Limit ARM DECOM 4把整个 ARM 类别的上限设成了 0。 - 第一行
plan之前的行在游戏开始时会被忽略,因为此时还没有任何 plan 匹配。如果在游戏过程中重新加载配置文件,它们可能会生效。零售版krogoth.txt开头就有两行这样的内容(weight cormakr 0.2、weight armmakr 0.2)。《横扫千军:王国》的 AI 文件根本不使用plan——见下文横扫千军:王国——没有 plan 的配置文件。
难度级别
原版配置文件带有三个 plan,分别名为 easy、medium 和 hard。当 plan 的某个词(不区分大小写)指明某个难度时——plan medium hard 同时涵盖两者——或者它的第一个词是 any 时,该 plan 就在那个难度下生效。词中没有指明任何难度的 plan(plan brutal、单独一个 plan)不匹配任何难度:其下的行会被忽略,直到下一个 plan 行为止。
单位名与类别词
Weight 和 Limit 都可以以下列内容为目标:
- 单位的
UnitName——例如ARMCOM、CORRAID(比较时不区分大小写)。该指令只作用于这一个单位,并将其锁定:之后的Weight行(对权重而言)或Limit行(对上限而言)不再改变它。 - 类别词——
ARM、CORE、PLANT、CONSTR、LEVEL3、SPECIAL……——与每个单位Category=字段中的词进行匹配。它作用于每个匹配且未被锁定的单位。 ALL——每个未被锁定的单位。
每个单位起始时优先级为 100 %,且没有上限。权重会乘以当前的百分比,截断为整数,并限制在 0–100 % 之间,所以大于 1 的权重可以恢复先前被降低的单位,但永远不会让它超过 100 %。上限则直接替换当前值。在 default.txt 的 easy plan 中,Weight ARM 0.2 让每个 Arm 单位都变成 20 %;随后的 Weight ARMRAD 0.25 把 Arm 雷达塔降到 5 % 并锁定它;Weight PLANT 2 则把每座 Arm 工厂从 20 % 拉回到 40 %。
Important
类别别名是与单位的 Category= 词表匹配,而不是与任何分类文件匹配。 只有至少一个单位声明了某个类别别名,它才“存在”。把它拼错成 LEVL3,这条指令就会悄无声息地什么也不做(kbot 的查看器会把它标为 matches no unit,即不匹配任何单位)。
实例解析——default.txt 的片段
// DEFAULT PROFILE
//--------------------------- EASY
plan easy
Weight ARM 0.2 // Every Arm unit to 20%
Weight CORE 0.2
Weight ARMRAD 0.25 // Radar towers to 5%, locked
Weight CORRAD 0.25
Weight ARMMAKR .1 // …metal makers to 2%, locked
Weight CORMAKR .1
Weight PLANT 2 // Factories back up to 40%
Weight CONSTR 3 // Constructors to 60%
// encourages advanced units
Weight ARMACK 2 // Advanced Construction Kbots
Weight CORACK 2
Weight ARMACV 2
Weight CORACV 2
Weight ARMACA 2
Weight CORACA 2
Weight SPECIAL 2
Weight LEVEL3 2
解读:在 easy 难度下,每个 Arm 和 Core 单位的优先级都降到 20 %;雷达塔降到 5 %,金属转换器降到 2 %(两者随后都被锁定);工厂(PLANT)和建造者(CONSTR)回升到 40 % 和 60 %,而高级建造者各自的那几行把它们的 60 % 翻倍,达到 100 % 的上限并将其锁定。
地图专用的配置文件
Cavedog 发布了几个针对地图调校的配置文件:
| 配置文件 | 使用者 |
|---|---|
default.txt |
大多数普通地图 |
metal.txt |
金属丰富的地图(Metal Heck 之类) |
airbattle.txt |
以空战为主的地图 |
seabattle.txt |
以海战为主的地图 |
hover.txt |
气垫船为主的地图 |
acid.txt |
酸性世界的地图 |
urban.txt |
城市地图 |
waterwrld.txt |
水世界地图 |
krogoth.txt |
Krogoth 头目任务 |
missions.txt |
战役任务 |
地图的 .ota 通过 aiprofile= 选择其中之一,例如 aiprofile=metal。
横扫千军:王国——没有 plan 的配置文件
Note
本节只讲 TA:K 的差异。 TAK 使用相同的 ai/*.txt 文件名约定和相同的 weight/limit 指令语法,但把 plan 指令视为可选。
横扫千军:王国的每个零售版 AI 配置文件(ai/default.txt,以及战役用的 ai/mission*.txt 一组)列出的 weight/limit 指令根本没有任何 plan 行——每个文件只有一个隐式的难度。其结构是一长串权重块,后面跟着一长串上限块,按单位名前缀以阵营分组。
实例解析——ai/default.txt 的前 30 行
// Kingdoms Default AI Profile 3-28-99
weight araarch 5
weight araat 1
weight arabow 8
weight arabroad 5
weight arabuild 10
weight aracan 4
weight aracastl 10
weight araclay 5
weight aradrag 25 // Dragons: top priority
weight arafast 1
weight arakeep 10
weight araknigh 5
weight aralode 10
weight aramana 5
weight arangate 0 // 0 = never build
weight arapal 8
weight arapries 10
weight arapult 1
weight arasmith 8
weight araspy 4
weight arassh 1
weight arasword 5
weight aratre 1
weight arawall 0
weight arawar 1
扫一遍 default.txt 中的权重偏向,就能看出 Cavedog 的优先次序:
aradrag 25——Aramon 的龙被有意加大了权重;AI 会比建造其他任何东西都更积极地建造龙。arabuild 10、aracastl 10、arakeep 10、arapries 10——每一种经济/建造者类型的权重都是 10。arawall 0、arangate 0——明确设为零,从不建造。araat 1、arafast 1、arassh 1——AI 降低了优先级、转而偏好更高级替代品的基础单位。
同样的模式在文件后面针对 tar*、ver* 和 zon* 单位重复出现。
上限块
权重块之下是一个与之平行的上限块:
limit araarch 16
limit araat 8
limit arabow 16
limit arabroad 16
limit arabuild 10
limit aracan 3
limit aracastl 2 // Only 2 castles per game
limit araclay 3
limit aradrag 1 // ONE dragon, even though weight=25
limit arafast 4
limit arakeep 2
limit araknigh 16
…
注意这种不对称:aradrag 的权重是 25(高优先级),上限却是 1(永远不超过一个)。TA:K 的 AI 就是这样朝英雄单位发展的——高权重让它早早被建造出来,低上限则让军队构成保持合理。
配置文件统计
default.txt 和战役任务文件都遵循同样的结构:
| 配置文件 | Weight 行数 | Limit 行数 | plan 数 |
|---|---|---|---|
ai/default.txt |
104 | 104 | 0 |
ai/mission06.txt |
104 | 104 | 0 |
ai/mission08.txt(Iron Plague 任务) |
161 | 162 | 0 |
Iron Plague 的任务文件有更丰富的权重/上限表,因为它们涵盖了基础游戏的 default.txt 所没有的 Creon(CRE 前缀)单位。
kbot 如何处理没有 plan 的文件
kbot-io 的 ai.Parse 把第一行 plan 之前的每一行都放进 AIFile.Preamble,这正是 TA 对待它们的方式(见上文);对于横扫千军:王国的配置文件,ai.ParseWith(data, ai.ParseOptions{DefaultPlan: true}) 会把它们读进一个匹配所有难度的隐式 plan。kbot 的查看器在单独的 Before first plan 标签页中显示它们,并同时说明两件事:TA 在游戏开始时忽略它们,而没有 plan 行的横扫千军:王国配置文件则在每个难度下都应用它们。kbot 没有模拟横扫千军:王国如何应用大于 1 的权重。
ai.IsAIFile() 查看每一行的第一个词:只要某一行以按常规方式书写的指令开头(一行指明 easy、medium、hard 或 any 的 plan,或者值以数字开头的 weight/limit 行),这个文件就是配置文件。这样既能识别没有 plan 的 TAK 配置文件,也能识别用制表符分隔的配置文件,而只是在正文中提到 “weight” 或 “limit” 的 .txt 则不会被误认为配置文件。
坑点
Warning
游戏什么也不报告。 拼错的单位名会被当作一个不匹配任何单位的类别读取,什么也不做;不是数字的值读作 0(于是上限会禁止该目标);第一行 plan 之前的行在游戏开始时会被忽略。配置文件损坏的第一个迹象通常是“AI 行为古怪”。kbot 的查看器会逐一标出这些情况(见下文)。
Limit 0,或除-1以外的任何负数上限,都会禁止 AI 建造该目标。-1解除上限。#在任何位置都会结束一行,即使在一个词的中间;没有块注释。//之所以能用,只是因为第一个词不是指令的行会被忽略,而值后面多出的词也会被忽略。- 大于 1 的权重永远不会让单位超过 100 %。 它只能恢复先前被某个类别权重降低的单位。
- 第一个指明某单位的行会锁定它。 把针对单个单位的行放在它们要细化的类别行之后。
- 词与词之间的空白只起分隔作用——制表符、空格、多个空格效果都一样。
- 不支持嵌套或共享的节。 每个
plan块都必须重新声明它关心的每一条权重/上限。
kbot 显示什么
studio 的资源浏览器(AI Profile 标签页)和 kbot mount 的 describe 都按游戏的方式读取配置文件:
- Before first plan——前导部分的行,并注明它们在游戏开始时会被忽略。
- 每个 plan 一个标签页,每个目标都依据安装目录中的
units/*.fbi(名称和Category=)标为 unit(单位)、category(类别)或 all units(所有单位)。没有任何单位拥有的类别——拼错的单位名就会被读成这样——会被标为 matches no unit(不匹配任何单位)。权重显示为乘数,并附一个条形,表示一个从 100 % 出发的单位最终剩下的百分比;上限显示为 ∞ Unlimited(不限)、Disabled(禁用,即 0 或除 -1 以外的任何负值)或 Max: N(最多 N 个)。 - 游戏读法不同的行——kbot-io 的诊断信息,例如不是数字的值词(
O读作 0),或值后面的词。若值词不是游戏所读出的那个数,就会在旁边显示出来(written "O");同一个数的另一种写法,例如.1或0.10,则不会。 - 游戏开始时(studio;
kbot mount打印计数)——针对 easy、medium 和 hard,列出配置文件改动了其权重或上限的每个单位,被单位行锁定的值带有 🔒。
典型大小
| 指标 | 在 Cavedog 配置文件中观察到的范围 |
|---|---|
| 文件大小 | 1–4 KB |
| 每个文件的 plan 数 | 3(easy/medium/hard) |
| 每个 plan 的 Weight 指令数 | 20–80 |
| 每个 plan 的 Limit 指令数 | 0–10 |