⬆️ ⬇️

First steps in robocode

I write this article at the request of the comments to the article “How I became the champion of Robocode” and continuing the work begun in it to draw attention to the Russian-speaking Robocode developers. Robocode is a game for programmers in which the task is to develop a tank control system. For starters, I’ll give a few clips to show what the conversation is about:









Introduction



In this article, I mean that the reader is familiar with the general principles of algorithms and object-oriented programming, as well as is familiar with the Java language, or is able to project their knowledge of other languages ​​into it.

')

Robocode has 4 official tournaments:



In the duel and the fight there are 4 weight categories:



I will consider the first steps in writing a robot for a duel in the total weight category. In fact, the robot from this article is in the micro-category, but optimization of the size of the bytecode is a whole science, knowledge of which is necessary for the development of successful nanobots and which is not disclosed in any way in this article.



Training



In order to start programming your champion, you at least need to download and install:



I also strongly recommend downloading and installing:





Game physics



Because together with this section, the article is too big, and the section is sufficiently advanced and should not be read and understood in order to take the first steps, I put it in a separate article, which I will publish in a short time.



UPD: published an article on physics http://habrahabr.ru/post/147956/



Robot control and interaction with the outside world



In general, the game has a whole hierarchy of classes, from which you can inherit to write your robot. There is a “simple” class robocode.Robot, which can only execute one command at a time, i.e. either move, or turn, or shoot (there is, however, a bonus - such robots do not receive damage from collisions with the walls). But in my opinion, programming such robots on the contrary is more complicated and I recommend (and will continue to proceed from the assumption that you followed my recommendation) to inherit from the class robocode.AdvancedRobot, which can control all parts at once in one move. It is also not without interest the class robocode.TeamRobot, which allows you to create teams, but this is a separate topic for a separate article.



The robot is controlled by many commands, but I will give only the most important ones:





At the beginning of each move, the robot receives a set of events that occurred during the execution of the last command. There are a lot of events and I will give only the most important of them:





Assembly preparation



If for some reason you decided not to use an ant for assembly, you can skip this section. You can download the repository from here: https://github.com/aleksey-zhidkov/HabrahabrTutorial , or manually create the following folder structure:

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




The build.properties file has the following contents:

  bin.dir = bin
 builds.dir = builds
 robocode.dir =;  the path to the Robocode home directory, the default is C: \\ Robocode (I draw your attention that this is the java properties format and the slashes must be short-cut)
 robocode.jar = $ {robocode.dir} \\ libs \\ robocode.jar 




The build.xml file has the following contents:

<?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> 




If you did everything correctly, then after executing the ant command at the root of the project, you should have the file HabrahabrTutorial \ builds \ ru.jdev.habrahabr.HabrahabrTutorial_0.1.jar, which should be ru \ jdev \ habrahabr \ HabrahabrTutorial.properties (for those who are far from Java, I will explain that jar is a regular zip archive, which can be opened by any archiver).



Making a robot



Create a project in the directory from the previous section in the development environment of your choice, connect the library $ {robocode_home} /libs/robocode.jar to it. Then set up the environment so that it either compiles the code itself into the folder with robots, or that it uses the script from the previous section when building it. Finally, create a new class ru.jdev.habrahabr.HabrahabrTutorial with the following code:



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




Start the game, select Battle -> New and make sure that en.jdev.habrahabr.HabrahabrTutorial appears in the list of robots.



Step one: Exit after death, calculating the enemy’s position, controlling the radar and drawing.



We give the code to the following form (here all comments are given right in the code. Also, hereafter, the added lines or methods are marked with the comment / * + * /, the modified lines or methods are marked with the comment / * ~ * /):

 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); } } 




Step Two: Getting Motion



We realize the beginnings of random orbital motion. Orbital means that in the event that the enemy stands still, and the random will always give out one direction, then our tank will turn the circles around the enemy. And the random component will make our tank a slightly more complex goal. To implement the movement, we add two methods:

 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); } 




And inside the condition that the enemy is detected in the main loop, add the following lines:

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




Step Three: Fire!



We implement the simplest algorithm for aiming, which shoots at the current position of the enemy. To achieve this goal, the robot will always keep the enemy at gunpoint and shoot as soon as possible. Implemented the task by one method:

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




And by adding three lines to the main loop:

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




What's next



And then welcome to roboviki, or write in the comments that you are interested in this topic and I will gradually try to highlight all the main techniques of the game. Thanks to everyone who mastered this post to the end.



PS This is my first tutorial, for this I will be glad to constructive criticism

Source: https://habr.com/ru/post/147947/



All Articles