ШІ — профілі комп'ютерних суперників
Джерело: coreprime/kbot @ 0ab97bf · Редагувати на GitHub
Файли в
ai/*.txt— це текстові профілі комп'ютерних суперників. Кожен профіль оголошує пріоритети будівництва й ліміти юнітів для кожного рівня складності; рушій читає їх, коли для мапи налаштовано гравця-ШІ. Ключaiprofile=в OTA визначає, з яким профілем стартує мапа, але користувач може змінити його в лобі.
Tip
Спробуйте самі.
# Plain-text — just read them
ls $(kbot ctx path)/ai/
head -40 $(kbot ctx path)/ai/default.txt
У kbot. Оглядач ресурсів у студії показує профіль на вкладці
AI Profile, а kbot mount виводить його командою describe ai/<name>.txt
(див. Що показує kbot).
З Go. Скористайтеся пакетом formats/ai з kbot-io:
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
Коротко
Профіль — це послідовність планів, по одному на кожен рівень
складності. Кожен план містить директиви 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 |
Починає новий блок плану. Рядки Weight/Limit, що йдуть далі, аж до наступного plan застосовуються лише тоді, коли план відповідає складності гри. |
Weight <target> <value> |
Weight ARMCK 0.5 |
Множить пріоритет будівництва цілі (див. як поєднуються ваги). |
Limit <target> <value> |
Limit ARMSILO 1 |
Обмежує, скільки юнітів цілі може мати комп'ютерний гравець: -1 — без обмежень, 0 або будь-яке інше від'ємне значення забороняє ціль. |
Як TA 3.1c читає текст (пакет
formats/ai
з kbot-io повторює цю поведінку):
- Рядок ділиться на слова за будь-якими пробільними символами —
пробілами, табуляціями, CR, VT, FF, — а
#завершує рядок, де б він не стояв, навіть усередині слова. - Перше слово — це директива, яка порівнюється без урахування
регістру. Рядок, перше слово якого будь-яке інше, ігнорується; саме так
пропускаються рядки
// commentу релізних файлах. Слова після значення теж ігноруються, тож кінцева// noteнічому не шкодить. - Значення — це числовий префікс третього слова. Вага — десяткове
число (
0.5,.5,2,1e1); ліміт — ціле число, що загортається до 32 бітів. Слово значення, яке не є числом, читається як 0, і директива все одно застосовується:Limit CORFORT O(літера O) у релізномуkrogoth.txtзабороняє CORFORT, аLimit ARM DECOM 4обмежує всю категорію ARM до 0. - Рядки перед першим рядком
planпід час старту гри ігноруються, бо жоден план ще не підійшов. Вони можуть застосуватися, якщо профіль перезавантажується під час гри. Релізнийkrogoth.txtпочинається з двох таких рядків (weight cormakr 0.2,weight armmakr 0.2). Файли ШІ в TA: Kingdoms узагалі не використовуютьplan— див. TA: Kingdoms — профілі без plan нижче.
Рівні складності
Стандартні профілі постачаються з трьома планами: easy, medium і
hard. План застосовується на певному рівні складності, коли одне з
його слів (без урахування регістру) називає цей рівень — plan medium hard охоплює обидва, — або коли його перше слово any. План, слова
якого не називають жодного рівня складності (plan brutal, голий
plan), не підходить ні до чого: рядки під ним ігноруються до
наступного рядка plan.
Імена юнітів і слова категорій
І Weight, і Limit можуть мати ціллю:
UnitNameюніта — наприклад,ARMCOM,CORRAID(порівнюються без урахування регістру). Директива застосовується лише до цього юніта й фіксує його: пізніші рядкиWeight(для ваги) чиLimit(для ліміту) його вже не змінюють.- Слово категорії —
ARM,CORE,PLANT,CONSTR,LEVEL3,SPECIAL, … — що зіставляється зі словами поляCategory=кожного юніта. Воно застосовується до кожного відповідного юніта, який не зафіксовано. ALL— кожен юніт, який не зафіксовано.
Кожен юніт починає зі 100 % пріоритету й без ліміту. Вага множить
поточний відсоток, відкидає дробову частину й обмежує результат
діапазоном 0–100 %, тож вага понад 1 може повернути юніт, знижений
раніше, але ніколи не підніме його вище 100 %. Ліміт замінює поточний. У
плані easy з default.txt рядок 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
Як це читати: на легкому рівні складності кожен юніт 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.
TA: Kingdoms — профілі без plan
Note
Цей розділ описує лише відмінності TA:K. TAK використовує ту саму
домовленість про імена файлів ai/*.txt і той самий синтаксис директив
weight/limit, але вважає директиву plan необов'язковою.
Кожен релізний профіль ШІ TA: Kingdoms (ai/default.txt, набір
ai/mission*.txt кампанії) перелічує директиви weight/limit
без жодного рядка plan — на кожен файл припадає лише один неявний
рівень складності. Структура така: довгий блок ваг, за ним — довгий блок
лімітів, згрупованих за сторонами за префіксом імені юніта.
Розібраний приклад — перші 30 рядків ai/default.txt
// 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 Dragon навмисно має завищену вагу; ШІ будуватиме драконів охочіше, ніж будь-що інше.arabuild 10,aracastl 10,arakeep 10,arapries 10— кожен економічний/будівельний тип має вагу 10.arawall 0,arangate 0— явний нуль, ніколи не будуються.araat 1,arafast 1,arassh 1— базові юніти, яким ШІ знижує пріоритет на користь альтернатив вищого рівня.
Та сама схема повторюється далі у файлі для юнітів 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 веде будівництво до
юніта-героя: висока вага змушує збудувати його рано, а низький ліміт
тримає склад армії в розумних межах.
Статистика профілів
default.txt і файли місій кампанії мають однакову структуру:
| Профіль | Рядки weight | Рядки limit | Плани |
|---|---|---|---|
ai/default.txt |
104 | 104 | 0 |
ai/mission06.txt |
104 | 104 | 0 |
ai/mission08.txt (місія Iron Plague) |
161 | 162 | 0 |
Файли місій Iron Plague мають багатші таблиці ваг і лімітів, бо
охоплюють юніти Creon (префікс CRE), яких немає в default.txt базової
гри.
Як kbot обробляє файли без plan
ai.Parse з kbot-io кладе кожен рядок перед першим рядком plan в
AIFile.Preamble — саме так із ними чинить TA (див. вище); для профілю
TA: Kingdoms ai.ParseWith(data, ai.ParseOptions{DefaultPlan: true}) читає їх в один неявний план, що підходить до кожного рівня
складності. Переглядачі kbot показують їх на окремій вкладці Before
first plan і повідомляють обидва факти: TA ігнорує їх під час старту
гри, а профілі TA: Kingdoms, у яких немає рядків plan, застосовують їх на
кожному рівні складності. kbot не моделює, як TA: Kingdoms застосовує
ваги понад 1.
ai.IsAIFile() дивиться на перше слово кожного рядка: файл
вважається профілем, якщо якийсь рядок починається з директиви,
записаної звичайним способом (рядок plan, що називає easy, medium,
hard або any, чи рядок weight/limit, значення якого починається
з числа). Так розпізнаються профілі TAK, у яких немає plan, і профілі,
розділені табуляціями, а .txt, де «weight» чи «limit» лише згадано в
тексті, за профіль не приймається.
Підводні камені
Warning
Гра нічого не повідомляє. Неправильно написане ім'я юніта
читається як категорія, що не відповідає жодному юніту, і нічого не
робить; значення, яке не є числом, читається як 0 (тож ліміт забороняє
ціль); рядки перед першим plan під час старту гри ігноруються. Перша
ознака зіпсованого профілю — зазвичай «ШІ поводиться дивно».
Переглядачі kbot позначають кожен із цих випадків (див. нижче).
Limit 0або будь-який від'ємний ліміт, крім-1, забороняє ШІ будувати цю ціль.-1знімає обмеження.#завершує рядок будь-де, навіть усередині слова; блокових коментарів немає.//працює лише тому, що рядок, перше слово якого не є директивою, ігнорується, а кінцеві слова після значення теж ігноруються.- Вага понад 1 ніколи не піднімає юніт вище 100 %. Вона може лише повернути юніт, знижений раніше вагою категорії.
- Перший рядок, що називає юніт, фіксує його. Ставте рядки для окремих юнітів після рядків категорій, які вони мають уточнювати.
- Пробільні символи між токенами мають значення лише як роздільник: табуляції, пробіли, кілька пробілів поспіль працюють однаково.
- Вкладених або спільних секцій немає. Кожен блок
planмає заново оголосити кожну потрібну йому вагу чи ліміт.
Що показує kbot
Оглядач ресурсів у студії (вкладка AI Profile) і describe у
kbot mount читають профіль так само, як гра:
- Before first plan — рядки преамбули з позначкою, що під час старту гри вони ігноруються.
- По одній вкладці на план; кожну ціль позначено як unit,
category або all units відповідно до
units/*.fbiінсталяції (ім'я йCategory=). Категорію, якої немає в жодного юніта, — а саме так читається неправильно написане ім'я юніта, — позначено як matches no unit. Ваги показано як множники зі смугою, що показує, скільки відсотків лишається в юніта, який мав 100 %; ліміти — як ∞ Unlimited, Disabled (0 або будь-яке від'ємне значення, крім -1) чи Max: N. - Рядки, які гра читає інакше, — діагностика kbot-io, наприклад
слово значення, яке не є числом (
Oчитається як 0), або слова після значення. Якщо слово значення не збігається з числом, яке читає гра, воно показується поруч (written "O"); інший запис того самого числа, як-от.1чи0.10, — ні. - At game start (у студії;
kbot mountвиводить кількості) — для easy, medium і hard кожен юніт, вагу чи ліміт якого змінює профіль, із 🔒 біля значень, зафіксованих рядком юніта.
Типові розміри
| Показник | Діапазон у профілях Cavedog |
|---|---|
| Розмір файлу | 1–4 КБ |
| Планів у файлі | 3 (easy/medium/hard) |
| Директив weight на план | 20–80 |
| Директив limit на план | 0–10 |