Добрый вечер форумчане!
Недавно вечером я сидел и, как обычно, потихоньку делал свой проект. Проект в моем представлении весьма достойный, но на деле пока практически нулевой. Задача была мной поставлена такая: сделать этакий Hero Defence, проработанный до мелочей, с отличной историей и прочим, и т.д. Не важно. Но вот захотелось мне сделать для волн нападающих достойный ИИ, как собственно и для волн, помогающих защитить что-либо.
До этого я много раз пытался, иногда вполне неплохо, делать такого рода ИИ. Но все, что было раньше - все должно меркнуть, перед новым ИИ. Читал кстати у нас на форума статейки, был даже вполне неплохой вариант (увы не вспомню как называлась карта и кто ее сотворил, было еще явно до 1.24 версии).

Суть в следующем. Нужно сделать не просто ИИ, который выбирал бы более приоритетные слабозащищенные и уязвимые цели, не просто пытался сбивать касты и держать в контроле ДД союзной команды, но и умело передвигаться по полю боя, заходить за спины, уходить от потенциально опасных противников и т.д.

Цель была понятна, сел изучать литературу. Изучал 2 дня (конечно, мало для такой темы, но начало положено) , сидел, корпел, пил кофе и вставлял спички в глаза. Признаться узнал, что все, что я делал под названием "ИИ" было не больше, чем игрушка ребенка.

Собственно и вопрос таков: есть ли у кого опыт создания хороших ИИ? Что вы для этого использовали? Использовали ли вы только Дерево принятия решений и FMS (конечные автоматы) или же возможно (ну а вдруг) кто-либо интегрировал в кодинг нейронные сети? Конечно, любому картостроителю хотелось бы, чтобы его "ребенок" обучался :) Возможно кто-то пользовался методом "Цепей Маркова"? (вот уж не знал, что их изучение в универе может пригодиться и тут)

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

Если когда-нибудь (уповаю) я доделаю свой проект, всех без исключения добавлю в благодарности
Всем спасибо и хороших выходных господа!

Принятый ответ

Сейчас как раз делал ИИ для демки. Циклы для подобного - полная жопа. Пытался детерминировать логику максимально, но всеравно выходили либо зацикливания (один тик я хочу пассать, один тик я бегу от гопников, а потом опять пассать), либо неправильные решения (вокруг куча гопников а я ссу). И тут я пришёл к гениальной мысли: сделать по теме синаптических весов из нейронных сетей. Саму нейросеть не реализовывать, но вот именно эту фишку взять. Т.с. берёшь входы и создаёшь весы выходов, составляя их по формулам из входов. Лучший вес выигрывает, задавая задачу на тик, а чтобы не было зацикливания - переопределение таска идёт лишь при определённых условиях (напр. поведение сильно изменилось, противник умер и т.д.). И дебажить можно легко! Вышло неплохо - всем рекомендую :3
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
7
9 лет назад
0
JaBeN_Симфер, переборы смущают, факторов у меня будет множество, + системка не для ии героя а для ии волн, т.е. юнитов тоже будет не 1-2. Буду начинать как минимум с таких древ )
ZLOI_DED, Спасибо, на счет синаптических весов надо бы мне почитать) Вообще не вижу вариантов создания ИИ без применения тех же самых весов для каждого действия, так что пошел читать))
P.S. ZLOI_DED, если это возможно, сможешь выложить алгоритм действий поподробнее?
1
10
9 лет назад
Отредактирован ZLOI_DED
1
Ambruziy:
P.S. ZLOI_DED, если это возможно, сможешь выложить алгоритм действий поподробнее?
Делаем весы по следующему принципу: профит от выполнения задачи / сложность выполнения.
Т.е. для аттаки можно, к примеру, сделать так: кол-во монет за убийство / расстояние до врага.
enum Task {
Idle, Attack...
}
update()
{
setTask();
executeTask();
}
setTask()
{
float idleWeight = 0f;
float attackWeight = 0f;
if (enemies.size() > 0)
{
Enemy e = getNearestEnemy();
Vector2f diff = new Vector2f(Math.abs(e.getLocation().x-getLocation().x), Math.abs(e.getLocation().y-getLocation().y));
attackWeight = 1f / diff.length();
target = e;
}
float maxWeight = Math.max(idleWeight, attackWeight);
if (currWeight == 0 ||
task == Task.Attack && (target == null || target.isDead() || currWeight-attackWeight>.1f))
if (maxWeight == attackWeight) {
task = Task.Attack;
} else {
task = Task.Idle;
}
}
currWeight = maxWeight;
executeTask()
{
switch (task)
{
case Attack:
if (target != null)
{
followAndAttack(target);
}
break;...
}
}
Базово именно так. Дальше можно всячески усложнить, добавив кучу разных других весов. Единственная сложность - калибровка. Тебе прийдётся запустить её с сотню раз, чтобы откалибровать весы, но это того стоит.
Как калибровать:
  1. Сделай либо сисаут либо вывод прямо на экран весов.
  2. Создавай ситуации и смотри как меняются весы.
  3. Делай выводы и изменяй формулы (напр. эти олухи постоянно обирают монетки, когда должны воевать - увеличить профит от аттаки каким-нибудь модификатором или уменьшить профит от сбора монет. Эти глупцы постоянно деруться, когда должны убегать при лоу хп - увеличить мод побега или уменьшить мод аттаки).
  4. ...
  5. PROFIT!
Ручная калибровка медленнее чем то что с нейронными сетями, но быстрее в том плане, что ты видишь лучше что поменять надо.
0
7
9 лет назад
0
ZLOI_DED, отлично! Спасибо за инфу, буду пробовать :)
0
10
9 лет назад
0
Ambruziy:
ZLOI_DED, отлично! Спасибо за инфу, буду пробовать :)
Дописал setTask();
0
29
9 лет назад
0
Что касается кучи проверок - это все верно, но нас приводит опять же к традиционному древу решений, через масс if...them...else. Тут "меня терзают смутные сомненья", что при достаточном кол-ве юнитов (хотя в самом дефенсе планирую совсем немного к слову, не больше 10) эта масса проверок может серьезно загрузить игру. Хотя, это надо смотреть.
У меня в карте используеться система урона с тучей if в ней 4к строк вида
if IsUnitType(t,UNIT_TYPE_HERO) and Dm > 0. and GetUnitAbilityLevel(t,'A085') > 0 then
call SaveReal(udg_AssassinHash,GetHandleId(t),StringHash("LSDC"),Dm+LoadReal(udg_AssassinHash,GetHandleId(t),StringHash("LSDC")))
endif

if IsUnitType(t,UNIT_TYPE_HERO) and Dm > 0. and GetUnitAbilityLevel(t,'A045') > 0 then
call SaveReal(udg_AssassinHash,GetHandleId(t),StringHash("MADC"),Dm+LoadReal(udg_AssassinHash,GetHandleId(t),StringHash("MADC")))
endif

if GetUnitAbilityLevel(t,'B03V') > 0 and Dm > 4. and t != s then
set r1 = Dm*.3
set udg_DBR[w] = Dm
call UnitDamageTarget(udg_NDCD[q+1],t,r1,true,false,ATTACK_TYPE_HERO,DAMAGE_TYPE_UNKNOWN,WEAPON_TYPE_WHOKNOWS)
set Dm = Dm+r1
endif

if LoadBoolean(udg_AssassinHash,GetHandleId(t),StringHash("MoDB")) and Dm > 5. and t != s then
set udg_D = LoadUnitHandle(udg_AssassinHash,GetHandleId(t),StringHash("MoDC"))
set r1 = Dm*(.10+.10*GetUnitAbilityLevel(udg_D,'A0GU'))
set udg_DBR[w] = Dm
call UnitDamageTarget(udg_NDCD[GetPlayerId(GetOwningPlayer(udg_D))+1],t,r1,true,false,ATTACK_TYPE_HERO,DAMAGE_TYPE_UNKNOWN,WEAPON_TYPE_WHOKNOWS)
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl",t,"chest"))
set Dm = Dm+r1
endif


if Dm > 5. and GetUnitAbilityLevel(dc,'A0F6') > 0 and t != s then
set r1 = Dm*(.1+.05*GetUnitAbilityLevel(dc,'A0F6'))
set udg_DBR[w] = Dm
call UnitDamageTarget(udg_NDCD[q+1],t,r1,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_MAGIC,null)
if r1 > 65. then
call JT(t,0,12,0.8,0.7,"|cffFF6600"+I2S(R2I(r1))+"!",dc)
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl",t,"origin"))
endif
set Dm = Dm+r1
endif
И это вызывается при каждом получении урона. Ничего не лагает. В твоём случае это будет в периодическом триггере вызываться. Если период будет где-то .05, то всё нормально будет.
Что касается второго замечания: конечно, грамотно продуманная схема приказов даст нам неплохой ии, проблема в том, что чтобы был ИИ отличным, нужно крайне много проверок. Опять же боюсь за производительность :)
Даже если будет, всегда потом можно оптимизировать код.
Однако если все-же я считаю, что мой комп молодец, встает второй вопрос, который я пока не знаю как решить:
после множества проверок ИИ будет себя вести абсолютно правильно, но именно так, как ты запрограммировал, т.е. ты будешь предвидеть его действия.
Ну в свою карту, где ты играешь не против игроков, а против крипов в любом случае не особо интересно играть, поэтому я так и не сделал не одной крупной пве карты.
0
10
9 лет назад
0
16GB:
Ну в свою карту, где ты играешь не против игроков, а против крипов в любом случае не особо интересно играть, поэтому я так и не сделал не одной крупной пве карты.
"ИИ не должен побеждать игрока, он должен красиво ему отдаваться."
0
7
9 лет назад
0
И это вызывается при каждом получении урона. Ничего не лагает.
Рад это слышать, вы меня успокоили :)
"ИИ не должен побеждать игрока, он должен красиво ему отдаваться."
Абсолютно согласен, в конечном варианте необходимо будет сделать сознательные "прорехи" в игре ИИ, чтобы целью игрока было найти эти "прорехи" и победить.
0
21
9 лет назад
0
По поводу переборов - имею ввиду перебор по тику подконтрольных ИИ юнитов, затем в каждом идет перебор целей при определенных условиях. А бывает еще сложнее, когда надо произвести обмен предметами с выгодой для обоих. В остальном алгоритме полностью поддерживаю ZLOI_DED, т.к. иду тем же путем.
Посмотри про транспортную задачу.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.