Robocodeの最初のステップ

要求されたとおり、この記事を「Robocodeのチャンピオンになったきっかけ」という記事へのコメントに書き込み、ロシア語を話す開発者にRobocodeに注意を引き付けるために作業を続けました。 Robocodeはプログラマ向けのゲームであり、タスクはタンク制御システムを開発することです。 会話の内容を示すために、シードのクリップをいくつか提供します。









はじめに



この記事では、読者はアルゴリズムとオブジェクト指向プログラミングの一般原則に精通しているだけでなく、Java言語に精通している、または他の言語の知識をそれに投影できることを意味します。



Robocodeには4つの公式トーナメントがあります。



決闘と戦いには4つのウェイトカテゴリがあります。



一般的な重量のカテゴリで決闘のロボットを書く最初のステップを検討します。 実際、この記事のロボットはミクロのカテゴリーに当てはまりますが、バイトコードサイズの最適化は科学全体であり、その知識は成功したナノボットの開発に必要であり、この記事では一切開示されていません。



準備する



チャンピオンのプログラミングを開始するには、少なくともダウンロードしてインストールする必要があります。



また、ダウンロードとインストールを強くお勧めします。





ゲーム物理学



なぜなら このセクションとともに、記事は非常に大きくなり、このセクションは非常に高度であるため、最初のステップを実行するために読んで理解する必要はありません。別の記事に掲載します。



UPD:物理学に関する記事を公開http://habrahabr.ru/post/147956/



ロボット制御と外界との相互作用



一般的に、ゲームにはクラスの階層全体があり、そこから継承してロボットを作成できます。 一度に1つのコマンドのみを実行できる「単純な」robocode.Robotクラスがあります。 移動、回転、または撃ちます(ただし、ボーナスがあります-そのようなロボットは壁との衝突によるダメージを受けません)。 しかし、私の意見では、そのようなロボットのプログラミングはより逆であり、私はrobocode.AdvancedRobotクラスから継承することをお勧めします(将来的にはあなたが私の推奨に従ったという仮定から進めます)。 robocode.TeamRobotクラスも面白くないため、コマンドを作成できますが、これは別の記事の別のトピックです。



ロボットは多くのコマンドで制御されますが、最も重要なもののみを示します。





各ターンの開始時に、ロボットは最後のコマンドの実行中に発生した一連のイベントを受け取ります。 多くのイベントがありますが、最も重要なイベントのみを紹介します。





組立準備



何らかの理由でアセンブリにantを使用しないことに決めた場合は、このセクションをスキップできます。 ここからリポジトリをダウンロードできます: https : //github.com/aleksey-zhidkov/HabrahabrTutorial 、または自分の手で次のフォルダー構造を作成します。

 / HabrahabrTutorial
 +-/ src
    -build.properties
    -build.xml




build.propertiesファイルの内容は次のとおりです。

  bin.dir = bin
 builds.dir =ビルド
 robocode.dir =;  Robocodeホームディレクトリへのパス、デフォルトではC:\\ Robocode(Javaプロパティ形式に注意を払う必要があり、スラッシュはエスケープする必要があります)
 robocode.jar = $ {robocode.dir} \\ libs \\ robocode.jar 




build.xmlファイルの内容は次のとおりです。

<?xml version="1.0" encoding="UTF-8"?> <project name="HabrahabrTutorial" basedir="." default="release"> <property file="build.properties"/> <property name="robot.version" value="0.1"/> <property name="robot.package" value="ru.jdev.habrahabr"/> <property name="robot.path" value="ru/jdev/habrahabr"/> <property name="robot.name" value="HabrahabrTutorial"/> <path id="src.files"> <pathelement location="src"/> </path> <target name="init"> <mkdir dir="${bin.dir}"/> </target> <target name="compile" depends="init" description="Compiles source files"> <javac destdir="${bin.dir}" debug="on" debuglevel="lines,vars,source" optimize="yes" target="1.6"> <src refid="src.files"/> <classpath> <pathelement location="${robocode.jar}"/> </classpath> </javac> </target> <target name="clean" description="Deletes all previous build artifacts"> <delete dir="${bin.dir}"/> </target> <target name="release" depends="clean, compile"> <copy todir="${bin.dir}"> <fileset dir="src"/> </copy> <echo file="${bin.dir}/${robot.path}/${robot.name}.properties">robocode.version=1.7.3 robot.java.source.included=true robot.version=${robot.version} robot.author.name=Alexey jdev Zhidkov robot.classname=${robot.package}.${robot.name} robot.name=${robot.name} robot.description=Tutorial robot for habrahabr.ru </echo> <jar destfile="${builds.dir}\${robot.package}.${robot.name}_${robot.version}.jar" compress="true"> <fileset dir="${bin.dir}"/> </jar> <copy todir="${robocode.dir}\robots\"> <fileset file="${builds.dir}\${robot.package}.${robot.name}_${robot.version}.jar"/> </copy> <delete includeEmptyDirs="true"> <fileset dir="${bin.dir}" includes="**/*"/> </delete> </target> </project>
      
      







すべてを正しく行った場合、プロジェクトのルートでantコマンドを実行すると、HabrahabrTutorial \ builds \ ru.jdev.habrahabr.HabrahabrTutorial_0.1.jarファイルが表示されるはずです。このファイルには、ru \ jdev \ habrahabr \ HabrahabrTutorial.propertiesファイルが含まれているはずです。 (Javaから遠く離れている人のために、jarは任意のアーカイバで開くことができる通常のzipアーカイブであることを説明します)。



ロボットを作成する



前のセクションのディレクトリで選択した開発環境でプロジェクトを作成し、ライブラリ$ {robocode_home} /libs/robocode.jarをそれに接続します。 次に、ロボット自体を含むフォルダーにコードをコンパイルするか、アセンブリ中に前のセクションのスクリプトを使用するように環境を構成します。 最後に、次のコードで新しいクラスru.jdev.habrahabr.HabrahabrTutorialを作成します。



 package ru.jdev.habrahabr; import robocode.AdvancedRobot; public class HabrahabrTutorial extends AdvancedRobot { @Override public void run() { while (true) { /** *      ,            *        */ execute(); } } }
      
      







ゲームを起動し、[バトル]-> [新規]を選択し、ru.jdev.habrahabr.HabrahabrTutorialがロボットのリストに表示されることを確認します。



ステップ1:死後に退出し、敵の位置を計算し、レーダーを制御して引きます。



コードを次の形式にします(ここで、すべてのコメントはコードで直接指定されます。また、以降、追加された行またはメソッドはコメント/ * + * /でマークされ、変更された行またはメソッドはコメント/ *〜* /でマークされます):

 package ru.jdev.habrahabr; import robocode.AdvancedRobot; import robocode.DeathEvent; import robocode.ScannedRobotEvent; import robocode.util.Utils; import java.awt.*; import java.awt.geom.Point2D; import static java.lang.Math.signum; import static java.lang.Math.toRadians; public class HabrahabrTutorial extends AdvancedRobot { /*+*/private static final double RADIANS_5 = toRadians(5); /*+*/private boolean isAlive = true; /*+*/private double enemyX = -1; /*+*/private double enemyY = -1; @Override public void run() { /*+*/setTurnRadarRightRadians(Double.POSITIVE_INFINITY); //          /*~*/while (isAlive) { //         true,       /*+*/if (enemyX > -1) { //    /*+*/final double radarTurn = getRadarTurn(); /*+*/setTurnRadarRightRadians(radarTurn); /*+*/} /** *      ,            *        */ execute(); } } /*+*/private double getRadarTurn() { //       //     : final double alphaToEnemy = angleTo(getX(), getY(), enemyX, enemyY); //  ,     ,      (Utils,    Robocode ): final double sign = (alphaToEnemy != getRadarHeadingRadians()) ? signum(Utils.normalRelativeAngle(alphaToEnemy - getRadarHeadingRadians())) : 1; //  5         return Utils.normalRelativeAngle(alphaToEnemy - getRadarHeadingRadians() + RADIANS_5 * sign); //  ,     setTurnRadarRightRadians,          //    } @Override /*+*/public void onScannedRobot(ScannedRobotEvent event) { /** ScannedRobotEvent       , ,   ,   *  ,  (    -,     )     */ //     final double alphaToEnemy = getHeadingRadians() + event.getBearingRadians(); //     enemyX = getX() + Math.sin(alphaToEnemy) * event.getDistance(); enemyY = getY() + Math.cos(alphaToEnemy) * event.getDistance(); } @Override /*+*/public void onDeath(DeathEvent event) { isAlive = false; } @Override /*+*/public void onPaint(Graphics2D g) { // ,      //       ,            //       Paint if (enemyX > -1) { g.setColor(Color.WHITE); g.drawRect((int) (enemyX - getWidth() / 2), (int) (enemyY - getHeight() / 2), (int) getWidth(), (int) getHeight()); } } /** *  Robocode    - 0        : * 90 - , 180 - , 270 - , 360 - . * <p/> * -          . *  ,      , , ,    */ /*+*/private static double angleTo(double baseX, double baseY, double x, double y) { double theta = Math.asin((y - baseY) / Point2D.distance(x, y, baseX, baseY)) - Math.PI / 2; if (x >= baseX && theta < 0) { theta = -theta; } return (theta %= Math.PI * 2) >= 0 ? theta : (theta + Math.PI * 2); } }
      
      







ステップ2:はじめに



ランダムな軌道運動の初歩を理解します。 オービタルとは、敵が静止し、ランダムに常に一方向を与える場合、戦車が敵の周りを旋回することを意味します。 そして、ランダムなコンポーネントは、私たちの戦車をもう少し難しいターゲットにします。 この動きを実装するには、2つのメソッドを追加します。

 private double getDistance() { //     return 200 - 400 * random(); } private double getBodyTurn() { //       final double alphaToMe = angleTo(enemyX, enemyY, getX(), getY()); //      (  ,  ) ... final double lateralDirection = signum((getVelocity() != 0 ? getVelocity() : 1) * Math.sin(Utils.normalRelativeAngle(getHeadingRadians() - alphaToMe))); //     final double desiredHeading = Utils.normalAbsoluteAngle(alphaToMe + Math.PI / 2 * lateralDirection); //     final double normalHeading = getVelocity() >= 0 ? getHeadingRadians() : Utils.normalAbsoluteAngle(getHeadingRadians() + Math.PI); //     return Utils.normalRelativeAngle(desiredHeading - normalHeading); }
      
      







そして、メインループで敵が検出されたという条件内で、次の行を追加します。

  setTurnRadarRightRadians(radarTurn); /*+*/ final double bodyTurn = getBodyTurn(); /*+*/ setTurnRightRadians(bodyTurn); /*+*/ /*+*/ if (getDistanceRemaining() == 0) { /*+*/ final double distance = getDistance(); /*+*/ setAhead(distance); /*+*/ } }
      
      







ステップ3:発射!



最も単純な照準アルゴリズムを実装し、敵の現在の位置を撃ちます。 この目標を達成するために、ロボットは常に敵を銃口に保ち、できるだけ早く撃ちます。 タスクは1つの方法で実装されます。

 private double getGunTurn() { //  :       ,      : return Utils.normalRelativeAngle(angleTo(getX(), getY(), enemyX, enemyY) - getGunHeadingRadians()); }
      
      







そして、メインループに3行追加します。

  setAhead(distance); } /*+*/ final double gunTurn = getGunTurn(); /*+*/ setTurnGunRightRadians(gunTurn); /*+*/ setFire(2); }
      
      







次は何ですか



そして、ロボットにようこそ、またはこのトピックに興味のあるコメントを書いてください。ゲームのすべての基本的なテクニックを徐々に強調していきます。 この投稿を最後までマスターした皆さんに感謝します。



PSこれは私の最初のチュートリアルですので、建設的な批判に喜んでいます



All Articles