/**
* Copyright (c) 2001-2014 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
*/
package net.sf.robocode.battle.peer;
import static net.sf.robocode.io.Logger.logMessage;
import net.sf.robocode.battle.Battle;
import net.sf.robocode.battle.BoundingRectangle;
import net.sf.robocode.host.IHostManager;
import net.sf.robocode.host.RobotStatics;
import net.sf.robocode.host.events.EventManager;
import net.sf.robocode.host.events.EventQueue;
import net.sf.robocode.host.proxies.IHostingRobotProxy;
import net.sf.robocode.io.Logger;
import net.sf.robocode.peer.*;
import net.sf.robocode.repository.IRobotItem;
import net.sf.robocode.security.HiddenAccess;
import net.sf.robocode.serialization.RbSerializer;
import robocode.*;
import robocode.control.RandomFactory;
import robocode.control.RobotSetup;
import robocode.control.RobotSpecification;
import robocode.control.snapshot.BulletState;
import robocode.control.snapshot.RobotState;
import robocode.exception.AbortedException;
import robocode.exception.DeathException;
import robocode.exception.WinException;
import static robocode.util.Utils.*;
import java.awt.geom.Arc2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import static java.lang.Math.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* RobotPeer is an object that deals with game mechanics and rules, and makes
* sure that robots abides the rules.
*
* @author Mathew A. Nelson (original)
* @author Flemming N. Larsen (contributor)
* @author Luis Crespo (contributor)
* @author Titus Chen (contributor)
* @author Robert D. Maupin (contributor)
* @author Nathaniel Troutman (contributor)
* @author Pavel Savara (contributor)
* @author Patrick Cupka (contributor)
* @author Julian Kent (contributor)
* @author "Positive" (contributor)
* @author "BD123" (contributor)
*/
public final class RobotPeer implements IRobotPeerBattle, IRobotPeer {
public static final int
WIDTH = 36,
HEIGHT = 36;
private static final int
HALF_WIDTH_OFFSET = WIDTH / 2,
HALF_HEIGHT_OFFSET = HEIGHT / 2;
private static final int MAX_SKIPPED_TURNS = 30;
private static final int MAX_SKIPPED_TURNS_WITH_IO = 240;
private Battle battle;
private RobotStatistics statistics;
private final TeamPeer teamPeer;
private final RobotSpecification robotSpecification;
private IHostingRobotProxy robotProxy;
private AtomicReference<RobotStatus> status = new AtomicReference<RobotStatus>();
private AtomicReference<ExecCommands> commands = new AtomicReference<ExecCommands>();
private AtomicReference<EventQueue> events = new AtomicReference<EventQueue>(new EventQueue());
private AtomicReference<List<TeamMessage>> teamMessages = new AtomicReference<List<TeamMessage>>(
new ArrayList<TeamMessage>());
private AtomicReference<List<BulletStatus>> bulletUpdates = new AtomicReference<List<BulletStatus>>(
new ArrayList<BulletStatus>());
// thread is running
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final StringBuilder battleText = new StringBuilder(1024);
private final StringBuilder proxyText = new StringBuilder(1024);
private RobotStatics statics;
private BattleRules battleRules;
// for battle thread, during robots processing
private ExecCommands currentCommands;
private double lastHeading;
private double lastGunHeading;
private double lastRadarHeading;
private double energy;
private double velocity;
private double bodyHeading;
private double radarHeading;
private double gunHeading;
private double gunHeat;
private double x;
private double y;
private boolean scan;
private boolean turnedRadarWithGun; // last round
private boolean isIORobot;
private boolean isPaintEnabled;
private boolean sgPaintEnabled;
// waiting for next tick
private final AtomicBoolean isSleeping = new AtomicBoolean(false);
private final AtomicBoolean halt = new AtomicBoolean(false);
// last and current execution time and detecting skipped turns
private int lastExecutionTime = -1;
private int currentExecutionTime;
private boolean isExecFinishedAndDisabled;
private boolean isEnergyDrained;
private boolean isWinner;
private boolean inCollision;
private boolean isOverDriving;
private RobotState state;
private final Arc2D scanArc;
private final BoundingRectangle boundingBox;
private final RbSerializer rbSerializer;
public RobotPeer(Battle battle, IHostManager hostManager, RobotSpecification robotSpecification, int duplicate, TeamPeer team, int robotIndex) {
super();
this.battle = battle;
this.robotSpecification = robotSpecification;
this.rbSerializer = new RbSerializer();
this.boundingBox = new BoundingRectangle();
this.scanArc = new Arc2D.Double();
this.teamPeer = team;
this.state = RobotState.ACTIVE;
this.battleRules = battle.getBattleRules();
if (team != null) {
team.add(this);
}
String teamName;
List<String> teamMembers;
boolean isTeamLeader;
int teamIndex;
if (teamPeer == null) {
teamName = null;
teamMembers = null;
isTeamLeader = false;
teamIndex = -1; // Must be set to -1 when robot is not in a team
} else {
teamName = team.getName();
teamMembers = team.getMemberNames();
isTeamLeader = team.size() == 1; // That is current team size, more might follow later. First robot is leader
teamIndex = team.getTeamIndex();
}
this.statics = new RobotStatics(robotSpecification, duplicate, isTeamLeader, battleRules, teamName, teamMembers,
robotIndex, teamIndex);
this.statistics = new RobotStatistics(this, battle.getRobotsCount());
this.robotProxy = (IHostingRobotProxy) hostManager.createRobotProxy(robotSpecification, statics, this);
}
public void println(String s) {
synchronized (proxyText) {
battleText.append(s);
battleText.append("\n");
}
}
private void print(String s) {
synchronized (proxyText) {
proxyText.append(s);
}
}
public String readOutText() {
synchronized (proxyText) {
final String robotText = battleText.toString() + proxyText.toString();
battleText.setLength(0);
proxyText.setLength(0);
return robotText;
}
}
public RobotStatistics getRobotStatistics() {
return statistics;
}
public ContestantStatistics getStatistics() {
return statistics;
}
public RobotSpecification getRobotSpecification() {
return robotSpecification;
}
// -------------------
// statics
// -------------------
public boolean isJuniorRobot() {
return statics.isJuniorRobot();
}
public boolean isAdvancedRobot() {
return statics.isAdvancedRobot();
}
public boolean isTeamRobot() {
return statics.isTeamRobot();
}
public boolean isDroid() {
return statics.isDroid();
}
public boolean isSentryRobot() {
return statics.isSentryRobot();
}
public boolean isInteractiveRobot() {
return statics.isInteractiveRobot();
}
public boolean isPaintRobot() {
return statics.isPaintRobot();
}
public String getName() {
return statics.getName();
}
public String getAnnonymousName() {
return statics.getAnnonymousName();
}
public String getShortName() {
return statics.getShortName();
}
public String getVeryShortName() {
return statics.getVeryShortName();
}
public int getRobotIndex() {
return statics.getRobotIndex();
}
public int getTeamIndex() {
return statics.getTeamIndex();
}
public int getContestantIndex() {
return getTeamIndex() >= 0 ? getTeamIndex() : getRobotIndex();
}
// -------------------
// status
// -------------------
public void setPaintEnabled(boolean enabled) {
isPaintEnabled = enabled;
}
public boolean isPaintEnabled() {
return isPaintEnabled;
}
public void setSGPaintEnabled(boolean enabled) {
sgPaintEnabled = enabled;
}
public boolean isSGPaintEnabled() {
return sgPaintEnabled;
}
public RobotState getState() {
return state;
}
public void setState(RobotState state) {
this.state = state;
}
public boolean isDead() {
return state == RobotState.DEAD;
}
public boolean isAlive() {
return state != RobotState.DEAD;
}
public boolean isWinner() {
return isWinner;
}
public boolean isRunning() {
return isRunning.get();
}
public boolean isSleeping() {
return isSleeping.get();
}
public boolean isHalt() {
return halt.get();
}
public void setHalt(boolean value) {
halt.set(value);
}
public BoundingRectangle getBoundingBox() {
return boundingBox;
}
public Arc2D getScanArc() {
return scanArc;
}
// -------------------
// robot space
// -------------------
public double getGunHeading() {
return gunHeading;
}
public double getBodyHeading() {
return bodyHeading;
}
public double getRadarHeading() {
return radarHeading;
}
public double getVelocity() {
return velocity;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getEnergy() {
return energy;
}
public double getGunHeat() {
return gunHeat;
}
public int getBodyColor() {
return commands.get().getBodyColor();
}
public int getRadarColor() {
return commands.get().getRadarColor();
}
public int getGunColor() {
return commands.get().getGunColor();
}
public int getBulletColor() {
return commands.get().getBulletColor();
}
public int getScanColor() {
return commands.get().getScanColor();
}
// ------------
// team
// ------------
public TeamPeer getTeamPeer() {
return teamPeer;
}
public String getTeamName() {
return statics.getTeamName();
}
public boolean isTeamLeader() {
return statics.isTeamLeader();
}
boolean isTeamMate(RobotPeer otherRobot) {
if (getTeamPeer() != null) {
for (RobotPeer mate : getTeamPeer()) {
if (otherRobot == mate) {
return true;
}
}
}
return false;
}
// -----------
// execute
// -----------
private ByteBuffer bidirectionalBuffer;
public void setupBuffer(ByteBuffer bidirectionalBuffer) {
this.bidirectionalBuffer = bidirectionalBuffer;
}
public void setupThread() {
Thread.currentThread().setName(getName());
}
public void executeImplSerial() throws IOException {
ExecCommands commands = (ExecCommands) rbSerializer.deserialize(bidirectionalBuffer);
final ExecResults results = executeImpl(commands);
bidirectionalBuffer.clear();
rbSerializer.serializeToBuffer(bidirectionalBuffer, RbSerializer.ExecResults_TYPE, results);
}
public void waitForBattleEndImplSerial() throws IOException {
ExecCommands commands = (ExecCommands) rbSerializer.deserialize(bidirectionalBuffer);
final ExecResults results = waitForBattleEndImpl(commands);
bidirectionalBuffer.clear();
rbSerializer.serializeToBuffer(bidirectionalBuffer, RbSerializer.ExecResults_TYPE, results);
}
public final ExecResults executeImpl(ExecCommands newCommands) {
validateCommands(newCommands);
if (!isExecFinishedAndDisabled) {
// from robot to battle
commands.set(new ExecCommands(newCommands, true));
print(newCommands.getOutputText());
} else {
// slow down spammer
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// If we are stopping, yet the robot took action (in onWin or onDeath), stop now.
if (battle.isAborted()) {
isExecFinishedAndDisabled = true;
throw new AbortedException();
}
if (isDead()) {
isExecFinishedAndDisabled = true;
throw new DeathException();
}
if (isHalt()) {
isExecFinishedAndDisabled = true;
if (isWinner) {
throw new WinException();
} else {
throw new AbortedException();
}
}
waitForNextTurn();
checkSkippedTurn();
// from battle to robot
final ExecCommands resCommands = new ExecCommands(this.commands.get(), false);
final RobotStatus resStatus = status.get();
final boolean shouldWait = battle.isAborted() || (battle.isLastRound() && isWinner());
return new ExecResults(resCommands, resStatus, readoutEvents(), readoutTeamMessages(), readoutBullets(),
isHalt(), shouldWait, isPaintEnabled());
}
public final ExecResults waitForBattleEndImpl(ExecCommands newCommands) {
if (!isHalt()) {
// from robot to battle
commands.set(new ExecCommands(newCommands, true));
print(newCommands.getOutputText());
waitForNextTurn();
}
// from battle to robot
final ExecCommands resCommands = new ExecCommands(this.commands.get(), false);
final RobotStatus resStatus = status.get();
final boolean shouldWait = battle.isAborted() || (battle.isLastRound() && !isWinner());
readoutTeamMessages(); // throw away
return new ExecResults(resCommands, resStatus, readoutEvents(), new ArrayList<TeamMessage>(), readoutBullets(),
isHalt(), shouldWait, false);
}
private void validateCommands(ExecCommands newCommands) {
if (Double.isNaN(newCommands.getMaxTurnRate())) {
println("You cannot setMaxTurnRate to: " + newCommands.getMaxTurnRate());
}
newCommands.setMaxTurnRate(Math.min(abs(newCommands.getMaxTurnRate()), Rules.MAX_TURN_RATE_RADIANS));
if (Double.isNaN(newCommands.getMaxVelocity())) {
println("You cannot setMaxVelocity to: " + newCommands.getMaxVelocity());
}
newCommands.setMaxVelocity(Math.min(abs(newCommands.getMaxVelocity()), Rules.MAX_VELOCITY));
}
private List<Event> readoutEvents() {
return events.getAndSet(new EventQueue());
}
private List<TeamMessage> readoutTeamMessages() {
return teamMessages.getAndSet(new ArrayList<TeamMessage>());
}
private List<BulletStatus> readoutBullets() {
return bulletUpdates.getAndSet(new ArrayList<BulletStatus>());
}
private void waitForNextTurn() {
synchronized (isSleeping) {
// Notify the battle that we are now asleep.
// This ends any pending wait() call in battle.runRound().
// Should not actually take place until we release the lock in wait(), below.
isSleeping.set(true);
isSleeping.notifyAll();
// Notifying battle that we're asleep
// Sleeping and waiting for battle to wake us up.
try {
isSleeping.wait();
} catch (InterruptedException e) {
// We are expecting this to happen when a round is ended!
// Immediately reasserts the exception by interrupting the caller thread itself
Thread.currentThread().interrupt();
}
isSleeping.set(false);
// Notify battle thread, which is waiting in
// our wakeup() call, to return.
// It's quite possible, by the way, that we'll be back in sleep (above)
// before the battle thread actually wakes up
isSleeping.notifyAll();
}
}
// -----------
// called on battle thread
// -----------
public void waitWakeup() {
synchronized (isSleeping) {
if (isSleeping()) {
// Wake up the thread
isSleeping.notifyAll();
try {
isSleeping.wait(10000);
} catch (InterruptedException e) {
// Immediately reasserts the exception by interrupting the caller thread itself
Thread.currentThread().interrupt();
}
}
}
}
private void waitWakeupNoWait() {
synchronized (isSleeping) {
if (isSleeping()) {
// Wake up the thread
isSleeping.notifyAll();
}
}
}
public void waitSleeping(long millisWait, int nanosWait) {
synchronized (isSleeping) {
// It's quite possible for simple robots to
// complete their processing before we get here,
// so we test if the robot is already asleep.
if (!isSleeping()) {
try {
for (long i = millisWait; i > 0 && !isSleeping() && isRunning(); i--) {
isSleeping.wait(0, 999999);
}
if (!isSleeping() && isRunning()) {
isSleeping.wait(0, nanosWait);
}
} catch (InterruptedException e) {
// Immediately reasserts the exception by interrupting the caller thread itself
Thread.currentThread().interrupt();
logMessage("Wait for " + getName() + " interrupted.");
}
}
}
}
public void checkSkippedTurn() {
// Store last and current execution time for detecting skipped turns
lastExecutionTime = currentExecutionTime;
currentExecutionTime = battle.getTime();
int numSkippedTurns = (currentExecutionTime - lastExecutionTime) - 1;
if (numSkippedTurns >= 1) {
events.get().clear(false);
if (isAlive()) {
for (int skippedTurn = lastExecutionTime + 1; skippedTurn < currentExecutionTime; skippedTurn++) {
addEvent(new SkippedTurnEvent(skippedTurn));
println("SYSTEM: " + getShortName() + " skipped turn " + skippedTurn);
}
}
if ((!isIORobot && (numSkippedTurns > MAX_SKIPPED_TURNS))
|| (isIORobot && (numSkippedTurns > MAX_SKIPPED_TURNS_WITH_IO))) {
println("SYSTEM: " + getShortName() + " has not performed any actions in a reasonable amount of time.");
println("SYSTEM: No score will be generated.");
setHalt(true);
waitWakeupNoWait();
punishBadBehavior(BadBehavior.SKIPPED_TOO_MANY_TURNS);
robotProxy.forceStopThread();
}
}
}
public void initializeRound(List<RobotPeer> robots, RobotSetup[] initialRobotSetups) {
boolean valid = false;
if (initialRobotSetups != null) {
int robotIndex = statics.getRobotIndex();
if (robotIndex >= 0 && robotIndex < initialRobotSetups.length) {
RobotSetup setup = initialRobotSetups[robotIndex];
if (setup != null) {
x = setup.getX();
y = setup.getY();
bodyHeading = gunHeading = radarHeading = setup.getHeading();
updateBoundingBox();
valid = validSpot(robots);
}
}
}
if (!valid) {
final Random random = RandomFactory.getRandom();
double maxWidth = battleRules.getBattlefieldWidth() - RobotPeer.WIDTH;
double maxHeight = battleRules.getBattlefieldHeight() - RobotPeer.HEIGHT;
double halfRobotWidth = RobotPeer.WIDTH / 2;
double halfRobotHeight = RobotPeer.HEIGHT / 2;
int sentryBorderSize = battle.getBattleRules().getSentryBorderSize();
int sentryBorderMoveWidth = sentryBorderSize - RobotPeer.WIDTH;
int sentryBorderMoveHeight = sentryBorderSize - RobotPeer.HEIGHT;
for (int j = 0; j < 1000; j++) {
double rndX = random.nextDouble();
double rndY = random.nextDouble();
if (isSentryRobot()) {
boolean placeOnHorizontalBar = random.nextDouble()
<= ((double) battleRules.getBattlefieldWidth()
/ (battleRules.getBattlefieldWidth() + battleRules.getBattlefieldHeight()));
if (placeOnHorizontalBar) {
x = halfRobotWidth + rndX * maxWidth;
y = halfRobotHeight + (sentryBorderMoveHeight * (rndY * 2.0 - 1.0) + maxHeight) % maxHeight;
} else {
y = halfRobotHeight + rndY * maxHeight;
x = halfRobotWidth + (sentryBorderMoveWidth * (rndX * 2.0 - 1.0) + maxWidth) % maxWidth;
}
} else {
if (battle.countActiveSentries() > 0) {
int safeZoneWidth = battleRules.getBattlefieldWidth() - 2 * sentryBorderSize;
int safeZoneHeight = battleRules.getBattlefieldHeight() - 2 * sentryBorderSize;
x = sentryBorderSize + RobotPeer.WIDTH + rndX * (safeZoneWidth - 2 * RobotPeer.WIDTH);
y = sentryBorderSize + RobotPeer.HEIGHT + rndY * (safeZoneHeight - 2 * RobotPeer.HEIGHT);
} else {
x = RobotPeer.WIDTH + rndX * (battleRules.getBattlefieldWidth() - 2 * RobotPeer.WIDTH);
y = RobotPeer.HEIGHT + rndY * (battleRules.getBattlefieldHeight() - 2 * RobotPeer.HEIGHT);
}
}
bodyHeading = 2 * Math.PI * random.nextDouble();
gunHeading = radarHeading = bodyHeading;
updateBoundingBox();
if (validSpot(robots)) {
break;
}
}
}
setState(RobotState.ACTIVE);
isWinner = false;
velocity = 0;
energy = 100;
if (statics.isSentryRobot()) {
energy += 400;
}
if (statics.isTeamLeader()) {
energy += 100;
}
if (statics.isDroid()) {
energy += 20;
}
gunHeat = 3;
setHalt(false);
isExecFinishedAndDisabled = false;
isEnergyDrained = false;
scan = false;
inCollision = false;
scanArc.setAngleStart(0);
scanArc.setAngleExtent(0);
scanArc.setFrame(-100, -100, 1, 1);
lastExecutionTime = -1;
status = new AtomicReference<RobotStatus>();
readoutEvents();
readoutTeamMessages();
readoutBullets();
battleText.setLength(0);
proxyText.setLength(0);
// Prepare new execution commands, but copy the colors from the last commands.
// Bugfix [2628217] - Robot Colors don't stick between rounds.
ExecCommands newExecCommands = new ExecCommands();
newExecCommands.copyColors(commands.get());
commands = new AtomicReference<ExecCommands>(newExecCommands);
}
private boolean validSpot(List<RobotPeer> robots) {
for (RobotPeer otherRobot : robots) {
if (otherRobot != null && otherRobot != this) {
if (getBoundingBox().intersects(otherRobot.getBoundingBox())) {
return false;
}
}
}
return true;
}
public void startRound(long waitMillis, int waitNanos) {
Logger.logMessage(".", false);
statistics.reset();
ExecCommands newExecCommands = new ExecCommands();
// Copy the colors from the last commands.
// Bugfix [2628217] - Robot Colors don't stick between rounds.
newExecCommands.copyColors(commands.get());
currentCommands = newExecCommands;
int others = battle.countActiveParticipants() - (isAlive() ? 1 : 0);
int numSentries = battle.countActiveSentries();
RobotStatus stat = HiddenAccess.createStatus(energy, x, y, bodyHeading, gunHeading, radarHeading, velocity,
currentCommands.getBodyTurnRemaining(), currentCommands.getRadarTurnRemaining(),
currentCommands.getGunTurnRemaining(), currentCommands.getDistanceRemaining(), gunHeat, others, numSentries,
battle.getRoundNum(), battle.getNumRounds(), battle.getTime());
status.set(stat);
robotProxy.startRound(currentCommands, stat);
synchronized (isSleeping) {
try {
// Wait for the robot to go to sleep (take action)
isSleeping.wait(waitMillis, waitNanos);
} catch (InterruptedException e) {
logMessage("Wait for " + getName() + " interrupted.");
// Immediately reasserts the exception by interrupting the caller thread itself
Thread.currentThread().interrupt();
}
}
if (!isSleeping() && !battle.isDebugging()) {
logMessage("\n" + getName() + " still has not started after " + waitMillis + " ms... giving up.");
}
}
public void performLoadCommands() {
currentCommands = commands.get();
fireBullets(currentCommands.getBullets());
if (currentCommands.isScan()) {
scan = true;
}
if (currentCommands.isIORobot()) {
isIORobot = true;
}
if (currentCommands.isMoved()) {
currentCommands.setMoved(false);
}
}
private void fireBullets(List<BulletCommand> bulletCommands) {
BulletPeer newBullet = null;
for (BulletCommand bulletCmd : bulletCommands) {
if (Double.isNaN(bulletCmd.getPower())) {
println("SYSTEM: You cannot call fire(NaN)");
continue;
}
if (gunHeat > 0 || energy == 0) {
return;
}
double firePower = min(energy,
min(max(bulletCmd.getPower(), Rules.MIN_BULLET_POWER), Rules.MAX_BULLET_POWER));
updateEnergy(-firePower);
gunHeat += Rules.getGunHeat(firePower);
newBullet = new BulletPeer(this, battleRules, bulletCmd.getBulletId());
newBullet.setPower(firePower);
if (!turnedRadarWithGun || !bulletCmd.isFireAssistValid() || statics.isAdvancedRobot()) {
newBullet.setHeading(gunHeading);
} else {
newBullet.setHeading(bulletCmd.getFireAssistAngle());
}
newBullet.setX(x);
newBullet.setY(y);
}
// there is only last bullet in one turn
if (newBullet != null) {
// newBullet.update(robots, bullets);
battle.addBullet(newBullet);
}
}
public final void performMove(List<RobotPeer> robots, double zapEnergy) {
// Reset robot state to active if it is not dead
if (isDead()) {
return;
}
setState(RobotState.ACTIVE);
updateGunHeat();
lastHeading = bodyHeading;
lastGunHeading = gunHeading;
lastRadarHeading = radarHeading;
final double lastX = x;
final double lastY = y;
if (!inCollision) {
updateHeading();
}
updateGunHeading();
updateRadarHeading();
updateMovement();
// At this point, robot has turned then moved.
// We could be touching a wall or another bot...
// First and foremost, we can never go through a wall:
checkWallCollision();
// If this robot is a border sentry robot then check if it hits its "range border"
if (isSentryRobot()) {
checkSentryOutsideBorder();
}
// Now check for robot collision
checkRobotCollision(robots);
// Scan false means robot did not call scan() manually.
// But if we're moving, scan
if (!scan) {
scan = (lastHeading != bodyHeading || lastGunHeading != gunHeading || lastRadarHeading != radarHeading
|| lastX != x || lastY != y);
}
if (isDead()) {
return;
}
// zap
if (zapEnergy != 0) {
zap(zapEnergy);
}
}
public void performScan(List<RobotPeer> robots) {
if (isDead()) {
return;
}
turnedRadarWithGun = false;
// scan
if (scan) {
scan(lastRadarHeading, robots);
turnedRadarWithGun = (lastGunHeading == lastRadarHeading) && (gunHeading == radarHeading);
scan = false;
}
// dispatch messages
if (statics.isTeamRobot() && teamPeer != null) {
for (TeamMessage teamMessage : currentCommands.getTeamMessages()) {
for (RobotPeer member : teamPeer) {
if (checkDispatchToMember(member, teamMessage.recipient)) {
member.addTeamMessage(teamMessage);
}
}
}
}
currentCommands = null;
lastHeading = -1;
lastGunHeading = -1;
lastRadarHeading = -1;
}
private void addTeamMessage(TeamMessage message) {
final List<TeamMessage> queue = teamMessages.get();
queue.add(message);
}
private boolean checkDispatchToMember(RobotPeer member, String recipient) {
if (member.isAlive()) {
if (recipient == null) {
if (member != this) {
return true;
}
} else {
final int nl = recipient.length();
final String currentName = member.statics.getName();
if ((currentName.length() >= nl && currentName.substring(0, nl).equals(recipient))) {
return true;
}
final String currentClassName = member.statics.getFullClassName();
if ((currentClassName.length() >= nl && currentClassName.substring(0, nl).equals(recipient))) {
return true;
}
}
}
return false;
}
public String getNameForEvent(RobotPeer otherRobot) {
if (battleRules.getHideEnemyNames() && !isTeamMate(otherRobot)) {
return otherRobot.getAnnonymousName();
}
return otherRobot.getName();
}
private void checkRobotCollision(List<RobotPeer> robots) {
inCollision = false;
for (RobotPeer otherRobot : robots) {
if (!(otherRobot == null || otherRobot == this || otherRobot.isDead())
&& boundingBox.intersects(otherRobot.boundingBox)) {
// Bounce back
double angle = atan2(otherRobot.x - x, otherRobot.y - y);
double movedx = velocity * sin(bodyHeading);
double movedy = velocity * cos(bodyHeading);
boolean atFault;
double bearing = normalRelativeAngle(angle - bodyHeading);
if ((velocity > 0 && bearing > -PI / 2 && bearing < PI / 2)
|| (velocity < 0 && (bearing < -PI / 2 || bearing > PI / 2))) {
inCollision = true;
atFault = true;
velocity = 0;
currentCommands.setDistanceRemaining(0);
x -= movedx;
y -= movedy;
boolean teamFire = (teamPeer != null && teamPeer == otherRobot.teamPeer);
if (!teamFire && !otherRobot.isSentryRobot()) {
statistics.scoreRammingDamage(otherRobot.getName());
}
this.updateEnergy(-Rules.ROBOT_HIT_DAMAGE);
otherRobot.updateEnergy(-Rules.ROBOT_HIT_DAMAGE);
if (otherRobot.energy == 0) {
if (otherRobot.isAlive()) {
otherRobot.kill();
if (!teamFire && !otherRobot.isSentryRobot()) {
final double bonus = statistics.scoreRammingKill(otherRobot.getName());
if (bonus > 0) {
println(
"SYSTEM: Ram bonus for killing " + this.getNameForEvent(otherRobot) + ": "
+ (int) (bonus + .5));
}
}
}
}
addEvent(
new HitRobotEvent(getNameForEvent(otherRobot), normalRelativeAngle(angle - bodyHeading),
otherRobot.energy, atFault));
otherRobot.addEvent(
new HitRobotEvent(getNameForEvent(this),
normalRelativeAngle(PI + angle - otherRobot.getBodyHeading()), energy, false));
}
}
}
if (inCollision) {
setState(RobotState.HIT_ROBOT);
}
}
private void checkWallCollision() {
int minX = 0 + HALF_WIDTH_OFFSET;
int minY = 0 + HALF_HEIGHT_OFFSET;
int maxX = (int) getBattleFieldWidth() - HALF_WIDTH_OFFSET;
int maxY = (int) getBattleFieldHeight() - HALF_HEIGHT_OFFSET;
boolean hitWall = false;
double adjustX = 0, adjustY = 0;
double angle = 0;
if (x < minX) {
hitWall = true;
adjustX = minX - x;
angle = normalRelativeAngle(3 * PI / 2 - bodyHeading);
} else if (x > maxX) {
hitWall = true;
adjustX = maxX - x;
angle = normalRelativeAngle(PI / 2 - bodyHeading);
} else if (y < minY) {
hitWall = true;
adjustY = minY - y;
angle = normalRelativeAngle(PI - bodyHeading);
} else if (y > maxY) {
hitWall = true;
adjustY = maxY - y;
angle = normalRelativeAngle(-bodyHeading);
}
if (hitWall) {
addEvent(new HitWallEvent(angle));
// only fix both x and y values if hitting wall at an angle
if ((bodyHeading % (Math.PI / 2)) != 0) {
double tanHeading = tan(bodyHeading);
// if it hits bottom or top wall
if (adjustX == 0) {
adjustX = adjustY * tanHeading;
} // if it hits a side wall
else if (adjustY == 0) {
adjustY = adjustX / tanHeading;
} // if the robot hits 2 walls at the same time (rare, but just in case)
else if (abs(adjustX / tanHeading) > abs(adjustY)) {
adjustY = adjustX / tanHeading;
} else if (abs(adjustY * tanHeading) > abs(adjustX)) {
adjustX = adjustY * tanHeading;
}
}
x += adjustX;
y += adjustY;
if (x < minX) {
x = minX;
} else if (x > maxX) {
x = maxX;
}
if (y < minY) {
y = minY;
} else if (y > maxY) {
y = maxY;
}
// Update energy, but do not reset inactiveTurnCount
if (statics.isAdvancedRobot()) {
setEnergy(energy - Rules.getWallHitDamage(velocity), false);
}
updateBoundingBox();
currentCommands.setDistanceRemaining(0);
velocity = 0;
setState(RobotState.HIT_WALL);
}
}
private void checkSentryOutsideBorder() {
int range = battle.getBattleRules().getSentryBorderSize();
int minX = range - HALF_WIDTH_OFFSET;
int minY = range - HALF_HEIGHT_OFFSET;
int maxX = (int) getBattleFieldWidth() - range + HALF_WIDTH_OFFSET;
int maxY = (int) getBattleFieldHeight() - range + HALF_HEIGHT_OFFSET;
boolean hitWall = false;
double adjustX = 0, adjustY = 0;
double angle = 0;
boolean isOutsideBorder = x > minX && x < maxX && y > minY && y < maxY;
if (isOutsideBorder) {
if ((x - minX) <= Rules.MAX_VELOCITY) {
hitWall = true;
adjustX = minX - x;
angle = normalRelativeAngle(PI / 2 - bodyHeading);
} else if ((maxX - x) <= Rules.MAX_VELOCITY) {
hitWall = true;
adjustX = maxX - x;
angle = normalRelativeAngle(3 * PI / 2 - bodyHeading);
} else if ((y - minY) <= Rules.MAX_VELOCITY) {
hitWall = true;
adjustY = minY - y;
angle = normalRelativeAngle(-bodyHeading);
} else if ((maxY - y) <= Rules.MAX_VELOCITY) {
hitWall = true;
adjustY = maxY - y;
angle = normalRelativeAngle(PI - bodyHeading);
}
}
if (hitWall) {
addEvent(new HitWallEvent(angle));
// only fix both x and y values if hitting wall at an angle
if ((bodyHeading % (Math.PI / 2)) != 0) {
double tanHeading = tan(bodyHeading);
// if it hits bottom or top wall
if (adjustX == 0) {
adjustX = adjustY * tanHeading;
} // if it hits a side wall
else if (adjustY == 0) {
adjustY = adjustX / tanHeading;
} // if the robot hits 2 walls at the same time (rare, but just in case)
else if (abs(adjustX / tanHeading) > abs(adjustY)) {
adjustY = adjustX / tanHeading;
} else if (abs(adjustY * tanHeading) > abs(adjustX)) {
adjustX = adjustY * tanHeading;
}
}
x += adjustX;
y += adjustY;
if (isOutsideBorder) {
if ((x - minX) <= Rules.MAX_VELOCITY) {
x = minX;
} else if ((maxX - x) <= Rules.MAX_VELOCITY) {
x = maxX;
}
if ((y - minY) <= Rules.MAX_VELOCITY) {
y = minY;
} else if ((maxY - y) <= Rules.MAX_VELOCITY) {
y = maxY;
}
}
// Update energy, but do not reset inactiveTurnCount
if (statics.isAdvancedRobot()) {
setEnergy(energy - Rules.getWallHitDamage(velocity), false);
}
updateBoundingBox();
currentCommands.setDistanceRemaining(0);
velocity = 0;
setState(RobotState.HIT_WALL);
}
}
private double getBattleFieldHeight() {
return battleRules.getBattlefieldHeight();
}
private double getBattleFieldWidth() {
return battleRules.getBattlefieldWidth();
}
private void updateBoundingBox() {
boundingBox.setRect(x - HALF_WIDTH_OFFSET, y - HALF_HEIGHT_OFFSET, WIDTH, HEIGHT);
}
// TODO: Only add events to robots that are alive? + Remove checks if the Robot is alive before adding the event?
public void addEvent(Event event) {
if (isRunning()) {
final EventQueue queue = events.get();
if ((queue.size() > EventManager.MAX_QUEUE_SIZE)
&& !(event instanceof DeathEvent || event instanceof WinEvent || event instanceof SkippedTurnEvent)) {
println(
"Not adding to " + statics.getShortName() + "'s queue, exceeded " + EventManager.MAX_QUEUE_SIZE
+ " events in queue.");
// clean up old stuff
queue.clear(battle.getTime() - EventManager.MAX_EVENT_STACK);
} else {
queue.add(event);
}
}
}
private void updateGunHeading() {
if (currentCommands.getGunTurnRemaining() > 0) {
if (currentCommands.getGunTurnRemaining() < Rules.GUN_TURN_RATE_RADIANS) {
gunHeading += currentCommands.getGunTurnRemaining();
radarHeading += currentCommands.getGunTurnRemaining();
if (currentCommands.isAdjustRadarForGunTurn()) {
currentCommands.setRadarTurnRemaining(
currentCommands.getRadarTurnRemaining() - currentCommands.getGunTurnRemaining());
}
currentCommands.setGunTurnRemaining(0);
} else {
gunHeading += Rules.GUN_TURN_RATE_RADIANS;
radarHeading += Rules.GUN_TURN_RATE_RADIANS;
currentCommands.setGunTurnRemaining(currentCommands.getGunTurnRemaining() - Rules.GUN_TURN_RATE_RADIANS);
if (currentCommands.isAdjustRadarForGunTurn()) {
currentCommands.setRadarTurnRemaining(
currentCommands.getRadarTurnRemaining() - Rules.GUN_TURN_RATE_RADIANS);
}
}
} else if (currentCommands.getGunTurnRemaining() < 0) {
if (currentCommands.getGunTurnRemaining() > -Rules.GUN_TURN_RATE_RADIANS) {
gunHeading += currentCommands.getGunTurnRemaining();
radarHeading += currentCommands.getGunTurnRemaining();
if (currentCommands.isAdjustRadarForGunTurn()) {
currentCommands.setRadarTurnRemaining(
currentCommands.getRadarTurnRemaining() - currentCommands.getGunTurnRemaining());
}
currentCommands.setGunTurnRemaining(0);
} else {
gunHeading -= Rules.GUN_TURN_RATE_RADIANS;
radarHeading -= Rules.GUN_TURN_RATE_RADIANS;
currentCommands.setGunTurnRemaining(currentCommands.getGunTurnRemaining() + Rules.GUN_TURN_RATE_RADIANS);
if (currentCommands.isAdjustRadarForGunTurn()) {
currentCommands.setRadarTurnRemaining(
currentCommands.getRadarTurnRemaining() + Rules.GUN_TURN_RATE_RADIANS);
}
}
}
gunHeading = normalAbsoluteAngle(gunHeading);
}
private void updateHeading() {
boolean normalizeHeading = true;
double turnRate = min(currentCommands.getMaxTurnRate(),
(.4 + .6 * (1 - (abs(velocity) / Rules.MAX_VELOCITY))) * Rules.MAX_TURN_RATE_RADIANS);
if (currentCommands.getBodyTurnRemaining() > 0) {
if (currentCommands.getBodyTurnRemaining() < turnRate) {
bodyHeading += currentCommands.getBodyTurnRemaining();
gunHeading += currentCommands.getBodyTurnRemaining();
radarHeading += currentCommands.getBodyTurnRemaining();
if (currentCommands.isAdjustGunForBodyTurn()) {
currentCommands.setGunTurnRemaining(
currentCommands.getGunTurnRemaining() - currentCommands.getBodyTurnRemaining());
}
if (currentCommands.isAdjustRadarForBodyTurn()) {
currentCommands.setRadarTurnRemaining(
currentCommands.getRadarTurnRemaining() - currentCommands.getBodyTurnRemaining());
}
currentCommands.setBodyTurnRemaining(0);
} else {
bodyHeading += turnRate;
gunHeading += turnRate;
radarHeading += turnRate;
currentCommands.setBodyTurnRemaining(currentCommands.getBodyTurnRemaining() - turnRate);
if (currentCommands.isAdjustGunForBodyTurn()) {
currentCommands.setGunTurnRemaining(currentCommands.getGunTurnRemaining() - turnRate);
}
if (currentCommands.isAdjustRadarForBodyTurn()) {
currentCommands.setRadarTurnRemaining(currentCommands.getRadarTurnRemaining() - turnRate);
}
}
} else if (currentCommands.getBodyTurnRemaining() < 0) {
if (currentCommands.getBodyTurnRemaining() > -turnRate) {
bodyHeading += currentCommands.getBodyTurnRemaining();
gunHeading += currentCommands.getBodyTurnRemaining();
radarHeading += currentCommands.getBodyTurnRemaining();
if (currentCommands.isAdjustGunForBodyTurn()) {
currentCommands.setGunTurnRemaining(
currentCommands.getGunTurnRemaining() - currentCommands.getBodyTurnRemaining());
}
if (currentCommands.isAdjustRadarForBodyTurn()) {
currentCommands.setRadarTurnRemaining(
currentCommands.getRadarTurnRemaining() - currentCommands.getBodyTurnRemaining());
}
currentCommands.setBodyTurnRemaining(0);
} else {
bodyHeading -= turnRate;
gunHeading -= turnRate;
radarHeading -= turnRate;
currentCommands.setBodyTurnRemaining(currentCommands.getBodyTurnRemaining() + turnRate);
if (currentCommands.isAdjustGunForBodyTurn()) {
currentCommands.setGunTurnRemaining(currentCommands.getGunTurnRemaining() + turnRate);
}
if (currentCommands.isAdjustRadarForBodyTurn()) {
currentCommands.setRadarTurnRemaining(currentCommands.getRadarTurnRemaining() + turnRate);
}
}
} else {
normalizeHeading = false;
}
if (normalizeHeading) {
if (currentCommands.getBodyTurnRemaining() == 0) {
bodyHeading = normalNearAbsoluteAngle(bodyHeading);
} else {
bodyHeading = normalAbsoluteAngle(bodyHeading);
}
}
if (Double.isNaN(bodyHeading)) {
Logger.realErr.println("HOW IS HEADING NAN HERE");
}
}
private void updateRadarHeading() {
if (currentCommands.getRadarTurnRemaining() > 0) {
if (currentCommands.getRadarTurnRemaining() < Rules.RADAR_TURN_RATE_RADIANS) {
radarHeading += currentCommands.getRadarTurnRemaining();
currentCommands.setRadarTurnRemaining(0);
} else {
radarHeading += Rules.RADAR_TURN_RATE_RADIANS;
currentCommands.setRadarTurnRemaining(
currentCommands.getRadarTurnRemaining() - Rules.RADAR_TURN_RATE_RADIANS);
}
} else if (currentCommands.getRadarTurnRemaining() < 0) {
if (currentCommands.getRadarTurnRemaining() > -Rules.RADAR_TURN_RATE_RADIANS) {
radarHeading += currentCommands.getRadarTurnRemaining();
currentCommands.setRadarTurnRemaining(0);
} else {
radarHeading -= Rules.RADAR_TURN_RATE_RADIANS;
currentCommands.setRadarTurnRemaining(
currentCommands.getRadarTurnRemaining() + Rules.RADAR_TURN_RATE_RADIANS);
}
}
radarHeading = normalAbsoluteAngle(radarHeading);
}
/**
* Updates the robots movement.
*
* This is Nat Pavasants method described here:
* http://robowiki.net/wiki/User:Positive/Optimal_Velocity#Nat.27s_updateMovement
*/
private void updateMovement() {
double distance = currentCommands.getDistanceRemaining();
if (Double.isNaN(distance)) {
distance = 0;
}
velocity = getNewVelocity(velocity, distance);
// If we are over-driving our distance and we are now at velocity=0
// then we stopped.
if (isNear(velocity, 0) && isOverDriving) {
currentCommands.setDistanceRemaining(0);
distance = 0;
isOverDriving = false;
}
// If we are moving normally and the breaking distance is more
// than remaining distance, enabled the overdrive flag.
if (Math.signum(distance * velocity) != -1) {
if (getDistanceTraveledUntilStop(velocity) > Math.abs(distance)) {
isOverDriving = true;
} else {
isOverDriving = false;
}
}
currentCommands.setDistanceRemaining(distance - velocity);
if (velocity != 0) {
x += velocity * sin(bodyHeading);
y += velocity * cos(bodyHeading);
updateBoundingBox();
}
}
private double getDistanceTraveledUntilStop(double velocity) {
double distance = 0;
velocity = Math.abs(velocity);
while (velocity > 0) {
distance += (velocity = getNewVelocity(velocity, 0));
}
return distance;
}
/**
* Returns the new velocity based on the current velocity and distance to move.
*
* @param velocity the current velocity
* @param distance the distance to move
* @return the new velocity based on the current velocity and distance to move
*
* This is Patrick Cupka (aka Voidious), Julian Kent (aka Skilgannon), and Positive's method described here:
* http://robowiki.net/wiki/User:Voidious/Optimal_Velocity#Hijack_2
*/
private double getNewVelocity(double velocity, double distance) {
if (distance < 0) {
// If the distance is negative, then change it to be positive
// and change the sign of the input velocity and the result
return -getNewVelocity(-velocity, -distance);
}
final double goalVel;
if (distance == Double.POSITIVE_INFINITY) {
goalVel = currentCommands.getMaxVelocity();
} else {
goalVel = Math.min(getMaxVelocity(distance), currentCommands.getMaxVelocity());
}
if (velocity >= 0) {
return Math.max(velocity - Rules.DECELERATION, Math.min(goalVel, velocity + Rules.ACCELERATION));
}
// else
return Math.max(velocity - Rules.ACCELERATION, Math.min(goalVel, velocity + maxDecel(-velocity)));
}
private final static double getMaxVelocity(double distance) {
final double decelTime = Math.max(1, Math.ceil(// sum of 0... decelTime, solving for decelTime using quadratic formula
(Math.sqrt((4 * 2 / Rules.DECELERATION) * distance + 1) - 1) / 2));
if (decelTime == Double.POSITIVE_INFINITY) {
return Rules.MAX_VELOCITY;
}
final double decelDist = (decelTime / 2.0) * (decelTime - 1) // sum of 0..(decelTime-1)
* Rules.DECELERATION;
return ((decelTime - 1) * Rules.DECELERATION) + ((distance - decelDist) / decelTime);
}
private static double maxDecel(double speed) {
double decelTime = speed / Rules.DECELERATION;
double accelTime = (1 - decelTime);
return Math.min(1, decelTime) * Rules.DECELERATION + Math.max(0, accelTime) * Rules.ACCELERATION;
}
private void updateGunHeat() {
gunHeat -= battleRules.getGunCoolingRate();
if (gunHeat < 0) {
gunHeat = 0;
}
}
private void scan(double lastRadarHeading, List<RobotPeer> robots) {
if (statics.isDroid()) {
return;
}
double startAngle = lastRadarHeading;
double scanRadians = getRadarHeading() - startAngle;
// Check if we passed through 360
if (scanRadians < -PI) {
scanRadians = 2 * PI + scanRadians;
} else if (scanRadians > PI) {
scanRadians = scanRadians - 2 * PI;
}
// In our coords, we are scanning clockwise, with +y up
// In java coords, we are scanning counterclockwise, with +y down
// All we need to do is adjust our angle by -90 for this to work.
startAngle -= PI / 2;
startAngle = normalAbsoluteAngle(startAngle);
scanArc.setArc(x - Rules.RADAR_SCAN_RADIUS, y - Rules.RADAR_SCAN_RADIUS, 2 * Rules.RADAR_SCAN_RADIUS,
2 * Rules.RADAR_SCAN_RADIUS, 180.0 * startAngle / PI, 180.0 * scanRadians / PI, Arc2D.PIE);
for (RobotPeer otherRobot : robots) {
if (!(otherRobot == null || otherRobot == this || otherRobot.isDead())
&& intersects(scanArc, otherRobot.boundingBox)) {
double dx = otherRobot.x - x;
double dy = otherRobot.y - y;
double angle = atan2(dx, dy);
double dist = Math.hypot(dx, dy);
final ScannedRobotEvent event = new ScannedRobotEvent(getNameForEvent(otherRobot), otherRobot.energy,
normalRelativeAngle(angle - getBodyHeading()), dist, otherRobot.getBodyHeading(),
otherRobot.getVelocity(), otherRobot.isSentryRobot());
addEvent(event);
}
}
}
private boolean intersects(Arc2D arc, Rectangle2D rect) {
return (rect.intersectsLine(arc.getCenterX(), arc.getCenterY(), arc.getStartPoint().getX(),
arc.getStartPoint().getY()))
|| arc.intersects(rect);
}
private void zap(double zapAmount) {
if (energy == 0) {
kill();
return;
}
energy -= abs(zapAmount);
if (energy < .1) {
energy = 0;
currentCommands.setDistanceRemaining(0);
currentCommands.setBodyTurnRemaining(0);
}
}
public void setRunning(boolean value) {
isRunning.set(value);
}
public void drainEnergy() {
setEnergy(0, true);
isEnergyDrained = true;
}
public void punishBadBehavior(BadBehavior badBehavior) {
kill(); // Bug fix [2828479] - Missed onRobotDeath events
statistics.setInactive();
final IRobotItem repositoryItem = (IRobotItem) HiddenAccess.getFileSpecification(robotSpecification);
StringBuffer message = new StringBuffer(getName()).append(' ');
boolean disableInRepository = false; // Per default, robots are not disabled in the repository
switch (badBehavior) {
case CANNOT_START:
message.append("could not be started or loaded.");
disableInRepository = true; // Disable in repository when it cannot be started anyways
break;
case UNSTOPPABLE:
message.append("cannot be stopped.");
break;
case SKIPPED_TOO_MANY_TURNS:
message.append("has skipped too many turns.");
break;
case SECURITY_VIOLATION:
message.append("has caused a security violation.");
disableInRepository = true; // No mercy here!
break;
}
if (disableInRepository) {
repositoryItem.setValid(false);
message.append(" This ").append(repositoryItem.isTeam() ? "team" : "robot").append(
" has been banned and will not be allowed to participate in battles.");
}
logMessage(message.toString());
}
void updateEnergy(double delta) {
if ((!isExecFinishedAndDisabled && !isEnergyDrained) || delta < 0) {
setEnergy(energy + delta, true);
}
}
private void setEnergy(double newEnergy, boolean resetInactiveTurnCount) {
if (resetInactiveTurnCount && (energy != newEnergy)) {
battle.resetInactiveTurnCount(energy - newEnergy);
}
energy = newEnergy;
if (energy < .01) {
energy = 0;
ExecCommands localCommands = commands.get();
localCommands.setDistanceRemaining(0);
localCommands.setBodyTurnRemaining(0);
}
}
public void setWinner(boolean newWinner) {
isWinner = newWinner;
}
public void kill() {
battle.resetInactiveTurnCount(10.0);
if (isAlive()) {
addEvent(new DeathEvent());
if (statics.isTeamLeader()) {
for (RobotPeer teammate : teamPeer) {
if (teammate.isAlive() && teammate != this) {
teammate.updateEnergy(-30);
BulletPeer sBullet = new BulletPeer(this, battleRules, 0);
sBullet.setState(BulletState.HIT_VICTIM);
sBullet.setX(teammate.x);
sBullet.setY(teammate.y);
sBullet.setVictim(teammate);
sBullet.setPower(4);
battle.addBullet(sBullet);
}
}
}
battle.registerDeathRobot(this);
// 'fake' bullet for explosion on self
final ExplosionPeer fake = new ExplosionPeer(this, battleRules);
battle.addBullet(fake);
}
updateEnergy(-energy);
setState(RobotState.DEAD);
}
public void waitForStop() {
robotProxy.waitForStopThread();
}
/**
* Clean things up removing all references to the robot.
*/
public void cleanup() {
battle = null;
if (robotProxy != null) {
robotProxy.cleanup();
robotProxy = null;
}
if (statistics != null) {
statistics.cleanup();
statistics = null;
}
status = null;
commands = null;
events = null;
teamMessages = null;
bulletUpdates = null;
battleText.setLength(0);
proxyText.setLength(0);
statics = null;
battleRules = null;
}
public Object getGraphicsCalls() {
return commands.get().getGraphicsCalls();
}
public boolean isTryingToPaint() {
return commands.get().isTryingToPaint();
}
public List<DebugProperty> getDebugProperties() {
return commands.get().getDebugProperties();
}
public void publishStatus(long currentTurn) {
final ExecCommands currentCommands = commands.get();
int others = battle.countActiveParticipants() - (isDead() || isSentryRobot() ? 0 : 1);
int numSentries = battle.countActiveSentries();
RobotStatus stat = HiddenAccess.createStatus(energy, x, y, bodyHeading, gunHeading, radarHeading, velocity,
currentCommands.getBodyTurnRemaining(), currentCommands.getRadarTurnRemaining(),
currentCommands.getGunTurnRemaining(), currentCommands.getDistanceRemaining(), gunHeat, others, numSentries,
battle.getRoundNum(), battle.getNumRounds(), battle.getTime());
status.set(stat);
}
void addBulletStatus(BulletStatus bulletStatus) {
if (isAlive()) {
bulletUpdates.get().add(bulletStatus);
}
}
public int compareTo(ContestantPeer cp) {
double myScore = statistics.getTotalScore();
double hisScore = cp.getStatistics().getTotalScore();
if (statistics.isInRound()) {
myScore += statistics.getCurrentScore();
hisScore += cp.getStatistics().getCurrentScore();
}
if (myScore < hisScore) {
return -1;
}
if (myScore > hisScore) {
return 1;
}
return 0;
}
@Override
public String toString() {
return statics.getShortName() + "(" + (int) energy + ") X" + (int) x + " Y" + (int) y + " " + state.toString()
+ (isSleeping() ? " sleeping " : "") + (isRunning() ? " running" : "") + (isHalt() ? " halted" : "");
}
}