今日は半日 「WindBot」のソースを読んで、半日「WindBot」の起動に苦しんだ。
WindBotの概要
ADSとの関係性
UDPで双方向通信をしていて(よく読んだらTCPだったけど、本体はUDPだった気がするしもうわからん)、パケット情報をもとにお互い(ADSとWindBot)のプロセスが内部でデュエルの処理(カードの移動、LPの増減etc...)を行っている。
エントリポイント
Program.cs
。
そんなに大きなプロジェクトでもないので読みたいところから読んでも追えると思う。
大まかな仕組み
予めExecutor
にアクション(summon, activate, etc...)とその発動条件(bool関数)を登録してコンパイルしておく。
受け取ったパケット情報をもとにPhaseやChainを進め、そのたびにExecutors
(Listオブジェクト)をiterateして、発動条件が真を返したらその処理が行い、パケットを送信する。
WindBotの起動
起動に必要な引数たち
ADS/gframe/windbot.cppより(リポジトリ)
namespace ygo { #if defined(_WIN32) || defined(__ANDROID__) bool WindBot::Launch(int port, const std::wstring& pass, bool chat, int hand) const { #else pid_t WindBot::Launch(int port, const std::wstring& pass, bool chat, int hand) const { #endif #ifdef _WIN32 auto args = fmt::format( L"./WindBot/WindBot.exe HostInfo=\"{}\" Deck=\"{}\" Port={} Version={} name=\"[AI] {}\" Chat={} {}", pass, deck, port, version, name, chat, hand ? fmt::format(L"Hand={}", hand) : L""); STARTUPINFO si = {}; PROCESS_INFORMATION pi = {}; si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; if (CreateProcess(NULL, (TCHAR*)Utils::ToPathString(args).data(), NULL, NULL, FALSE, 0, NULL, executablePath.data(), &si, &pi)) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return true; } return false; ...
ADS自身がコマンドでコールしているので使えそう。
無駄な数時間
VS2019でデバッグすればいいのにprintf()
で出力しようとして、しかも文字化けして上手くいかない落とし穴。
デバッグ実行
例として、オルターガイストを起動するには
- HostInfo="" (まさかの空文字列)
- Deck="Altergeist"
- Port=7911
- Version=590188
- Name="AI"
- Chat=False
- Hand=0
の引数でWindBotを起動すればよい。
無事ホスト側のADSに「ピンポーン」とAIが入ってきて、めでたしめでたし。
WindBotの活用法
遊戯王システムのプログラミングをしなければならないと思い、個人の限界を感じてプロジェクトをなかったことにしようと思った所、以前途中でやめたWindBotのソース解析を思い出した。
要は、各カードに対するbool判定を学習可能な関数で置き換えれば良いので、私の仕事はAIの脳みそを作るだけになった。やったね。
既存Botの問題点
変化しない条件分岐
状況が変わろうとも、何回対戦しようとも分岐条件が変わらない。
そりゃ人間が勝つに決まってる。
決まったカードしか扱えない
何度も対戦したらデッキ構成がバレて、駆け引きが減少する。
デッキの動的な変化にも対応できる汎用AIを作る予定。