IA: perfis dos adversários controlados pelo computador
Fonte: coreprime/kbot @ 0ab97bf · Editar no GitHub
Os arquivos em
ai/*.txtsão perfis de adversários do computador em texto simples. Cada perfil declara prioridades de construção e limites de unidades por dificuldade; o motor os lê quando um jogador de IA é configurado num mapa. A chaveaiprofile=do OTA escolhe com qual perfil um mapa começa, mas o usuário pode trocá-lo no lobby.
Tip
Experimente você mesmo.
# Plain-text — just read them
ls $(kbot ctx path)/ai/
head -40 $(kbot ctx path)/ai/default.txt
No kbot. O explorador de recursos do studio mostra o perfil na aba
AI Profile, e o kbot mount o imprime com describe ai/<name>.txt
(veja O que o kbot mostra).
Em Go. Use o formats/ai do 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
Visão geral
Um perfil é uma sequência de plans, um por nível de dificuldade. Cada
plan contém diretivas Weight e Limit cujo alvo é um único nome de
unidade (ARMCOM), uma palavra de categoria (PLANT, LEVEL3, SPECIAL)
ou 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
...
Diretivas
| Diretiva | Sintaxe | Significado |
|---|---|---|
plan <word>… |
plan easy |
Abre um novo bloco de plan. As linhas Weight/Limit que vêm depois só valem quando o plan corresponde à dificuldade da partida, até o próximo plan. |
Weight <target> <value> |
Weight ARMCK 0.5 |
Multiplica a prioridade de construção do alvo (veja como os pesos se combinam). |
Limit <target> <value> |
Limit ARMSILO 1 |
Limita quantas unidades do alvo um jogador do computador pode ter: -1 é ilimitado, 0 ou qualquer outro valor negativo proíbe o alvo. |
Como o TA 3.1c lê o texto (o
formats/ai
do kbot-io segue o mesmo comportamento):
- A linha é dividida em palavras em qualquer espaço em branco (espaços,
tabulações, CR, VT, FF), e
#encerra a linha onde quer que apareça, mesmo no meio de uma palavra. - A primeira palavra é a diretiva, comparada sem diferenciar maiúsculas
de minúsculas. Uma linha cuja primeira palavra seja qualquer outra coisa é
ignorada; é assim que as linhas
// commentda versão comercial são puladas. As palavras depois do valor também são ignoradas, então um// noteno fim da linha não faz mal. - O valor é o prefixo numérico da terceira palavra. Um peso é um número
decimal (
0.5,.5,2,1e1); um limite é um inteiro que dá a volta em 32 bits. Uma palavra de valor que não seja um número é lida como 0, e a diretiva continua valendo: oLimit CORFORT O(com a letra O) dokrogoth.txtda versão comercial proíbe o CORFORT, eLimit ARM DECOM 4limita a categoria ARM inteira a 0. - As linhas antes da primeira linha
plansão ignoradas quando a partida começa, porque nenhum plan foi selecionado ainda. Elas podem passar a valer se o perfil for recarregado durante a partida. Okrogoth.txtda versão comercial começa com duas linhas assim (weight cormakr 0.2,weight armmakr 0.2). Os arquivos de IA do TA: Kingdoms não usamplande forma alguma; veja TA: Kingdoms: perfis sem plan abaixo.
Níveis de dificuldade
Os perfis originais trazem três plans chamados easy, medium e hard. Um
plan vale numa dificuldade quando uma das suas palavras (sem diferenciar
maiúsculas de minúsculas) nomeia essa dificuldade (plan medium hard cobre
as duas) ou quando a primeira palavra é any. Um plan cujas palavras não
nomeiam nenhuma dificuldade (plan brutal, um plan sozinho) não
corresponde a nada: as linhas abaixo dele são ignoradas até a próxima linha
plan.
Nomes de unidades e palavras de categoria
Tanto Weight quanto Limit podem ter como alvo:
- O
UnitNamede uma unidade, por exemploARMCOM,CORRAID(comparado sem diferenciar maiúsculas de minúsculas). A diretiva vale só para essa unidade e a trava: as linhasWeightseguintes (no caso de um peso) ou as linhasLimitseguintes (no caso de um limite) não a alteram mais. - Uma palavra de categoria (
ARM,CORE,PLANT,CONSTR,LEVEL3,SPECIAL, …), comparada com as palavras do campoCategory=de cada unidade. Vale para toda unidade correspondente que não esteja travada. ALL: toda unidade que não esteja travada.
Toda unidade começa com prioridade de 100% e sem limite. Um peso
multiplica a porcentagem atual, trunca o resultado para um número inteiro e
o restringe à faixa de 0 a 100%, então um peso acima de 1 pode recuperar
uma unidade reduzida antes, mas nunca a leva além de 100%. Um limite
substitui o atual. No plan easy de default.txt, Weight ARM 0.2 deixa
toda unidade ARM em 20%; o Weight ARMRAD 0.25 seguinte leva a torre de
radar ARM a 5% e a trava; Weight PLANT 2 traz cada fábrica ARM de volta de
20% para 40%.
Important
Os nomes de categoria são comparados com a lista de tokens do
Category= da unidade, e não com algum arquivo de taxonomia. Um nome de
categoria só "existe" se pelo menos uma unidade o declarar. Escreva
LEVL3 errado e a diretiva, sem aviso, não faz nada (os visualizadores do
kbot a marcam como matches no unit).
Exemplo comentado: trecho de 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
Como ler isso: na dificuldade fácil, toda unidade ARM e CORE cai para 20% de
prioridade; as torres de radar caem para 5% e os conversores de metal para 2%
(os dois ficam travados em seguida); as fábricas (PLANT) e os construtores
(CONSTR) sobem de volta para 40% e 60%, e as linhas próprias dos
construtores avançados dobram os seus 60% até o teto de 100% e os travam.
Perfis específicos de mapas
A Cavedog lançou alguns perfis ajustados para certos mapas:
| Perfil | Usado por |
|---|---|
default.txt |
A maioria dos mapas genéricos |
metal.txt |
Mapas ricos em metal (Metal Heck e afins) |
airbattle.txt |
Mapas focados em combate aéreo |
seabattle.txt |
Mapas focados em combate naval |
hover.txt |
Mapas com muitos hovercrafts |
acid.txt |
Mapas do mundo ácido |
urban.txt |
Mapas urbanos |
waterwrld.txt |
Mapas de mundo aquático |
krogoth.txt |
A missão do chefe Krogoth |
missions.txt |
Missões da campanha |
O .ota do mapa escolhe qual usar via aiprofile=, por exemplo
aiprofile=metal.
TA: Kingdoms: perfis sem plan
Note
Esta seção trata só do que muda no TA:K. O TAK usa a mesma convenção
de nomes de arquivo ai/*.txt e a mesma sintaxe de diretivas
weight/limit, mas trata a diretiva plan como opcional.
Todos os perfis de IA do TA: Kingdoms da versão comercial (ai/default.txt,
o conjunto ai/mission*.txt da campanha) listam diretivas weight/limit
sem nenhuma linha plan: há uma única dificuldade implícita por
arquivo. A estrutura é um longo bloco de pesos seguido de um longo bloco de
limites, agrupados por lado pelo prefixo do nome da unidade.
Exemplo comentado: as 30 primeiras linhas de 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
Uma leitura dos pesos de default.txt revela as prioridades da Cavedog:
aradrag 25: o Dragão de Aramon recebe peso exagerado de propósito; a IA constrói dragões com mais vontade do que qualquer outra coisa.arabuild 10,aracastl 10,arakeep 10,arapries 10: todo tipo de economia/construtor fica com peso 10.arawall 0,arangate 0: zero explícito, nunca construídos.araat 1,arafast 1,arassh 1: unidades básicas que a IA deixa em segundo plano em favor de alternativas de nível mais alto.
O mesmo padrão se repete para as unidades tar*, ver* e zon* mais
abaixo no arquivo.
Bloco de limites
Abaixo do bloco de pesos vem um bloco de limites paralelo:
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
…
Repare na assimetria: aradrag tem peso 25 (prioridade alta), mas limite
1 (nunca mais de um). É assim que a IA do TA:K constrói uma unidade herói:
o peso alto faz com que ela seja construída cedo, e o limite baixo mantém a
composição do exército razoável.
Estatísticas dos perfis
O default.txt e os arquivos das missões da campanha seguem todos a mesma
forma:
| Perfil | Linhas de peso | Linhas de limite | Plans |
|---|---|---|---|
ai/default.txt |
104 | 104 | 0 |
ai/mission06.txt |
104 | 104 | 0 |
ai/mission08.txt (missão de Iron Plague) |
161 | 162 | 0 |
Os arquivos das missões de Iron Plague têm tabelas de pesos/limites mais
ricas porque cobrem as unidades de Creon (prefixo CRE), que o default.txt
do jogo base omite.
Como o kbot trata arquivos sem plan
O ai.Parse do kbot-io coloca todas as linhas anteriores à primeira linha
plan em AIFile.Preamble, que é o que o TA faz com elas (veja acima); para
um perfil do TA: Kingdoms, ai.ParseWith(data, ai.ParseOptions{DefaultPlan: true}) as lê como um único plan implícito que corresponde a todas as
dificuldades. Os visualizadores do kbot as mostram numa aba própria,
Before first plan, e dizem as duas coisas: o TA as ignora quando a partida
começa, e os perfis do TA: Kingdoms, que não têm linhas plan, as aplicam em
todas as dificuldades. O kbot não modela como o TA: Kingdoms aplica pesos
acima de 1.
O ai.IsAIFile() olha a primeira palavra de cada linha: um arquivo é um
perfil quando alguma linha começa com uma diretiva escrita do jeito usual
(uma linha plan que nomeia easy, medium, hard ou any, ou uma linha
weight/limit cujo valor começa com um número). Isso detecta os perfis do
TAK, que não têm plan, e os separados por tabulação, enquanto um .txt que
só menciona "weight" ou "limit" no texto não é confundido com um perfil.
Armadilhas
Warning
O jogo não avisa nada. Um nome de unidade escrito errado é lido como
uma categoria que não corresponde a nenhuma unidade e não faz nada; um
valor que não é número é lido como 0 (e então um limite proíbe o alvo); as
linhas antes do primeiro plan são ignoradas no início da partida. O
primeiro sinal de um perfil quebrado costuma ser "a IA está se comportando
de um jeito estranho". Os visualizadores do kbot apontam cada um desses
casos (veja abaixo).
Limit 0, ou qualquer limite negativo diferente de-1, proíbe a IA de construir aquele alvo.-1remove o teto.#encerra a linha em qualquer posição, mesmo no meio de uma palavra; não existem comentários de bloco. O//só funciona porque uma linha cuja primeira palavra não é uma diretiva é ignorada, e as palavras depois do valor também são ignoradas.- Um peso acima de 1 nunca leva uma unidade além de 100%. Ele só pode recuperar uma unidade que um peso de categoria reduziu antes.
- A primeira linha que nomeia uma unidade a trava. Coloque as linhas específicas de unidades depois das linhas de categoria que elas devem refinar.
- Os espaços em branco entre os tokens só importam como separadores: tabulações, espaços e espaços múltiplos funcionam do mesmo jeito.
- Não há suporte a seções aninhadas ou compartilhadas. Cada bloco
planprecisa declarar de novo cada peso/limite que lhe interessa.
O que o kbot mostra
O explorador de recursos do studio (aba AI Profile) e o describe do
kbot mount leem o perfil do mesmo jeito que o jogo:
- Before first plan: as linhas do preâmbulo, identificadas como ignoradas quando a partida começa.
- Uma aba por plan, com cada alvo marcado como unit, category ou
all units de acordo com os
units/*.fbida instalação (nome eCategory=). Uma categoria que nenhuma unidade tem, que é como um nome de unidade escrito errado é lido, é marcada como matches no unit. Os pesos aparecem como multiplicadores, com uma barra para a porcentagem que sobra para uma unidade que estava em 100%; os limites, como ∞ Unlimited, Disabled (0 ou qualquer valor negativo diferente de -1) ou Max: N. - Linhas que o jogo lê de outro jeito: os diagnósticos do kbot-io, como
uma palavra de valor que não é número (
Oé lido como 0) ou palavras depois do valor. Quando a palavra de valor não é o número que o jogo lê, ela aparece ao lado dele (written "O"); outra grafia do mesmo número, como.1ou0.10, não aparece. - No início da partida (no studio; o
kbot mountimprime as contagens): para easy, medium e hard, cada unidade cujo peso ou limite o perfil altera, com 🔒 nos valores que uma linha de unidade travou.
Tamanhos típicos
| Métrica | Faixa observada nos perfis da Cavedog |
|---|---|
| Tamanho do arquivo | 1–4 KB |
| Plans por arquivo | 3 (easy/medium/hard) |
| Diretivas de peso por plan | 20–80 |
| Diretivas de limite por plan | 0–10 |