Создание простого (Melee) AI

Содержание:

6. Больше теории Jass’a: управление действиями, условия и циклы

Управление действиями - о том, как сделать возможным ходить разными путями в программе, {например, как устанавливать порядок действий. Можно создать множество действий, а потом сделать их последовательными благодаря условиям и циклам, а можно использовать это и в других целях...} Jass поддерживает это со своим if и циклическими конструкциями. Так давайте бегло ознакомимся с ними.
{Примечание: данная часть статьи была переведена лишь потому, что здесь можно найти некоторые интересные примеры использования циклов при создании AI-скриптов.}
if
Существует несколько вариантов того, как может быть использован if. Но основная идея в том, что если что-то - истина, тогда мы сделаем какие-то действия, в противном случае мы сделаем нечто другое (или может быть вообще ничего не сделаем).
if condition then
    doSomething
    doSomethingMore
endif

if GetUnitCount( 'hpea' ) < 12 then   // Если у нас меньше 12 крестьян,
    call setProduce( 1, 'hpea', 0)       // Строить больше одного крестьянина (в нашей главной базе)
endif
Эй, а если мы захотим сделать что-то другое, если будем иметь всех крестьян?
if condition then
    doSomething
else
    doSomethingElse
endif

if GetUnitCount( 'hpea' ) < 12 then   // Если у на меньше 12 крестьян,
    call setProduce( 1, 'hpea', 0) Строить больше одного крестьянина (в нашей главной базе)
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
endif
Это хорошо, если есть только две возможности, но что если нам нужно выбрать между тремя отдельными действиями?
if condition then
    doSomething
elseif condition2
    doSomethingElse
else
    doSomeOtherThing
endif

if GetUnitCount( 'hpea' ) < 12 then   // Если у на меньше 12 крестьян,
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need more peasants" )
    call setProduce( 1, 'hpea', 0)       // Построить одного крестьянина (в начальном городе)
elseif GetUnitCount( 'hpea' ) < 20 then
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We have to many peasants" )
endif
Путём добавления большего количества elseif’ов Вы можете сделать программу выбирающую между несколькими различными действиями. Просто убедитесь, что Вы поместили ваши условия в соответствующее место. Следующий скрипт неудачен:
if GetUnitCount( 'hpea' ) < 20 then
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need no more peasants" )
elseif GetUnitCount( 'hpea' ) < 12 then   // Если у на меньше 12 крестьян,
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need more peasants" )
    call setProduce( 1, 'hpea', 0)       // Построить одного крестьянина (в начальном городе)
else
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We have to many peasants" )
endif
Если у нас есть один крестьянин, код скажет, что нам больше не нужны крестьяне. Это потому что условия проверяются сверху вниз.
Loop
Циклы - это повторяющаяся много раз вещь. Если вы использовали циклы в других языках, Вы можете ощутить неудобство с циклами Jass'а. Всё же есть способ реализации циклов while и for в Jass.
Давайте сначала посмотрим на цикл, который будет запускаться вечно.
loop
    doSomethingForever
endloop

loop
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Let's spam the user forever")
    call Sleep( 1.0 )    // Он не будет вечным без спячки, потому что движок
// убьёт нас за ненадёжность
Endloop
Циклы, которые запускаются навсегда, используются, но они не очень гибкие. Так давайте введем строки, чтобы выйти из цикла.
loop
    doSomething
    exitwhen condition
    doSomethingMore
endloop

local integer i = 1
loop
    call Sleep( 1.0 )    // Он не будет вечным без спячки, потому что движок
// убьёт нас за ненадёжность
    exitwhen i > 10
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Let's spam the user 10 times")
    set i = i + 1
endloop
for
Мы не имеем циклы for в Jass, но мы можем сделать цикл, который будет вести себя подобно циклу for.
for ( initStatement; condition; endOfIterationStatement ) {
    stuff;    
}
может быть сделан на Jass как
initStatement
loop
    exitwhen not (condition) 
    stuff  
    endOfIterationStatement  
endloop
А теперь давайте посмотрим на более конкретный пример.
for (int i = 0; i < 10; i++) {
    printf("We need to say it 10 times");
}
На Jass’e станет
local integer i = 0
loop
    exitwhen not ( i < 10 )   // выход из цикла, если i >= 10
    call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "We need to say it 10 times")
    set i = i + 1
endloop
while
Может быть, вам следует самому попытаться сделать это. Если вы не имеете обыкновение в использовать цикл while, просто проигнорируйте иго, Jass, во всяком случае, ничего не знает о нём.

7. Функции, которые мы использовали до этого

Эта часть о тех функциях, которые мы использовали до этого, о том, как они работают, что они делают, и почему мы хотим, чтобы они делали только это.
Для каждых функций, которые мы использовали, я укажу, как она объявлена в sourcefiles, объясню аргументы, что она делает и что возвращает.
native Sleep takes real seconds returns nothing
call Sleep( 3.5 ) заставляет наш скрипт заснуть на 3,5 секунд
Если мы никогда не спим, движок получается анормальным и выводит скрипт из строя после некоторого времени.
constant native GetLocalPlayer takes nothing returns player
local Player p = GetLocalPlayer() // {создаёт локального игрока p, которого мы используем, чтобы выдать текст именно ему (смотрите ниже)}
native DisplayTextToPlayer takes player toPlayer, real x, real y, string message returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, "Hello local player!" )
Нам нужно сообщить функции, что писать и кому. Изменяя нули на нечто другое, мы можем также контролировать, где текст появится на экране. Почувствуйте себя свободным для эксперимента, нули обычно работают всё же лучше. Это сообщение уйдет после некоторого времени. Время зависит от длины сообщения.
native DisplayTimedTextToPlayer takes player toPlayer, real x, real y, real duration, string message returns nothing
call DisplayTextToPlayer( GetLocalPlayer(), 0.0, 0.0, 60, "Hello for 60 secs!" )
Также, как и выше, но теперь мы контролируем, как долго будет показываться текст.
native ClearHarvestAI takes nothing returns nothing
call ClearHarvestAI() // приказывает искусственному интеллекту, отвечающему за сбор, остановить сбор.
Это используется с двумя функциями ниже, чтобы контролировать, как много рабочих собирают золото и дерево.
native HarvestGold takes integer town, integer peons returns nothing
call HarvestGold( 0, 5 ) // Сообщает 5 рабочим начать сбор золота в городе 0, который является главным городом.
Если этот вызов сделан дважды, без ClearHarvestAI, 10 рабочих вместо 5 будут собирать золото.
native HarvestWood takes integer town, integer peons returns nothing
call HarvestWood( 0, 10 )
Работает точно также, как и выше, только вместо золота дерево.
native SetProduce takes integer qty, integer id, integer town returns boolean
local boolean success = SetProduce( 1, 'hpea', 0 ) Попытается создать одного крестьянина в городе 0 (главный город).
call SetProduce( 1, 'hpea', 0 )
Я не полностью верю обратной величине, но это, может быть, из-за того, что иногда я немного скептичен.
native GetUnitCount takes integer unitid returns integer
set numberOfPeasants = GetUnitCount( 'hpea' ) // Считает количество, крестьян, которое имеет AI-игрок.
Это величина включает и тех крестьян, кто в то время создаётся.
native GetUnitCountDone takes integer unitid returns integer
set numberOfPeasants = GetUnitCount( 'hpea' ) // Считает количество, крестьян, которое имеет AI-игрок.
Это величина не включает тех крестьян, кто в то время создаётся.
Вы, вероятно, скажете, что всё это очень хорошо, но как я могу построить или сосчитать другие вещи? Мы скоро рассмотрим, какие секретные величины использованы, чтобы создавать другие вещи.