В день когда было объявлено о соревновании я яростно взялся за создание
бота для StarCraft на языке F#. F# отлично работает в таких областях
как AI, асинхронное программирование и всякие замечательные штуки.
Однако, я не стал писать об этом проекте раньше по этическим причинам.
Дело в том что API, используемое для взаимодействия с Broodwar напрямую нарушает лицензионное соглашение StarCraft.
Подумайте об этом — предоставлять API для программного доступа к
продукту может быть опасно. Разработка ботов идёт рука об руку со
взломом, эксплоитами и испорченным играм для нормальных игроков. Хотя
мои мотивы в модификации StarCraft ограничиваются написанием ботов для
собственного использования, я абсолютно уважаю желание Blizzard
предотвратить реверс-инжиниринг их программ.
Итак, как представитель Microsoft и человек уважающий лицензионные
соглашения, я не особенно распропространялся о своём проекте. Так было
пока юридический отдел Blizzard официально не разрешил проведение
упомянутого соревнования. Разрешите представить Вам моего
StarCraft-бота на F# (который, на самом деле, просто порт Java Proxy Bot).
Настройка необходимых программ
1. Купите и установите StarCraft
Первое, что следует сделать — купить и установить StarCraft и расширение BroodWar. Их можно раздобыть за $15 в онлайн-магазине Blizzard.
Обязательно установите StarCraft в легко доступную папку вроде
«E:\StarCraft». Иначе копирование файлов бота в Program Files
(доступную только администратору) будет очень непростым делом.
2. Скачайте и установите Chaos Launcher
Chaos Launcher
— инструмент, управляющий загрузкой и запуском StarCraft. Т.к.
оригинальная игра вышла в 1998, мы не станем винить её за то что она не
может нормально работать в оконном режиме. Но для разработки бота это
очень важный вопрос.
Для начала, распакуйте файл в папку, например "E:\StarCraft\Chaos
Launcher\”. Потом, чтобы проверить всё просто запустите
ChaosLauncher.exe. Отметьте «W-Mode plugin» и нажмите Start. Теперь Вы
можете играть в StarCraft в оконном режиме!
3. Установите BWAPI
BWAPI это открытое API для взаимодействия со StarCraft: BroodWar, и именно это API мы будем использовать в нашем боте.Скачайте BWAPI_Beta_2.6.1.zip
и распакуйте его в папку, например "E:\StarCraft\BWLAPI_Beta_2.6.1\”.
Заметьте, что Вам ещё необходимо пройти регистрацию. В файле Readme
перечислены следующие шаги:
Скопируйте содержимое папки ChaosLauncher в директорию, куда Вы установили Chaos Launcher ("E:\StarCraft\Chaos Launcher"\”)
Скопируйте содержимое папки StarCraft в Вашу папку StarCraft ("E:\StarCraft\”)
Скопируйте содержимое папки Windows в ваш системный каталог ("C:\Windows\”)
Представляю StarCraftBot 9K
Теперь все необходимые инструменты на месте, но написание бота для
программы, расширений к которой вообще не планировалось, будет
непростым делом. StarCraft Bot 9K имеет следующую архитектуру (которая,
в свою очередь, повторяет архитектуру Java ProxyBot, доступного на
сайте AIIDE).
Вы используете Chaos Launcher для запуска StarCraft
Когда начинается игра, код BWAPI исполняется каждый игровой ход
В каждый ход StarCraftConnector (С++) передаёт всю публично доступную информацию из игры.
Клиент StarCraftBot9K (C#) слушает эти события
И наконец сам StarCraftBot9K (F#) отвечает за принятие решений в игре.
4. Скачайте StarCraftBot9K
В самом конце этого поста указан zip-файл со всеми исходными кодами.
Скачайте его и распакуйте в папку, например "E:\StarCraftBot9K\”.
5. Установите StarCraftConnector
При сборке StarCraftBot9K одним из получившихся файлов будет
StarCraftConnector.dll — библиотека на C++, построенная поверх BWAPI,
которая отсылает сообщения о состоянии игры в сокет. После этого
приложение на C# слушает этот сокет и по мере надобности отправляет
сообщения игре StarCraft.
Чтобы «установить» StarCraftConnector скопиркйте его в
"E:\StarCraft\bwapi-data\AI\”. Эту папку Вы должны были скопировать
когда устанавливали BWAPI.
После этого откройте "E:\StarCraft\bwapi-data\bwapi.ini” и установите
поле ai_dll в значение "bwapi-data\AI\StarCraftConnector.dll”.
6. Запустите StarCraftBot9K
Наконец, Вы готовы к запуску StarCraftBot9K! Просто запустите
приложение. Оно начнёт слушать сокет пока StarCraftConnector не начнёт
отправку данных.
7. Запустите StarCraft
Теперь снова запустите Chaos Launcher, в этот раз включив модуль "BWAPI
Injector”. Заметьте, запускать его нужно от имени администратора.
Когда запустится StarCraft всё будет выглядеть как обычно, BWAPI и StarCraftConnector включаются только при начале Custom Game.
8. Передайте управление StarCraftBot 9K
Когда начнётся игра Вы должны увидеть что то наподобие этого. Это
отображение внутреннего состояния игры StarCraft в простом
WinForms-приложении. Сюда ещё придётся добавить много дополнительных
компонент — например объектную модель для отправки правильных команд
StarCraft'у.
Когда вы отметите галочку "Economy AI Module”, асинхронный поток F#
запустится и подключится к StarCraft, начав строительство Вашей базовой
экономики (строительство SCV, Дронов или Зондов и отправка их к
руднику.)
/// Модуль AI для управления экономикой. Постройка дронов, веспен-гейзеры и прочее let private getEconomyAI (mediator : GameMediator) = async {
while true do
let currentState = mediator.CurrentGameState
// Строить рабочих на все имеющиеся ресурсы if currentState.SupplyUsed < currentState.SupplyTotal && currentState.Minerals >= 50 && currentState.CanProduce.[ int (getWorkerType(g_GameMetadata.PlayerRace)) ] then
// БАГ: рабочий строится в первом попавшемся командном центре, это может быть не лучшим решением // В идеале у нас должна быть абстракция более высокого уровня "база" со статусом "активная" или "мало ресурсов" let cmdCenter = currentState.Units |> Seq.filter (fun unit -> unit.Player = g_GameMetadata.PlayerID) |> Seq.tryFind (fun unit -> isCommandCenter (enum<UnitID> unit.TypeID))
if Option.isSome cmdCenter then let cmdCenterID = cmdCenter |> Option.get |> (fun unit -> unit.ID) let cmd = TrainUnit(cmdCenterID, int (getWorkerType(g_GameMetadata.PlayerRace))) mediator.SendCommand(cmd)
// Посылаем незанятых рабочих к ближайшему минеральному полю for scunit in currentState.Units do // Если юнит наш, это рабочий и он ничего не делает, // отправить его к ближайшему полю минералов if scunit.Player = mediator.GameMetadata.PlayerID && isWorker scunit && scunit.OrderID = int (UnitOrder.PlayerGuard) then
let orderID = scunit.OrderID
let scunitLoc = { X = scunit.XPos; Y = scunit.YPos }
let mineralPatches = currentState.Units |> Seq.filter(fun unit -> unit.TypeID = int UnitID.MineralField) |> Seq.map(fun minPatch -> let patchLoc = { X = minPatch.XPos; Y = minPatch.YPos } scunitLoc.FlyingDistanceTo(patchLoc), minPatch) |> Seq.sortBy(fun (dist, minPatch) -> dist)
if mineralPatches <> Seq.empty then let targetMinPatch = mineralPatches |> Seq.head |> snd let cmd = RightClickUnit(scunit.ID, targetMinPatch.ID) mediator.SendCommand(cmd)
// Спим и проверяем позже. Если этот агент будет отменён, здесь мы жто увидим do! Async.Sleep(100) }
Пока что AI не представляет из себя ничего особенного. Но асинхронная
модель F#, reactive framework и события первого порядка позволяют
создать очень мощную и элегантную модель на основе асинхронных событий,
происходящих в игре.
К примеру, мы приказываем юниту вроде Голиафа атаковать противника. При
этом может случиться множество вещей! Голиаф может убить другого юнита,
тот юнит может убежать, Голиаф может погибнуть и так далее. И результат
Вашего действия Вы узнаете только спустя несколько секунд. Асинхронные
процессы в F# позволяют очень эффективно оборачивать такие асинхронные
действия.
let! result = tryEngage enemyUnit
match result with | MarineWasKilled -> printfn "Sad." | UnitWasKilled -> printfn "Horray! Now go find a medic to heal" | UnitRanAway -> printfn "Should you chase? Maybe..."
Функция tryEngage может быть просто объединением нескольких событий
(marine.OnKilled, unit.OnKilled и новое событие когда юнит отбегает
слишком далеко). Может быть пока эту концепцию сложновато понять. Я
постараюсь написать новый пост об этом позже.
Я прикрепил исходный код. Хотя возможно ему и требуется дополнительная
полировка, всё должно быть довольно понятно. Заметьте, что эта статья
идёт не с одним, а сразу с двумя предупреждениями!
Все образцы кода предоставляются «КАК ЕСТЬ» без каких либо
гарантий, явных либо подразумеваемых. Никаких гарантий что код подойдёт
для того, для чего Вы его планируете использовать. Другими словами,
если использование BWAPI приведёт к Вашему бану на Battle.net или
повредит Вашей системе, не ожидайте от меня сострадания.
Как видите, здесь очень много взаимодействующих частей. Я
рад предоставить некоторую техническую поддержку, но если Вы
отклолнитесь от указанной процедуры не ожидайте что я буду знать почему
<случайная штука> не работает или как устроен StarCraft изнутри.