Help us understand the problem. What is going on with this article?

Fighting ICEを動かしてみる

Fighting ICEを動かしてみる

TL;DR

格闘ゲームを題材にAIによるゲーム操作を試してみることができるFighting ICEを動かしてみました。
強化学習を利用すると面白いところですが、今回はとりあえず動かしてみるというところまでです。

Fighting ICEとは

Fighting ICEは、格闘ゲームに対してAIによるゲーム操作の開発および実行環境を提供してくれます。

強化学習の実行環境としてはOpenAI Gymが有名ですが、見た目に地味なゲームが多いため、格ゲーで動かせるのは視覚的にも楽しいです。

インストール方法

基本的にはGet started - Installに従って必要なファイルをダウンロードします。
必要なファイルは以下の通りです。

  • JDK・・・後述の注意事項があります。
  • Eclipse・・・Eclipseである必要はありませんが、IDEはあった方が便利です。
  • Fighting ICE・・・今回はVersion 4.30を使用しました。

Fighting ICEを適当なフォルダにダウンロードしたら、解凍してください。

JDKの注意事項

ホームページにも記載がありますが、JDKの選択は注意点があり、JDK 1.8ではないと動作しません(私はOracle JDKで試しましたが、1.8であればOpneJDKでも動作するかもしれません)。

JDK 11をインストールし、ライブラリだけJDK 1.8を指定しても動作しないため注意してください。

動かしてみる

Fighting ICEを解凍すると以下の様なフォルダ構成になっています。

Eclipseからの動かし方はGet Started - First Stepを参照してください。

ただ、開発時はともかく動かしてみる場合にEclipse経由では面倒ですので、ここではシェルスクリプトから起動出来るようにします。
Fighting ICEを解凍したフォルダ直下にrun_stage.shという名前で以下の内容のシェルスクリプトを作成します。

#!/bin/sh

JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home
JAVA_COMMAND=$JAVA_HOME/bin/java

$JAVA_COMMAND -cp "FightingICE.jar:lib/*:lib/lwjgl/*:lib/natives/macos/*" -XstartOnFirstThread Main

run_stage.shを実行すると以下の様にFighting ICEの起動画面が表示されます。

OSによって異なるライブラリ

今回実行環境がMacOSでしたのでlib/natives/macos/*を指定しました。この指定は実行環境となるOSによって異なります。
対応しているのはmacosの他に、linuxwindowsです。

MacOSで実行する際のVMオプションの指定

MacOSで実行する場合は-XstartOnFirstThreadの指定が必要でした。
他の環境では確認していないため、起動時にエラーがでたら同オプションの指定を試してみてください。

AIを作成してみる

開発はEclipseで行います。Get Started - First Stepに記載のあるとおり、Javaプロジェクトとして作成し、ライブラリとしてAIToolKit.jarを指定します。

後は新規クラスとしてaiinterface.AIInterfaceを継承したクラスを実装します。
インターフェイスとして定義されているメソッドをオーバーライドして独自のAIを実装します。

以下の実装は、単純に敵に向かって進み、近距離になったらランダムでパンチやキックを繰り出すプログラムになっています。

import aiinterface.AIInterface;
import struct.FrameData;
import struct.GameData;
import struct.Key;
import aiinterface.CommandCenter;
import java.util.Random;

public class MyFighter implements AIInterface {
    private Key inputKey;
    private boolean playerNumber;
    private FrameData frameData;
    private CommandCenter cc;
    private int lastDistance;
    private Random randomizer = new Random();
    private boolean isLastActionMoved = false;

    @Override
    public int initialize(GameData gameData, boolean playerNumber) {
        this.playerNumber = playerNumber;
        this.inputKey = new Key();
        this.cc = new CommandCenter();
        this.frameData = new FrameData();

        return 0;
    }

    @Override
    public void close() {
        // NOP
    }

    @Override
    public void getInformation(FrameData frameData) {
        this.frameData = frameData;
        this.cc.setFrameData(this.frameData, this.playerNumber);
    }

    @Override
    public Key input() {
        return this.inputKey;
    }

    @Override
    public void processing() {
        if (this.frameData.getEmptyFlag() || this.frameData.getRemainingFramesNumber() <= 0) {
            return;
        }

        // コマンドが入力済みの場合は、コマンドの内容を実行します。
        if (this.cc.getSkillFlag()) {
            this.inputKey = cc.getSkillKey();

            return;
        }

        this.lastDistance = frameData.getDistanceX();

        // 全開のコマンドが移動で、距離が引き続き離れている場合、移動を継続します。
        if (this.lastDistance >= 100 && this.isLastActionMoved) {
            return;
        }

        // 上記でコマンドが入力済みの場合は、継続してそのコマンドを実行する指定をしているため、
        // 別のコマンドを入力したい場合は、毎回手動でクリアーする必要があります。
        this.inputKey.empty();
        this.cc.skillCancel();

        if (this.lastDistance < 100) {
            int nextActionInt = this.randomizer.nextInt(3) + 1;

            switch (nextActionInt) {
            case 1:
                this.cc.commandCall("A");
                break;
            case 2:
                this.cc.commandCall("B");
                break;
            case 3:
                this.cc.commandCall("C");
                break;
            }

            this.isLastActionMoved = false;

            return;
        }

        if (this.lastDistance >= 100) {
            this.cc.commandCall("FORWARD_WALK");
            this.isLastActionMoved = true;

            return;
        }
    }

    @Override
    public void roundEnd(int arg0, int arg1, int arg2) {
        // NOP
    }

}

クラス名の指定や後述のexport時のJARファイル名については、以下の様な注意事項があります。

  • aiinterface.AIInterfaceを継承したクラス名がそのまま実行時に指定するAI名になります。
  • aiinterface.AIInterfaceを継承したクラスがデフォルトパッケージ以外に所属すると実行時に見つけられません。
  • クラス名とJARファイル名は一致している必要があります。

後はJARファイルとしてexportしてFighting ICEの所定のフォルダに配置することで、対戦時のAIとして選択することが可能になります。
exportで必要なのは*.classファイルのみです。

Eclpseでのexport方法はGet Started - First Stepを参照してください。

対戦してみる

exportしたJARファイルはFighting ICRdata/aiフォルダに配置してください。

対戦相手として2018 Competition ResultsSimpleAIを拝借しました。
同ページからall AIsをダウンロードして解凍し、Bots&Slides/simpleai/SimpleAI.jardata/aiに配置してください。

後はrun_stage.shを起動してAIとキャラクターを選択します。ちなみに決定キーはZキーです。

あとはPlayにカーソルを合わせて決定キーを押下すると対戦が始まります。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした