RealTimeBattle

ホームページ

RealTimeBattle はプログラムしたロポット同士を対戦させるゲームです。 ホームページには Windows 用のサーバプログラムとドキュメントなどがあります。 但し、ここにあるものは Cygwin 用であり、またヴァージョン1.8として公開 されているものには必要な全てのファイルが含まれているわけではありません。

ロボットのプログラムとシステムとは標準入出力を介してメッセージをやりと りして通信を行うため、標準入出力を取り扱える任意のプログラミング言語を 使用することができます。 但し、単体の実行可能ファイルであることが必要なので、インタプリタや Java のように Virtual Machine が必要な場合は注意が必要です。 Windows ではバッチファイルで標準入出力を実行可能ファイルと同等に取り扱 えないため、 Java のプログラムを動作させるには exe ファイルにする必要 があります。 そのため、 gcj コンパイラを使用します。

なお、 gcj は java 1.5 相当ですが、使用できるクラスライブラリは限られ ます。 ここでは java.util.Scanner が使用できなかったので robo.Scanner クラス として相当クラスを作成しています。

インストール

ここでは、Windows 用 GTK 版にカスタマイズされた Realtimebattle と、 MinGW 用の Java 、さらに、この講義用に作成した、スケルトンクラスやユー ティリティクラスと、サンプルプログラムのインストールを行います。

gs.zip (142MB)をダウンロードし、適当なフォルダで解凍して ください。 すると、 RealTimeBattle のプログラム一式と、 thisiscool-gcc プロジェク トの gcj コンパイラと、私が作成したロボットのスケルトンクラスとサンプ ルプログラムが得られます。 使い方は README.txt を読んで下さい。

サンプルプログラム

もともと付属していた rotate_and_fire.cc をリファクタリングし、 Java に 移植した。 (C++ 用の不完全なリファクタリングした内容)

構成

robo.Constant.java
プログラム内で使用している各定数
robo.Robo.java
サーバであるシミュレータとの通信や、メッセージの解釈などを定義した Robo の定義。 メッセージを受けた時の動作は、仮実装としてなにもしない ({}) としている。 ロボットを作成する場合はこのクラスを継承し、各メソッドをオーバライドす る。
raf.java
サンプル実装例の RotateAndFire クラスの定義。 robo.Robo クラスを継承し、各動作のメソッドをオーバーライドして動作を記 述する。

詳細

Robo.java


package robo;
import java.util.*;
import static robo.Constant.*;
public class Robo {
    final private String robot_name;
    final private String robot_colour;
    public Robo(String name, String colour){

コンストラクタにロボット名と色を指定する。


...
    final public void robot_option(int option, int value ){
	output("RobotOption",option,value);
    }
    final protected void name(String name ){
	output("Name",name);
    }
    final protected void colour( String home, String away ){
	output("Colour",home,away);
    }
    final protected void rotate( int what, double vel ){
	output("Rotate",what,vel);
    }
    final protected void rotate_to(int what, double vel, double angle ){
	output("RotateTo",what,vel,angle);
    }
    final protected void rotate_amount(int what, double vel, double angle ){
	output("RotateAmount",what,vel,angle);
    }
    final protected void sweep(int what, double vel, double left,
			       double right ){
	output("Sweep",what,vel,left,right);
    }
    final protected void accelerate(double amount ){
	output("Accelerate",amount);
    }
    final protected void brake(double amount ){
	output("Brake",amount);
    }
    final protected void shoot(double energy ){
	output("Shoot",energy);
    }
    final protected void print(String message ){
	output("Print",message);
    }
    protected void debug(String message ){
	output("Debug", message);
    }
    final protected void debug_line(double start_angle,double start_radius,
			      double end_angle, double end_radius ){
	output("DebugLine",start_angle,start_radius,end_angle,end_radius);
    }
    final protected void debug_circle(double center_angle, double center_radius,
                                double circle_radius ){
	output("DebugCircle",center_angle,center_radius,circle_radius);
    }

これらは、実際のロボットの実装において使用するロボットのコマンドになる。 rotate, accelerate, break, shoot などがある。 これらのコマンドの意味はすべて理解すべし。


...
    final protected void game_option( int option, double value ){
	switch( option ){
	case ROBOT_MAX_ROTATE:
	    robot_max_rotate = value;
	    break;
	case ROBOT_CANNON_MAX_ROTATE:
	    robot_cannon_max_rotate = value;
	    break;
	case ROBOT_RADAR_MAX_ROTATE:
	    robot_radar_max_rotate = value;
	    break;
	case ROBOT_MAX_ACCELERATION:
	    robot_max_acceleration = value;
	    break;
	case ROBOT_MIN_ACCELERATION:
	    robot_min_acceleration = value;
	    break;
	case ROBOT_START_ENERGY:
	    robot_start_energy = value;
	    break;
	case ROBOT_MAX_ENERGY:
	    robot_max_energy = value;
	    break;
	case ROBOT_ENERGY_LEVELS:
	    robot_energy_levels = value;
	    break;
	case SHOT_SPEED:
	    shot_speed = value;
	    break;
	case SHOT_MIN_ENERGY:
	    shot_min_energy = value;
	    break;
	case SHOT_MAX_ENERGY:
	    shot_max_energy = value;
	    break;
	case SHOT_ENERGY_INCREASE_SPEED:
	    shot_energy_increase_speed = value;
	    break;
	case TIMEOUT:
	    timeout = value;
	    break;
	case DEBUG_LEVEL:
	    debug_level = value;
	    break;
	case SEND_ROBOT_COORDINATES:
	    send_robot_coordinates = (int) value;
	    break;
	}
    }

各ゲームオプションの値が protected な変数に取得される。 ロボットの実装において参照すべき定数は、この値を参照すること。


...
    final public void check_messages(){
	quitting = false;
	String msg_name;
	Action act;
	Scanner sc = new Scanner(System.in);
	while( sc.hasNext() && !quitting ){
	    msg_name = sc.next();
	    act = msgToRobot.get(msg_name);
	    if(act!=null){
		act.perform(sc);
	    }
	}
    }

ここがメッセージを処理する主プログラムになる。 java.util.Scanner を利用しているため、リアルタイムな反応ではなく、ロボッ トサーバからの入力値がバッファされ、値が得られる度に状況に応じたメソッ ドが呼び出される。


    final protected boolean is_quitting () { return quitting; }

ゲームの終了を判定する関数。


...
    // Override the following method to control your robot
    protected void your_name( String prev_name ){}
    protected void your_colour(String colour ){}
    protected void game_starts            (){}
    protected void radar_noobject (double dist,double angle ){}
    protected void radar_robot    (double dist,double angle ){}
    protected void radar_shot     (double dist,double angle ){}
    protected void radar_wall     (double dist,double angle ){}
    protected void radar_cookie   (double dist,double angle ){}
    protected void radar_mine     (double dist,double angle ){}
    protected void coordinates    (double x,double y,double rotation){}
    protected void info           (double time,double speed,
				   double cannon_angle ){}
    protected void robot_info     (double energy, int enemy ){}
    protected void rotation_reached(int what ){}
    protected void energy          (double energylevel ){}
    protected void robots_left     (int number_of_robots ){}
    protected void collision_noobject     (double angle ){}
    protected void collision_robot        (double angle ){}
    protected void collision_shot         (double angle ){}
    protected void collision_wall         (double angle ){}
    protected void collision_cookie       (double angle ){
	print("Cookie eaten!");
    }
    protected void collision_mine         (double angle ){
	print("Oh no! A mine");
    }
    protected void dead                   (){}
    protected void game_finishes          (){}

これらがサーバからのメッセージによって呼び出されるメソッド。 これらをオーバライドしてロボットに動きをつける。


    // available the following variables to recognize your robot's condition
    protected  double radar_and_cannon_rotate;
    protected  double robot_rotate;
    protected  double acceleration;
    protected  double brake_value;
    // All game options are stored in these variables.
    protected  double robot_max_rotate;
    protected  double robot_cannon_max_rotate;
    protected  double robot_radar_max_rotate;
    protected  double robot_max_acceleration;
    protected  double robot_min_acceleration;
    protected  double robot_start_energy;
    protected  double robot_max_energy;
    protected  double robot_energy_levels;
    protected  double shot_speed;
    protected  double shot_min_energy;
    protected  double shot_max_energy;
    protected  double shot_energy_increase_speed;
    protected  double timeout;
    protected  double debug_level;
    protected  int send_robot_coordinates;
}

ロボットの状態を示す変数や定数。 これらを参照してロボットを操作する。

robo.Constant.java


package robo;
public class Constant {
    final public static int number_of_object_types = 5;
    // 0 - no messages
    // 1 - messages when RotateTo and RotateAmount finished
    // 2 - messages also when sweep direction is changed

    // robot_option_type
    final public static int SIGNAL=2;
    // 0 - no signal, > 1 - signal to send (e.g. SIGUSR1 or SIGUSR2) 
    final public static int SEND_SIGNAL=0;
    // 0 - false, 1 - true   
    final public static int SEND_ROTATION_REACHED=1;
    // 0 - no messages
    // 1 - messages when RotateTo and RotateAmount finished
    // 2 - messages also when sweep direction is changed
    final public static int USE_NON_BLOCKING=3;
    // 0 - false, 1 - true 
    // This option should always be sent as soon as possible

    // message_to_robot_type 
    final public static int NOOBJECT = -1;
    final public static int ROBOT = 0;
    final public static int SHOT = 1;
    final public static int WALL = 2;
    final public static int COOKIE = 3;
    final public static int MINE = 4;
    final public static int LAST_OBJECT_TYPE = 5;
    // warning_type
    final public static int UNKNOWN_MESSAGE=0;
    final public static int PROCESS_TIME_LOW=1;
    final public static int MESSAGE_SENT_IN_ILLEGAL_STATE=2;
    final public static int UNKNOWN_OPTION=3;
    final public static int OBSOLETE_KEYWORD=4;
    final public static int NAME_NOT_GIVEN=5;
    final public static int COLOUR_NOT_GIVEN=6;
    // game_option_type
    final public static int  ROBOT_MAX_ROTATE=0;
    final public static int  ROBOT_CANNON_MAX_ROTATE=1;
    final public static int  ROBOT_RADAR_MAX_ROTATE=2;
    final public static int  ROBOT_MAX_ACCELERATION=3;
    final public static int  ROBOT_MIN_ACCELERATION=4;
    final public static int  ROBOT_START_ENERGY=5;
    final public static int  ROBOT_MAX_ENERGY=6;
    final public static int  ROBOT_ENERGY_LEVELS=7;
    final public static int  SHOT_SPEED=8;
    final public static int  SHOT_MIN_ENERGY=9;
    final public static int  SHOT_MAX_ENERGY=10;
    final public static int  SHOT_ENERGY_INCREASE_SPEED=11;
    final public static int  TIMEOUT=12;
    final public static int  DEBUG_LEVEL=13;
    // 0 - no debug, 5 - highest debug level
    final public static int  SEND_ROBOT_COORDINATES= 14;
    // 0 - no coordinates, 
    // 1 - coordniates are given relative the starting position
    // 2 - absolute coordinates 
}

これらはサーバから送られてくるメッセージの数値と意味を対応付ける定義。 すべて Robo.java 内で処理されているので、理解しなくてもプログラミング 可能。

raf.java


import robo.Robo;
import static robo.Constant.*;
class RotateAndFire extends robo.Robo {
    public RotateAndFire(String name, String colour){
	super(name,colour);
    }

コンストラクタでは親クラス Robo のコンストラクタに名前と色が伝わるよう に指定する。


    private double ptwice(double x){
	if(x<0){
	    return 0;
	}
	return 2*x;
    }
    @Override
    public void game_starts(){
	initValue();
	rotate_allowed = true;
	shots_hit = 0;
	last_shot_hit_time = -2.0;
	current_time = 0;
	
	radar_and_cannon_rotate =
	    robot_cannon_max_rotate - ptwice(robot_rotate);
	rotate( 1, robot_rotate );
	rotate( 6, radar_and_cannon_rotate );
	accelerate( acceleration );
    }

    @Override
    public void radar_robot(double dist, double angle ){
	if( radar_and_cannon_rotate != - robot_rotate ){
	    radar_and_cannon_rotate = - robot_rotate;
	    rotate( 6, radar_and_cannon_rotate );
	}
	if( dist < 2 && acceleration != 0.0 ){
	    acceleration = 0.0;
	    accelerate( acceleration );
	    brake_value = 1.0;
	    brake( brake_value );
	    if( debug_level >= 1 ){
		debug("Brakes locked: Robot found and at distance "+dist);
	    }
	}
	shoot( 2 );
    }

レーダに弾が映った時呼び出されるメソッド。

  1. ロボットの回転と逆向きにレーダとキャノンを回す。
  2. 弾との距離が近く、しかも加速している時、加速を止め、ブレーキをかけ る。
  3. 2 の強さで弾を撃つ

...
    @Override
    public void info(double time, double speed, double cannon_angle ){
	current_time = time;
	if( current_time - last_shot_hit_time > 2.0 ){
	    shots_hit = 0;
	}
    }

受け取った情報を保存している。


...
    public static void main(String[] arg){
	Robo raf = new RotateAndFire("Rotate&Fire(new)", "9977dd");
	raf.robot_option( USE_NON_BLOCKING, 0 );
	raf.check_messages();
    }
}

実行用の static main メソッド。 ここでは名前と色を与えてコンストラクタを動かし、オプションを与えた後、 check_messages メソッドを呼び出す。 ここはコンストラクタの記述以外は変更する必要はない。


坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科