/******************************************************************************* * Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://robocode.sourceforge.net/license/epl-v10.html * * Contributors: * Nutch Poovarawan from Cubic Creative * - The design and ideas for the JuniorRobot class * Flemming N. Larsen * - Implementor of the JuniorRobot * Pavel Savara * - Re-work of robot interfaces *******************************************************************************/ package robocode; import robocode.robotinterfaces.IBasicEvents; import robocode.robotinterfaces.IJuniorRobot; import robocode.robotinterfaces.peer.IJuniorRobotPeer; import robocode.util.Utils; import java.awt.*; import static java.lang.Math.toRadians; /** * This is the simplest robot type, which is simpler than the {@link Robot} and * {@link AdvancedRobot} classes. The JuniorRobot has a simplified model, in * purpose of teaching programming skills to inexperienced in programming * students. The simplified robot model will keep player from overwhelming of * Robocode's rules, programming syntax and programming concept. * <p/> * Instead of using getters and setters, public fields are provided for * receiving information like the last scanned robot, the coordinate of the * robot etc. * <p/> * All methods on this class are blocking calls, i.e. they do not return before * their action has been completed and will at least take one turn to execute. * However, setting colors is executed immediately and does not cost a turn to * perform. * * @author Nutch Poovarawan from Cubic Creative (designer) * @author Flemming N. Larsen (implementor) * @author Pavel Savara (contributor) * @see Robot * @see AdvancedRobot * @see TeamRobot * @see Droid * @since 1.4 */ public class JuniorRobot extends _RobotBase implements IJuniorRobot { /** * The color black (0x000000) */ public final static int black = 0x000000; /** * The color white (0xFFFFFF) */ public final static int white = 0xFFFFFF; /** * The color red (0xFF0000) */ public final static int red = 0xFF0000; /** * The color orange (0xFFA500) */ public final static int orange = 0xFFA500; /** * The color yellow (0xFFFF00) */ public final static int yellow = 0xFFFF00; /** * The color green (0x008000) */ public final static int green = 0x008000; /** * The color blue (0x0000FF) */ public final static int blue = 0x0000FF; /** * The color purple (0x800080) */ public final static int purple = 0x800080; /** * The color brown (0x8B4513) */ public final static int brown = 0x8B4513; /** * The color gray (0x808080) */ public final static int gray = 0x808080; /** * Contains the width of the battlefield. * * @see #fieldWidth */ public int fieldWidth; /** * Contains the height of the battlefield. * * @see #fieldWidth */ public int fieldHeight; /** * Current number of other robots on the battle field. */ public int others; /** * Current energy of this robot, where 100 means full energy and 0 means no energy (dead). */ public int energy; /** * Current horizontal location of this robot (in pixels). * * @see #robotY */ public int robotX; /** * Current vertical location of this robot (in pixels). * * @see #robotX */ public int robotY; /** * Current heading angle of this robot (in degrees). * * @see #turnLeft(int) * @see #turnRight(int) * @see #turnTo(int) * @see #turnAheadLeft(int, int) * @see #turnAheadRight(int, int) * @see #turnBackLeft(int, int) * @see #turnBackRight(int, int) */ public int heading; /** * Current gun heading angle of this robot (in degrees). * * @see #gunBearing * @see #turnGunLeft(int) * @see #turnGunRight(int) * @see #turnGunTo(int) * @see #bearGunTo(int) */ public int gunHeading; /** * Current gun heading angle of this robot compared to its body (in degrees). * * @see #gunHeading * @see #turnGunLeft(int) * @see #turnGunRight(int) * @see #turnGunTo(int) * @see #bearGunTo(int) */ public int gunBearing; /** * Flag specifying if the gun is ready to fire, i.e. gun heat <= 0. * {@code true} means that the gun is able to fire; {@code false} * means that the gun cannot fire yet as it still needs to cool down. * * @see #fire() * @see #fire(double) */ public boolean gunReady; /** * Current distance to the scanned nearest other robot (in pixels). * If there is no robot in the radar's sight, this field will be less than 0, i.e -1. * This field will not be updated while {@link #onScannedRobot()} event is active. * * @see #onScannedRobot() * @see #scannedAngle * @see #scannedBearing * @see #scannedEnergy * @see #scannedVelocity * @see #scannedHeading */ public int scannedDistance = -1; /** * Current angle to the scanned nearest other robot (in degrees). * If there is no robot in the radar's sight, this field will be less than 0, i.e -1. * This field will not be updated while {@link #onScannedRobot()} event is active. * * @see #onScannedRobot() * @see #scannedDistance * @see #scannedBearing * @see #scannedEnergy * @see #scannedVelocity * @see #scannedHeading */ public int scannedAngle = -1; /** * Current angle to the scanned nearest other robot (in degrees) compared to * the body of this robot. * If there is no robot in the radar's sight, this field will be less than 0, i.e -1. * This field will not be updated while {@link #onScannedRobot()} event is active. * * @see #onScannedRobot() * @see #scannedDistance * @see #scannedAngle * @see #scannedEnergy * @see #scannedVelocity * @see #scannedHeading */ public int scannedBearing = -1; /** * Current velocity of the scanned nearest other robot. * If there is no robot in the radar's sight, this field will be -99. * Note that a positive value means that the robot moves forward, a negative * value means that the robot moved backward, and 0 means that the robot is * not moving at all. * This field will not be updated while {@link #onScannedRobot()} event is active. * * @see #onScannedRobot() * @see #scannedDistance * @see #scannedAngle * @see #scannedBearing * @see #scannedEnergy * @see #scannedHeading */ public int scannedVelocity = -99; /** * Current heading of the scanned nearest other robot (in degrees). * If there is no robot in the radar's sight, this field will be less than 0, i.e -1. * This field will not be updated while {@link #onScannedRobot()} event is active. * * @see #onScannedRobot() * @see #scannedDistance * @see #scannedAngle * @see #scannedBearing * @see #scannedEnergy * @see #scannedVelocity */ public int scannedHeading = -1; /** * Current energy of scanned nearest other robot. * If there is no robot in the radar's sight, this field will be less than 0, i.e -1. * This field will not be updated while {@link #onScannedRobot()} event is active. * * @see #onScannedRobot() * @see #scannedDistance * @see #scannedAngle * @see #scannedBearing * @see #scannedVelocity */ public int scannedEnergy = -1; /** * Latest angle from where this robot was hit by a bullet (in degrees). * If the robot has never been hit, this field will be less than 0, i.e. -1. * This field will not be updated while {@link #onHitByBullet()} event is active. * * @see #onHitByBullet() * @see #hitByBulletBearing */ public int hitByBulletAngle = -1; /** * Latest angle from where this robot was hit by a bullet (in degrees) * compared to the body of this robot. * If the robot has never been hit, this field will be less than 0, i.e. -1. * This field will not be updated while {@link #onHitByBullet()} event is active. * * @see #onHitByBullet() * @see #hitByBulletAngle */ public int hitByBulletBearing = -1; /** * Latest angle where this robot has hit another robot (in degrees). * If this robot has never hit another robot, this field will be less than 0, i.e. -1. * This field will not be updated while {@link #onHitRobot()} event is active. * * @see #onHitRobot() * @see #hitRobotBearing */ public int hitRobotAngle = -1; /** * Latest angle where this robot has hit another robot (in degrees) * compared to the body of this robot. * If this robot has never hit another robot, this field will be less than 0, i.e. -1. * This field will not be updated while {@link #onHitRobot()} event is active. * * @see #onHitRobot() * @see #hitRobotAngle */ public int hitRobotBearing = -1; /** * Latest angle where this robot has hit a wall (in degrees). * If this robot has never hit a wall, this field will be less than 0, i.e. -1. * This field will not be updated while {@link #onHitWall()} event is active. * * @see #onHitWall() * @see #hitWallBearing */ public int hitWallAngle = -1; /** * Latest angle where this robot has hit a wall (in degrees) * compared to the body of this robot. * If this robot has never hit a wall, this field will be less than 0, i.e. -1. * This field will not be updated while {@link #onHitWall()} event is active. * * @see #onHitWall() * @see #hitWallAngle */ public int hitWallBearing = -1; /** * The robot event handler for this robot. */ private InnerEventHandler innerEventHandler; /** * Moves this robot forward by pixels. * * @param distance the amount of pixels to move forward * @see #back(int) * @see #robotX * @see #robotY */ public void ahead(int distance) { if (peer != null) { peer.move(distance); } else { uninitializedException(); } } /** * Moves this robot backward by pixels. * * @param distance the amount of pixels to move backward * @see #ahead(int) * @see #robotX * @see #robotY */ public void back(int distance) { ahead(-distance); } /** * Turns the gun to the specified angle (in degrees) relative to body of this robot. * The gun will turn to the side with the shortest delta angle to the specified angle. * * @param angle the angle to turn the gun to relative to the body of this robot * @see #gunHeading * @see #gunBearing * @see #turnGunLeft(int) * @see #turnGunRight(int) * @see #turnGunTo(int) */ public void bearGunTo(int angle) { if (peer != null) { peer.turnGun(Utils.normalRelativeAngle(peer.getBodyHeading() + toRadians(angle) - peer.getGunHeading())); } else { uninitializedException(); } } /** * Skips a turn. * * @see #doNothing(int) */ public void doNothing() { if (peer != null) { peer.execute(); } else { uninitializedException(); } } /** * Skips the specified number of turns. * * @param turns the number of turns to skip * @see #doNothing() */ public void doNothing(int turns) { if (turns <= 0) { return; } if (peer != null) { for (int i = 0; i < turns; i++) { peer.execute(); } } else { uninitializedException(); } } /** * Fires a bullet with the default power of 1. * If the gun heat is more than 0 and hence cannot fire, this method will * suspend until the gun is ready to fire, and then fire a bullet. * * @see #gunReady */ public void fire() { fire(1); } /** * Fires a bullet with the specified bullet power, which is between 0.1 and 3 * where 3 is the maximum bullet power. * If the gun heat is more than 0 and hence cannot fire, this method will * suspend until the gun is ready to fire, and then fire a bullet. * * @param power between 0.1 and 3 * @see #gunReady */ public void fire(double power) { if (peer != null) { getEventHandler().juniorFirePower = power; peer.execute(); } else { uninitializedException(); } } /** * Do not call this method! * <p/> * {@inheritDoc} */ public final IBasicEvents getBasicEventListener() { return getEventHandler(); } /** * Do not call this method! * <p/> * {@inheritDoc} */ public final Runnable getRobotRunnable() { return getEventHandler(); } /** * This event methods is called from the game when this robot has been hit * by another robot's bullet. When this event occurs the * {@link #hitByBulletAngle} and {@link #hitByBulletBearing} fields values * are automatically updated. * * @see #hitByBulletAngle * @see #hitByBulletBearing */ public void onHitByBullet() {} /** * This event methods is called from the game when a bullet from this robot * has hit another robot. When this event occurs the {@link #hitRobotAngle} * and {@link #hitRobotBearing} fields values are automatically updated. * * @see #hitRobotAngle * @see #hitRobotBearing */ public void onHitRobot() {} /** * This event methods is called from the game when this robot has hit a wall. * When this event occurs the {@link #hitWallAngle} and {@link #hitWallBearing} * fields values are automatically updated. * * @see #hitWallAngle * @see #hitWallBearing */ public void onHitWall() {} /** * This event method is called from the game when the radar detects another * robot. When this event occurs the {@link #scannedDistance}, * {@link #scannedAngle}, {@link #scannedBearing}, and {@link #scannedEnergy} * field values are automatically updated. * * @see #scannedDistance * @see #scannedAngle * @see #scannedBearing * @see #scannedEnergy */ public void onScannedRobot() {} /** * The main method in every robot. You must override this to set up your * robot's basic behavior. * <p/> * Example: * <pre> * // A basic robot that moves around in a square * public void run() { * ahead(100); * turnRight(90); * } * </pre> * This method is automatically re-called when it has returned. */ public void run() {} /** * Sets the colors of the robot. The color values are RGB values. * You can use the colors that are already defined for this class. * * @param bodyColor the RGB color value for the body * @param gunColor the RGB color value for the gun * @param radarColor the RGB color value for the radar * @see #setColors(int, int, int, int, int) */ public void setColors(int bodyColor, int gunColor, int radarColor) { if (peer != null) { peer.setBodyColor(new Color(bodyColor)); peer.setGunColor(new Color(gunColor)); peer.setRadarColor(new Color(radarColor)); } else { uninitializedException(); } } /** * Sets the colors of the robot. The color values are RGB values. * You can use the colors that are already defined for this class. * * @param bodyColor the RGB color value for the body * @param gunColor the RGB color value for the gun * @param radarColor the RGB color value for the radar * @param bulletColor the RGB color value for the bullets * @param scanArcColor the RGB color value for the scan arc * @see #setColors(int, int, int) */ public void setColors(int bodyColor, int gunColor, int radarColor, int bulletColor, int scanArcColor) { if (peer != null) { peer.setBodyColor(new Color(bodyColor)); peer.setGunColor(new Color(gunColor)); peer.setRadarColor(new Color(radarColor)); peer.setBulletColor(new Color(bulletColor)); peer.setScanColor(new Color(scanArcColor)); } else { uninitializedException(); } } /** * Moves this robot forward by pixels and turns this robot left by degrees * at the same time. The robot will move in a curve that follows a perfect * circle, and the moving and turning will end at the same time. * <p/> * Note that the max. velocity and max. turn rate is automatically adjusted, * which means that the robot will move slower the sharper the turn is * compared to the distance. * * @param distance the amount of pixels to move forward * @param degrees the amount of degrees to turn to the left * @see #heading * @see #robotX * @see #robotY * @see #turnLeft(int) * @see #turnRight(int) * @see #turnTo(int) * @see #turnAheadRight(int, int) * @see #turnBackLeft(int, int) * @see #turnBackRight(int, int) */ public void turnAheadLeft(int distance, int degrees) { turnAheadRight(distance, -degrees); } /** * Moves this robot forward by pixels and turns this robot right by degrees * at the same time. The robot will move in a curve that follows a perfect * circle, and the moving and turning will end at the same time. * <p/> * Note that the max. velocity and max. turn rate is automatically adjusted, * which means that the robot will move slower the sharper the turn is * compared to the distance. * * @param distance the amount of pixels to move forward * @param degrees the amount of degrees to turn to the right * @see #heading * @see #robotX * @see #robotY * @see #turnLeft(int) * @see #turnRight(int) * @see #turnTo(int) * @see #turnAheadLeft(int, int) * @see #turnBackLeft(int, int) * @see #turnBackRight(int, int) */ public void turnAheadRight(int distance, int degrees) { if (peer != null) { ((IJuniorRobotPeer) peer).turnAndMove(distance, toRadians(degrees)); } else { uninitializedException(); } } /** * Moves this robot backward by pixels and turns this robot left by degrees * at the same time. The robot will move in a curve that follows a perfect * circle, and the moving and turning will end at the same time. * <p/> * Note that the max. velocity and max. turn rate is automatically adjusted, * which means that the robot will move slower the sharper the turn is * compared to the distance. * * @param distance the amount of pixels to move backward * @param degrees the amount of degrees to turn to the left * @see #heading * @see #robotX * @see #robotY * @see #turnLeft(int) * @see #turnRight(int) * @see #turnTo(int) * @see #turnAheadLeft(int, int) * @see #turnAheadRight(int, int) * @see #turnBackRight(int, int) */ public void turnBackLeft(int distance, int degrees) { turnAheadRight(-distance, degrees); } /** * Moves this robot backward by pixels and turns this robot right by degrees * at the same time. The robot will move in a curve that follows a perfect * circle, and the moving and turning will end at the same time. * <p/> * Note that the max. velocity and max. turn rate is automatically adjusted, * which means that the robot will move slower the sharper the turn is * compared to the distance. * * @param distance the amount of pixels to move backward * @param degrees the amount of degrees to turn to the right * @see #heading * @see #robotX * @see #robotY * @see #turnLeft(int) * @see #turnRight(int) * @see #turnTo(int) * @see #turnAheadLeft(int, int) * @see #turnAheadRight(int, int) * @see #turnBackLeft(int, int) */ public void turnBackRight(int distance, int degrees) { turnAheadRight(-distance, -degrees); } /** * Turns the gun left by degrees. * * @param degrees the amount of degrees to turn the gun to the left * @see #gunHeading * @see #gunBearing * @see #turnGunRight(int) * @see #turnGunTo(int) * @see #bearGunTo(int) */ public void turnGunLeft(int degrees) { turnGunRight(-degrees); } /** * Turns the gun right by degrees. * * @param degrees the amount of degrees to turn the gun to the right * @see #gunHeading * @see #gunBearing * @see #turnGunLeft(int) * @see #turnGunTo(int) * @see #bearGunTo(int) */ public void turnGunRight(int degrees) { if (peer != null) { peer.turnGun(toRadians(degrees)); } else { uninitializedException(); } } /** * Turns the gun to the specified angle (in degrees). * The gun will turn to the side with the shortest delta angle to the * specified angle. * * @param angle the angle to turn the gun to * @see #gunHeading * @see #gunBearing * @see #turnGunLeft(int) * @see #turnGunRight(int) * @see #bearGunTo(int) */ public void turnGunTo(int angle) { if (peer != null) { peer.turnGun(Utils.normalRelativeAngle(toRadians(angle) - peer.getGunHeading())); } else { uninitializedException(); } } /** * Turns this robot left by degrees. * * @param degrees the amount of degrees to turn to the left * @see #heading * @see #turnRight(int) * @see #turnTo(int) * @see #turnAheadLeft(int, int) * @see #turnAheadRight(int, int) * @see #turnBackLeft(int, int) * @see #turnBackRight(int, int) */ public void turnLeft(int degrees) { turnRight(-degrees); } /** * Turns this robot right by degrees. * * @param degrees the amount of degrees to turn to the right * @see #heading * @see #turnLeft(int) * @see #turnTo(int) * @see #turnAheadLeft(int, int) * @see #turnAheadRight(int, int) * @see #turnBackLeft(int, int) * @see #turnBackRight(int, int) */ public void turnRight(int degrees) { if (peer != null) { peer.turnBody(toRadians(degrees)); } else { uninitializedException(); } } /** * Turns this robot to the specified angle (in degrees). * The robot will turn to the side with the shortest delta angle to the * specified angle. * * @param angle the angle to turn this robot to * @see #heading * @see #turnLeft(int) * @see #turnRight(int) * @see #turnAheadLeft(int, int) * @see #turnAheadRight(int, int) * @see #turnBackLeft(int, int) * @see #turnBackRight(int, int) */ public void turnTo(int angle) { if (peer != null) { peer.turnBody(Utils.normalRelativeAngle(toRadians(angle) - peer.getBodyHeading())); } else { uninitializedException(); } } /* * Returns the event handler of this robot. */ private InnerEventHandler getEventHandler() { if (innerEventHandler == null) { innerEventHandler = new InnerEventHandler(); } return innerEventHandler; } /* * The JuniorRobot event handler, which implements the basic robot events, * JuniorRobot event, and Runnable. */ private final class InnerEventHandler implements IBasicEvents, Runnable { private double juniorFirePower; private long currentTurn; public void onBulletHit(BulletHitEvent event) {} public void onBulletHitBullet(BulletHitBulletEvent event) {} public void onBulletMissed(BulletMissedEvent event) {} public void onDeath(DeathEvent event) {} public void onHitByBullet(HitByBulletEvent event) { double angle = peer.getBodyHeading() + event.getBearingRadians(); hitByBulletAngle = (int) (Math.toDegrees(Utils.normalAbsoluteAngle(angle)) + 0.5); hitByBulletBearing = (int) (event.getBearing() + 0.5); JuniorRobot.this.onHitByBullet(); } public void onHitRobot(HitRobotEvent event) { double angle = peer.getBodyHeading() + event.getBearingRadians(); hitRobotAngle = (int) (Math.toDegrees(Utils.normalAbsoluteAngle(angle)) + 0.5); hitRobotBearing = (int) (event.getBearing() + 0.5); JuniorRobot.this.onHitRobot(); } public void onHitWall(HitWallEvent event) { double angle = peer.getBodyHeading() + event.getBearingRadians(); hitWallAngle = (int) (Math.toDegrees(Utils.normalAbsoluteAngle(angle)) + 0.5); hitWallBearing = (int) (event.getBearing() + 0.5); JuniorRobot.this.onHitWall(); } public void onRobotDeath(RobotDeathEvent event) { others = peer.getOthers(); } public void onScannedRobot(ScannedRobotEvent event) { scannedDistance = (int) (event.getDistance() + 0.5); scannedEnergy = Math.max(1, (int) (event.getEnergy() + 0.5)); scannedAngle = (int) (Math.toDegrees( Utils.normalAbsoluteAngle(peer.getBodyHeading() + event.getBearingRadians())) + 0.5); scannedBearing = (int) (event.getBearing() + 0.5); scannedHeading = (int) (event.getHeading() + 0.5); scannedVelocity = (int) (event.getVelocity() + 0.5); JuniorRobot.this.onScannedRobot(); } public void onStatus(StatusEvent e) { final RobotStatus s = e.getStatus(); others = peer.getOthers(); energy = Math.max(1, (int) (s.getEnergy() + 0.5)); robotX = (int) (s.getX() + 0.5); robotY = (int) (s.getY() + 0.5); heading = (int) (s.getHeading() + 0.5); gunHeading = (int) (s.getGunHeading() + 0.5); gunBearing = (int) (Utils.normalRelativeAngle(s.getGunHeading() - s.getHeading()) + 0.5); gunReady = (s.getGunHeat() <= 0); currentTurn = e.getTime(); // Auto fire if (juniorFirePower > 0 && gunReady && (peer.getGunTurnRemaining() == 0)) { if (peer.setFire(juniorFirePower) != null) { gunReady = false; juniorFirePower = 0; } } // Reset event data scannedDistance = -1; scannedAngle = -1; scannedBearing = -1; scannedVelocity = -99; scannedHeading = -1; scannedEnergy = -1; hitByBulletAngle = -1; hitByBulletBearing = -1; hitRobotAngle = -1; hitRobotBearing = -1; hitWallAngle = -1; hitWallBearing = -1; } public void onWin(WinEvent event) {} public void run() { fieldWidth = (int) (peer.getBattleFieldWidth() + 0.5); fieldHeight = (int) (peer.getBattleFieldHeight() + 0.5); // noinspection InfiniteLoopStatement while (true) { long lastTurn = currentTurn; // Used for the rescan check JuniorRobot.this.run(); // Run the code in the JuniorRobot // Make sure that we rescan if the robot did not execute anything this turn. // When the robot executes the currentTurn will automatically be increased by 1, // So when the turn stays the same, the robot did not take any action this turn. if (lastTurn == currentTurn) { peer.rescan(); // Spend a turn on rescanning } } } } }