Лучший интеллект монстров

Материал из Клуб любителей рогаликов
Перейти к: навигация, поиск

Я написал это, чтобы зафиксировать то, что считаю наиболее интересным в искусственном интеллекте монстров. Эта статья главным образом касается того, как монстр может оценивать свое окружение. Места, где кто-нибудь вносил предложения относительно того, как расширить или улучшить исходную модель, отмечены (*). Специальная благодарность группе rec.games.roguelike.misc за их комментарии и предложения.

Некоторые из RL-игр используют простой искусственный интеллект. Выглядит это примерно так:

	if (ISeeHero [ЯВижуГероя])
        then (MoveToAttackHero [ДвигатьсяЧтобыАтаковатьГероя])
	else (DoNothing [НичегоНеДелать])

Удивительно, но это достаточно хорошо работает. Но давайте расширим это, чтобы придать монстрам немного индивидуальности, некоторый стиль.

Руководящие принципы:

Разные монстры хотят различные вещи. Монстры видят других монстров и объекты поблизости и взаимодействуют с ними, даже когда героя нет поблизости. Каждый монстр действует независимо. Группы монстров должны осознавать себя как группы и должны действовать как группа. Вы всегда можете сделать это более сложным, но нам не нужен совершенный искусственный интеллект. Нам нужна только иллюзия несколько более интересного поведения. Средняя продолжительность жизни монстра в RL игре может составлять до 100 ходов, и только 10 % из этого в поле зрения игрока. Поведение монстра подразделяется на 5 возможных исходных типов: Желание, Страх, Агрессия, Передвижение и Прочее.

Агрессия - монстр стремится напасть на другого монстра (или героя). Страх - монстр стремится уйти от монстра (или героя); это может быть расширено путём включения других аспектов. Желание - монстр ищет объект или объекты, включая золото, пищу, оружие, и т.д. Передвижение - некоторая функция, предотвращающая блокирование монстров в локации. Прочее - это охватывает специальное поведение, например пребывание около алтаря или движение при патрулировании. Типы данных используемые для принятия решения:

Каждый монстр будет иметь весовые значения для 5 вышеперечисленных типов. Если это значение ноль, тогда монстр игнорирует любое поведение данного типа. Монстр может также иметь одну или более индивидуально определенных линий поведения, такие как Ненависть к Эльфам или Жажда Золота. Каждая из них будет также иметь весовой коэффициент и должна рассматриваться как дополнение к значению основного типа поведения. Для того, чтобы монстр имел Желание, необходимо иметь индивидуально определенную линию поведения, указывающую, вещь (вещи) какого типа (типов) он желает.

Чтобы работали такие типы поведения как Страх и Агрессия, монстр должен уметь соотносить свою боевую силу с любым монстром, которого он видит.

  • Возможное усовершенствование: монстры будут оценивать силу двумя методами, умным и глупым, когда человек-крестьянин и человек-волшебник могут показаться глупому монстру, одним и тем же, но умный монстр может заметить разницу.
  • Возможное усовершенствование: уменьшите воспринятую боевую силу монстра, если он ранен. Это может быть просто. Например: if (HP*lt; (MaxHP/2)) CombatStr--

Монстру потребуется список дружественных монстров. Сделайте его настолько широким, насколько возможно. Например: орки всех типов дружественны с орками всех других типов, так Орк-Воин будет знать, что Орк-Священник его друг.

  • Возможное усовершенствование: расширите эту простую бинарность (друг/не друг) до диапазона значений, чтобы указать степень взаимоотношений от чрезвычайной ненависти, до полного страха.

Чтобы работало Желание, каждый монстр должен уметь воспринимать ценность товаров или добычи. Каждый объект также в этом нуждается, но раз уж объекты обычно имеют ценность, используйте это.

В течение хода монстра это будет выглядеть так:

  1. Осмотреться вокруг и оценить, что его окружает. Монстр должен смотреть во всех направлениях. Для каждого обнаруженного им объекта или монстра, нужно выполнить вычисление, чтобы определить, куда он хочет пойти.
  2. Если он нашёл дружественного монстра, монстр добавляет (monstr str +1 - distance) [(сила монстра +1 - расстояние)] к своей собственной боевой силе. Друзья могут встретится и действовать вместе.
  3. Если он нашёл монстра, который не является другом, предположим, что это враг. Здесь мы станем хитрыми. Желание убить кого-либо базируется только на том, насколько он по нашему мнению силен и далек. Боевая сила цели здесь не имеет значения. Более того, он только и заинтересован, как напасть на ближайшего врага с любого заданного направления. Я не ожидаю, что он окажется способным идти за одним врагом, чтобы найти другого. Нам также потребуется сделать это, чтобы правильно оценивать группы монстров.
    Для любого заданного направления, значение агрессии - (my str + my buddies str [per step 2]) x (6 - nearest enemy distance) + 2x (basic aggressive value + any special hates) [(моя сила + сила моих приятелей [при шаге 2]) x x (6 - расстояние до ближайшего врага) + 2x (базовое значение агрессии + любая специальная ненависть)]. Мы не знаем наверняка сколько у нас друзей, пока мы не закончим осматриваться вокруг. Это означает, что нам нужно отслеживать некоторые промежуточные значения. Сначала, отследим расстояние до самого близкого монстра в каждом направлении. В конце мы изменим его, чтобы найти реальные значения.
  4. Для каждого недружественного монстра, рассчитаем значение Страха. Это (target's perceived str) x (6-target's distance) + 2x (any special fears) [(воспринятая сила цели) x (6-расстояние до цели) + 2x (любой специальный страх)]. Так как мы вычисляем это для каждого монстра в конкретном направлении, то мы можем отобразить значение с первого раза. Это означает, что группы монстров считаются более опасными, чем индивидуумы.
    Если есть хоть какие-нибудь монстры (мы возвращаем ненулевое значение), тогда мы добавим + 2x (my base fear)[+ 2x (мой базовый Страх)] для конкретного направления. Таким образом, ваш основной страх статичен для каждого направления, и не зависит от количества монстров. Суммарное значение нужно добавить к направлению противоположному тому, куда мы смотрим, так что мы представляем Страх как Желание идти другим путём как противоположность отрицательному Желанию двигаться в этом направлении. Это делается для того, чтобы было более просто нормализовать его в конце.
  5. Для каждого объекта или цели монстра, сформируйте любые Желания если это применимо. Если монстр, который осматривается вокруг, хочет золото или снаряжение, используйте воспринятое значение ($) для любых монстров-целей. Если они желают пищи, используйте функцию пищевой ценности трупа. Объекты на земле должны рассчитываться тем же путём. Полное Желание для конкретного направления должно быть суммой (obj value) + (6-obj distance) + (basic desire) + (particular desire) [(ценность объекта) + (6- расстояние до объекта) + (основное Желание) + + (специфическое Желание)]. Обратите внимание, что эти ценности складываются, а не умножаются.
  6. Если Вы видите что-то, что может вызвать категорию Прочее (Misc), то вычислите значение для этого предмета. Примеры могут включать пребывание вблизи алтаря, или прийти поболтать с героем, и т.д.
    (*) Предложение от Эйдана Райдера (Aidan Ryder): возможно алтарь должен уменьшать значение Страха, чтобы показать, что монстр пытается защищать свою святыню.
  7. Вычисляем значение Передвижения, которое является случайным направлением со значением 1 << (значение основного Передвижение) и << 2, если монстр испуган. Базовое значение Передвижения, должно быть низким, но должна увеличиваться если монстр возвращается к предыдущему местоположению или стоит на месте. Это должно предотвращать стояние на одном месте. Если монстр не двигается к предыдущему местоположению и не останавливается, значение Передвижения уменьшается до минимума для данного монстра (из шаблона монстра). Не изменяйте значение Передвижения если монстр занят задачей, которая занимает больше, чем один ход (включая сон).
    (*) Предложение от Эйдана Райдера (Aidan Ryder): Когда монстр занят, например "Питанием" ... это занимает время, пропорциональное весу пищи, которую он ест ... и во время этого его "радиус обзора" уменьшен до одного или двух полей ... что позволяет Вам подкрадываться к монстрам во время еды, чтобы убивать их.
  8. Наконец, вернёмся к значению Агрессии. В каждом направлении мы должны теперь сохранять величину расстояния до ближайшего монстра, если он там есть, иначе 0. Если там есть монстр, мы должны взять теперь нашу общую воспринимаемую силу нападения (вместе со всеми нашими друзьями) и можем преобразовать её в значение поведения Агрессии.
  9. Сложить все значения. Приведите к вектору (в направлениях X и Y, или возможно даже Z). Это направление, в котором хочет идти монстр. Также вычислите, что является самым сильным мотивом из этих 5, и присвойте это действию монстра. Это позволит монстру использовать атаку на расстоянии если она для него возможна.
    (*) См. примечания относительно поиска пути ниже.
  10. Убедитесь, что вычислили все параметры хода монстра прежде, чем он начнет действовать. Это удержит группы вместе.

Из этого выходят несколько хороших линий поведения:

Пример №1: Если огр Bobo (относительная сила 4) должен выбирать между слабым монстром поблизости и немного более сильным немного дальше, то сначала он пойдёт делать пюре из более слабого.

#.........    # = стена    . = свободное пространство
#..3..B.2.    3 = монстр с силой 3
#.........    B = Bobo     2 = монстр с силой 2

Здесь Bobo гонится за 2.

(*) Стивен Салтер утверждает, что в интересах монстра лучше нападать на самого сильного противника, которого, как он думает, сможет убить. Это позволило бы группе собирать более опасную оппозицию перед любыми незначительными врагами. Я не согласен. Так как большинство монстров на данном уровне имеет примерно равную боевую силу, нападать на более слабого противника лучше, чем получить повреждения от более сильного. Многое зависит от как Вы настроите ваших монстров, и насколько плотно заселите ваши уровни. Вы можете решить, что является для Вас наилучшим.

Пример №2: Если огр Bobo попадает в следующую ситуацию:

........
.1...8..
........
...B....
........

Он будет двигаться строго влево, обходя по спирали вокруг монстра силой 1, чтобы раздавить его и убежать (предположим, что других два монстра стоят на месте).

Пример №3: Вы можете определить тип осторожного падальщика. Если Nimble RatThief (относительные сила 1, Агрессия 1, Страх 4, жадность 4) замечает Героя (относительная сила 4, $ 4), он попробует и останется примерно на расстоянии в 3 поля, перемещаясь дальше за ним, при столкновения героя с другими монстрами приближаясь, если герой получает повреждения (и становится слабее). Вор-крысятник 'тень' героя, собирая всё, что остаётся после героя, приближаясь чтобы убить, только если герой становится слабее.

В этом примере, Nimble хочет золото игрока больше, чем он хочет убить игрока, поэтому Nimble предпочтёт быть бродягой или карманником для раненого игрока.

Слабые места этой модели: Есть большая проблема с 'то, чего не вижу, о том не знаю'. Наши монстры не запоминают, куда они хотели пойти от одного хода до следующего. Если монстр теряет свою цель из виду, то он полностью забывает о её существовании.

#..........     # = стена    . = свободное пространство
#.B........     B = Bobo     $ = Золото
#......####
#........$.

Бобо идёт на восток и теряет из виду золото.

Одно решение: Эта проблема главным образом будет возникать с Желаниями. Другие монстры будут двигаться вокруг и могут возвращаться назад согласно своим собственным представлениям, или двигаться дальше безо всякого интереса. Одно из решений в том, чтобы запоминать, между ходами самое сильное Желание и направление. Если расчетное желание для этого направления - ноль, (цель исчезла), включите ранее запомненное желание назад.

Обход препятствия. Если монстр сталкивается с препятствием, которое он может увидеть, вроде ямы, он не знает, как его обходить. Препятствия становятся проблемой только если они находятся точно в одном из 8 направлений, но монстр будет часто перемещаться в таких условиях. Выбрав направление монстр хочет переместиться туда из нынешнего местоположения. Я также должен сделать несколько больше работы, если мы хотим охватить все аспекты поиска пути.

#.....    # = стена    . = свободное пространство
#.B.X$    B = Bobo     X = яма
#.....    $ = Золото

У Bobo проблема.

Эйдан Райдер (Aidan Ryder) указывает, что ямы или другие ловушки должны затрагивать только Агрессию и Желание, не всегда Страх:

#...........    # = стена
#.......X...    X = ловушка
#...D.B.X...    B = несчастный Bobo
#.......X...    D = громадный HellWyrm (Адский червь?)
#...........

Теперь Bobo должен бежать наутёк так быстро, как только возможно, пренебрегая ловушками, потому что он так напуган wyrm'ом ..., что если он попробует обойти вокруг, wyrm может вполне поймать его.

Решение: Мое первое решение было напролом. Если препятствие непосредственно в одном из 8 направлений и Вы хотите оказаться по другую его сторону, разделите направляющие векторы (Агрессия и Желание), чтобы указать 3/4 на (желательное направление + 45 градусов) и 1/4 в (желательное направление - 45 градусы). Если препятствие - преодолимое, но нежелательное, оставьте все страхи одни опасения. Это позволит Вам обходить маленькие препятствия и все ещё учитывать другие импульсы, которые могут направить Вас в одном направлении, а не в другом. Это не самое лучшее решение, так как монстры застревают на больших препятствиях и все ещё теряются с лёгкостью. Но это легко осуществить с существующей моделью.

Хорошее решение: Мы должны сочетать импульс поведения с алгоритмом поиска пути, там, где это имеет смысл. Мы будем нуждаться в дополнительном члене данных для местоположения (x, y). Когда мы оцениваем желания монстров (шаги 1-10 выше), если самое сильное поведение монстра или Агрессия, или Желание, мы сохраняем местоположение цели, так же как направление. Если самое сильное поведение - Страх или Передвижения, мы сохраним пустое значение. Для поведения Прочее, это будет зависеть от определенного поведения.

Когда мы закончим с оценкой всех полей и если наш (x, y) не пустое значение, то мы можем использовать алгоритм поиска пути, чтобы добраться к цели. Вы только должны оценить пути в желательном направлении, или возможно желательное направление и два соседних направления. Не забудьте включить фактор для другого поведения, которое мы уже оценили (если Вы хотите пойти на восток, и у Вас сильный страх перед севером, вес южного маршрута более желателен. Вы не должны переоценивать заново ту вещь на севере, которая внушает страх).

Если (x, y) пустое значение, вернёмся к более простому методу, поскольку лучше настраивать поведения, которые не направлены на определенную цель.

Вот и все. Принимаются любые комментарии и пожелания.



Автор: Joseph Swing.
Источник: A Better Monster AI.
Перевел: Сергей В. Ждановских, 20.02.2006.