forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Болтовня (http://forum.boolean.name/forumdisplay.php?f=25)
-   -   Искусственный интеллект. Как? (http://forum.boolean.name/showthread.php?t=19452)

ant0N 02.10.2014 07:52

Искусственный интеллект. Как?
 
Хотелось бы узнать какие существуют приемы для создания умного, легкорасшираемого, приятночитаемого ИИ.
Сам я всегда делал через бесконечность if-else-end + state = true.
Кто как делает?

impersonalis 02.10.2014 11:40

Ответ: Искусственный интеллект. Как?
 
Ээээ? "Хотелось бы иметь аппарат!" Хоть какого применения ИИ?
см. ещё

ant0N 02.10.2014 13:25

Ответ: Искусственный интеллект. Как?
 
Цитата:

Сообщение от impersonalis (Сообщение 287560)
Ээээ? "Хотелось бы иметь аппарат!" Хоть какого применения ИИ?
см. ещё

это не то... Мне не нужны заумные книжки и старомодные ученые. Я не спрашиваю про искусственный интеллект вцелом. Я спрашиваю кто как программирует NPC?
Вот, например, только что нашел: ИИ в Халфе

impersonalis 02.10.2014 13:27

Ответ: Искусственный интеллект. Как?
 
Цитата:

Сообщение от ant0N (Сообщение 287567)
Я спрашиваю кто как программирует NPC?

это другое дело. А то: телепаты-то сами знаете где

Taugeshtu 02.10.2014 14:29

Ответ: Искусственный интеллект. Как?
 
Цитата:

Хотелось бы узнать какие существуют приемы для создания умного, легкорасшираемого, приятночитаемого ИИ.
Finite state machine, например. Ещё можно нагуглить старые (пре-релизные) геймплейные видео TES 4: Oblivion, в них об их Radiant AI рассказывали, можно почерпнуть пару идей.

Mr_F_ 02.10.2014 15:21

Ответ: Искусственный интеллект. Как?
 
Могу рассказать об AI в Faded.
Там тупняка покамест много конечно с ботами, но все части интеллекта хорошо изолированы, и баги являются частью конкретных стейтов/их недостатка для конкретных ситуаций, а не всей системы в целом.

Я прошёл через 3 итерации полной переписки системы, и нынешняя версия мне нравится.

Первое, на что стоит обратить внимание, это, как подметил Taugeshtu, стейт-машины. Они простые и нормально работают для простого ИИ (турелям хватит).
Обычно стейт это какая-то последовательность действий, которую повторяет бот, пока не перейдёт в другой. В стайте про HL ими можно назвать shedule'ы по сути. В идеале стейты должны иметь чёткую задачу, чтоб если не понимаешь, что бот творит, достаточно было глянуть в каком он стейте.
Херня начинает происходить при попытке написать для сложного бота при каких условиях бот должен быть в каком стейте, т.к. событий в игре много и стейтов тоже становится много.

Распространённый вариант - State transition table, когда у нас есть таблица в какой стейт перейти при получении ботом каждого эвента в зависимости от того, в каком он стейте сейчас, но с ней случается комбинаторный взрыв, и невозможно за всем уследить.

Далее (уже не про стейт-машины), если Behaviour trees.
Вкратце, суть такова (насколько я её понял из разных презентаций): есть дерево, ноды которого - условия, а конечные листья - собственно, действия или их последовательности. Условия могут возвращать успех, тогда мы идём дальше вниз по нему, а если возвращают неудачу, то мы возвращаемся на родительскую ноду и пробуем следующую её ветвь, если же следующей нет, то он тоже возвращает к своему паренту.
Тут одновременно 2 удобства, на мой взгляд - иерархически удобно разбитая логика + непрямое наличие приоритетов у действий.
Там они много типов нод напридумывали ещё, но судя по докам, всё это мотивировалось желанием создать юзер-френдли систему, в которой нубы-дизайнеры без знания кода смогли бы городить поведения.

Мои хотелки и бекграунд требовали немного иного:
1. Хочу задавать весь AI в коде.
2. Долгое время я прототипировал AI в движке первой Мафии, а там все скрипты по дефолту были coroutine'ами, и для меня было естественным описывать растянутое во времени поведение ими, наподобие:
Код:

персонаж_идисюда точка1
персонаж_посмотрина точка2
персонаж_анимация "чешет репу"
персонаж_идисюда точка3

3. Не хочу переписывать с нуля огромную FSM, которая у меня уже была.

Из этих условий у меня вышел странный code-driven гибрид стейтмашины и BT:

У бота по-прежнему есть стейты. и он может находится только в одном. Стейт представляет собой корутину, идущую циклом.
Каждый тик AI (0.5 сек у меня) сначала заполняются переменные бота тем, что он видит/слышит, а затем на основе перменных проверяется дерево, схожее с BT, но цель которого - выбрать стейт.
Начало дерева выглядит так:

Код:

                if (checkSlip()) {}
                else if (checkGetHit()) {}
                else if (checkCuffed()) {}
                else if (checkNoPath()) {}
                else if (checkDoorOnTheWay()) {}
                else if (checkRecentHit()) {}                                // autoclose off
                //else if (checkShouldUseElevator()) {}
                else if (checkRecentBulletHit()) {}
                else if (checkHandsUp()) {}
                else if (checkAir()) {}
                else if (checkShooterVisible()) {}
                else if (checkRecentGunshot()) {}
                else if (checkShouldReload()) {}
                else if (checkPickupsVisible()) {}
                else if (checkManiac()) {}
                else if (checkKillerVisible()) {}
                else if (checkDealWithBulletHit()) {}
                else if (checkArrestVillainVisible()) {}
                else if (checkThiefVisible()) {}
                else if (checkMDVisible()) {}
                else if (checkIntruder()) {}
                else if (checkDealWithVillain()) {}
                else if (checkRecentScream()) {}
                else if (checkPhysHitLight()) {}
                else if (checkEvacuate()) {}
                else if (checkPhysThrowerVisible()) {}
                else if (checkMDSound()) {}
                else if (checkUncertainVillainVisible()) {} // autoclose on
                else if (checkVoice()) {}
                else if (checkConversation()) {}
                else if (checkSusp()) {}
                else if (checkCrouchedVisible()) {}
                else if (checkSemafor()) {}
                else if (checkRecentFootstep()) {}
                else if (checkBody()) {}
                else if (checkBloodVisible()) {}
                else if (checkInspectLoudSounds()) {}
                else if (checkDealWithBody()) {}
                else if (checkCamShooting()) {}
                else if (checkSuspPickup()) {}
                else if (checkInspectFootsteps()) {}
                else if (checkSecure()) {}
                else if (checkEscortVillain()) {}
                else if (checkPhone()) {}


Это его часть. Проверки написаны по приоритетам - от важных до неважных. Это проверки базового AI, ниже там могут быть ещё проверки каждого уникального бота свои.

Внутри каждого чека ветвление более частное, например:

Код:

        bool checkThiefVisible()
        {
                if (ignoreVillains) return false;
       
                if ((villainVisible >= 0) && (!villainUncertain[villainVisible]))
                {
                        if (guardVisible>=0)
                        {
                                if (!villainReported[villainVisible])
                                {
                                        if ((behaviour<=behType.civil) || (!HasAnyLoadedWeapon()))
                                        {
                                                if (villainDanger[villainVisible]>VILLAIN_SUSP)
                                                {
                                                        if (!ignorePointToVillain)
                                                        {
                                                                changeState(State.pointToVillain);
                                                                return true;
                                                        }
                                                }
                                        }
                                }
                        }
               
                        if (behaviour!=behType.civil)
                        {
                                if (villainDanger[villainVisible]==VILLAIN_THIEF)
                                {
                                        if (Vector3.Distance(villain[villainVisible].transform.position, transform.position) < 2.5f)
                                        {
                                                changeState(State.dealWithThief);
                                                return true;
                                        }
                                        else
                                        {
                                                intruderVisible = villainVisible;
                                                changeState(State.catchIntruderRun);
                                                return true;
                                        }
                                }
                        }
                }
                return false;
        }



Если функция по частным проверкам не позволяет уйти в стейт - она возвращает false, и мы идём дальше вниз по списку первому.
Если она выбирает стейт и возвращает true, дерево заканчивается.

changeState проверяет, в каком мы стейте, если в том же самом - то ничего не делается, если в другом - то корутина прошлого резко дропается, и начинается новая.

Тут можно порассуждать, лучше ли делать так, или лучше иметь некую exitState функцию для каждого стейта (типа курил сигарету в стейте, а на выходе надо её выкинуть), сначала я делал второе, но это было мутно, т.к. нам, к примеру, не надо прятать пистолет при переходе из стрельбы в перезарядку, но надо при переходе в спокойное состояние, так что подумал что лучше заканчивать действия в начале самих стейтов.

Igor 03.10.2014 01:13

Ответ: Искусственный интеллект. Как?
 
Кстати, в state transition table можно записывать вероятность перехода в каждое другое состояние для имитации недетерминированного поведения.
Behavior trees по-сути, тоже являются конечными автоматами, но с ограничениями на структуру. Думаю, можно при реализации сохранить лучшее от обоих - сделать конечный автомат, но при организации связей считать, что одни состояния важнее других существует некая иерархия - например, в состоянии "атаковать" выделить подсостояния типа "стрелять", "перезаряжаться", "сменить позицию", недоступные для вызова извне. Как результат - структура переходов сильно упростится, и можно менять взаимодействие между подсостояниями, не боясь, что они используются где-либо ещё, а количество "главных" состояний резко уменьшится


Часовой пояс GMT +4, время: 22:05.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Перевод: zCarot