[Rumble-jp-dev] RobocodeEngineとチーム戦について(長文)

Back to archive index

Laplace lapla****@csc*****
2003年 2月 15日 (土) 00:33:21 JST


laplaceです。

長文&簡易ソース付ですいません。

ランダム環境やチーム戦のリーグなどを提案したので、
それらが実現可能か、できるならRoboLeagueを改良して
試してみようと思っていたのですが、
調査段階でRoboLeagueではだめだということがわかり自作中です。
(自作中のものはできれば公開したいですね。
rumblejpの新大会の機能やバトルを自宅でも実行できる
RoboLeagueみたいなのになればいいなぁと思っています。)

そこで、Teamをどのように取得するかを調べていたところ、
RbocodeEngineを使用すると取得できないことが分かり、
また、robocode.controlパッケージを使用するとTeamの対戦を
連続的に行うことがだめであることが分かりました。

現状のrumblejpシステムでも、これらのパッケージや
クラスをベースに作られているので新大会でチーム戦を
行うことは不可能ということになります。
とりあえずは、アゲインを考えるということで
改造しないというのもいいですが、robocode.controlパッケージの
使用するのを避ける方向でシステムは作成しませんか?
Team関係のデータは作らないが、エンジン部分はTeamも動かせるように
しておくだけです。
Robocode自体もこのパッケージを使用しておらず(デモのみに使用)、
RoboLeagueが使用しているだけです。
おそらくチームの考えを導入する前の遺産パッケージだと思います。

とりあえず、代替案を考え作成してみました。(動作確認まではできてません)
下記のように変更すると、ロボットの詳細がかなり多く取得できるようになり、
ファイル書き込み型のロボだとか、TeamRobot,AdvancedRobot,Droidを
継承しているとかTeamLeaderかどうかなども取得できるようになります。
これらを取得するとなると、SQLに保存しているデータも変える必要があります。
このようなデータがいるかどうかは皆さんで考えて、
変更する必要があるか決めましょう。決めてください。

ローカルで動作確認が出来ないので、CVSのソースを変更して
コミットするのはかなり気がひけます。

とりあえず、エンジン部分の変更を下記に記しておきますが、
まだ読んでもらわなくてもかまいません。
忘れないうちに記しておくだけです(^^;;;

// 変更方法 ---------------

BattlefieldSpecificationとBattleSpecificationは、
robocode.battle.BattlePropertiesを使用するようにする。
BattleSpecificationは設定するだけで、使用されていないようなので
すぐに消せそうだったが、RobocodeListenerに定義されている
メソッドの引数になっているので、
使用しているところがあるなら注意が必要。

robocode.control.RobotSpecificationは、コンストラクタが
パッケージプライベートでなければそのまま使えたのだが…。
robocode.repository.FileSpecification
(RobotSpecification,TeamSpecification)を使用するようにするため
各データやMapなどの変更が必要。
クラス名が同じなので、ほとんど変更の必要がないが
importがどちらのクラスになっているか注意する。

RobocodeEngineは使用せず、RobocodeManagerを使用し、
セキュリティなどの初期化だけをまねする。
RobocodeManagerは、新インターフェイス(BattleStatusListener)が
使用できるようなBattleManagerを返すようにオーバーライドする必要がある。
getLocalRepositoryは、getRobotRepositoryManager()を
使用して同様のメソッドを作成する。
このときrobocode.repository.(Robot,Teamの)
FileSpecificationを返すようにする。
(取り出せる状態の物をそのまま返すだけでよい)
また、BattleManagerでRobocodeListenerのbattleCompleteを
呼び出さないようにsendResultsToListenerメソッドを
オーバーライドする必要がある。

RobocodeListenerは、RobocodeManagerになにか与える
必要があるので(nullではだめ)、引数のデータを変更して
新インターフェイスを呼び出すアダプタを作成する。

バトルの開始は、
manager.getBattleManager().startNewBattle(BattleSpecification)ではなく
manager.getBattleManager().startNewBattle(BattleProperties,boolean)
にする。

RobotResultsは、robocode.control.RobotSpecificationが使用で
きなくなったので別のもので同等+αのものを作成する。
rumblejpで定義されているものとほぼ同じなのでそれを使ってもよい。

// ソースサンプル --------------

ここから下はソースです。
ほとんどそのままで使えると思いますが、
変更決定後に、もう少し吟味して現状のものに追加しようと思います。
ざっとみてすぐわかる人はすでに作成した経験のある
黒澤さんだけかも知れませんが、興味がある人は読んでみてください。

// 現在のRobocodeListnerの代わり
public interface BattleStatusListener {
  public abstract void battleComplete(BattleProperties properties,
  BattleResult[] results);
  public abstract void battleMessage(String message);
  public abstract void battleAborted(BattleProperties properties);
}

public class BattleResult implements Serializable {
 // 現在のデータとあわせるならIDだが
 // 取得できないので、どこかで変換する必要あり
  private String name;
  private int rank;
  private int score;
  private int survival;
  private int lastSurvivorBonus;
  private int bulletDamage;
  private int bulletDamageBonus;
  private int ramDamage;
  private int ramDamageBonus;
  private int firsts;
  private int seconds;
  private int thirds;
 // その他いろいろ取得可能、精度はdoubleで取得できる

  protected BatteleResult(int rank,String name,
  ContestantStatistics contestantStatistics){
  // 各値に設定
  }

  // 各ゲッターメソッド
}

public class RobotResult extends BattleResult
 implements Serializable {
  public static final int NORMAL_ROBOT_TYPE   = 0x00000001;
  public static final int ADVANCED_ROBOT_TYPE = 0x00000002;
  public static final int TEAM_ROBOT_TYPE     = 0x00000004;
  public static final int DROID_TYPE          = 0x00000010;
  public static final int TEAM_LEADER_TYPE    = 0x00000020;
  public static final int IO_ROBOT_TYPE       = 0x00000100;

  private int robotType;

  public RobotResult(int rank,RobotPeer robotPeer){
    super(rank,robotPeer.getName(),robotPeer.getStatistics());
  // roboTypeの設定
 }
 // ロボットタイプの取得
}

//チーム拡張用の結果データ、アゲインでは不要
public class TeamResult extends BattleResult
 implements Serializable {
  // チームのロボット個々の結果
  private RobotResult[] robotResults;

  public TeamResult(int rank,TeamPeer teamPeer){
    super(rank,teamPeer.getName(),teamPeer.getStatistics());
  // チームメンバーのスコア設定
 }
}

// robocode.controlパッケージを極力使用しないようにするための
// 拡張クラス群
public class AdvancedRobocodeManager extends RobocodeManager {
  private BattleStatusListener battleStatusListener;
  private AdvancedBattleManager advancedBattleManager;

  public AdvancedRobocodeManager(boolean slave,
  BattleStatusListener listener){
    super(slave,new DummyAdapter());
    battleStatusListener = listener;
  }

  public BattleManager getBattleManager(){
    if(advancedBattleManager == null){
      advancedBattleManager = new AdvancedBattleManager(this);
    }
    return advancedBattleManager;
  }

  class DummyAdapter implements RobocodeListner {
    public void battleComplete(
   BattleSpecification battlespecification,
   RobotResults arobotresults[]){
      throw new RuntimeException(
   "RobocodeListner.battleComplete should not be invoked.");
    }

    public void battleAborted(BattleSpecification battlespecification){
      if(battleStatusListener!=null){
        battleStatusListener.battleAborted(
     getBattleManager().getBattleProperties());
      }
    }

    public void battleMessage(String s){
      if(battleStatusListener!=null){
        battleStatusListener.battleMessage(s);
      }
    }
  }

  class AdvancedBattleManager extends BattleManager {
    public AdvancedBattleManager(RobocodeManager manager){
      super(manager);
    }

  // 上記アダプタのようにしても良いが、
  // この関数を実行されると処理が無駄になるので
    // listener(RobocodeListener).battleCompleteが
  // 使用されないように、オーバーライドする
    public void sendResultsToListener(Battle battle,
      RobocodeListener listener){
      if(battleStatusListener == null){
        return;
      }
   
   // 結果の取得
      ContestantPeerVector orderedContestants =
        (ContestantPeerVector)battle.getContestants().clone();
   // スコア順にソートされる
      orderedContestants.sort();

      BattleResult[] results = new BattleResult[orderedContestants.size()];
      for(int i=0;i<results.length;i++){
        ContestantPeer cp = orderedContestants.elementAt(i);
        if(cp instanceof TeamPeer){
          // チーム全体とチームそれぞれのロボットのスコアを獲得
          results[i] = new TeamResult(i+1,(TeamPeer)cp);
        }
        else if(r instanceof RobotPeer){
          // 結果や各種データの設定
          results[i] = new RobotResult(i+1,(RobotPeer)cp);
        }
        else {
          // ...
          throw new RuntimeException("Unknown Peer.");
        }
      }

      battleStatusListener.battleComplete(getBattleProperties(), results);
    }
  }
}

// 現在使用中のエンジンを変更するならこんな感じか?
public class AugmentedRobocodeEngine implements BattleStatusListener {
  private RobocodeManager manager = null;

  // rumblejpではMapなのでそれにあわせるほうがいいが、
 // Map の valueはrobocode.repository.RobotSpecificationにする
  private Map robots = new HashMap();

  public AugmentedRobocodeEngine(File rootDir){
    manager = new AdvancedRobocodeManager(false,this);
    initSecurity();
  }
 
 // アゲインで使用する場合はほとんど変更なし
  public robocode.repository.RobotSpecification[] getLocalRepository(){
    Repository robotRepository =
      manager.getRobotRepositoryManager().getRobotRepository();
    FileSpecificationVector v =   robotRepository.
      getRobotSpecificationsVector(false, false, true, false, false, true);
    robocode.repository.RobotSpecification robotSpecs[] =
      new robocode.repository.RobotSpecification[v.size()];
    for(int i = 0; i < robotSpecs.length; i++){
      robotSpecs[i] =
(robocode.repository.RobotSpecification)v.elementAt(i);
    }
    return robotSpecs;
  }
 ・・・
  その他は最初のほうに記した変更方法に沿って修正を加える。
 リーグ作成のJSPぐらいまでに影響があるのかな?

 かなり長くなってしまいました、すいません。





Rumble-jp-dev メーリングリストの案内
Back to archive index