/**
* 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;
import net.sf.robocode.battle.events.BattleEventDispatcher;
import net.sf.robocode.battle.peer.BulletPeer;
import net.sf.robocode.battle.peer.ContestantPeer;
import net.sf.robocode.battle.peer.RobotPeer;
import net.sf.robocode.battle.peer.TeamPeer;
import net.sf.robocode.battle.snapshot.TurnSnapshot;
import net.sf.robocode.host.ICpuManager;
import net.sf.robocode.host.IHostManager;
import net.sf.robocode.io.Logger;
import net.sf.robocode.io.RobocodeProperties;
import net.sf.robocode.repository.IRobotItem;
import net.sf.robocode.security.HiddenAccess;
import net.sf.robocode.settings.ISettingsManager;
import robocode.*;
import robocode.control.RandomFactory;
import robocode.control.RobotResults;
import robocode.control.RobotSetup;
import robocode.control.RobotSpecification;
import robocode.control.events.*;
import robocode.control.events.RoundEndedEvent;
import robocode.control.snapshot.BulletState;
import robocode.control.snapshot.ITurnSnapshot;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The {@code Battle} class is used for controlling a battle.
*
* @author Mathew A. Nelson (original)
* @author Flemming N. Larsen (contributor)
* @author Luis Crespo (contributor)
* @author Robert D. Maupin (contributor)
* @author Titus Chen (contributor)
* @author Nathaniel Troutman (contributor)
* @author Julian Kent (contributor)
* @author Pavel Savara (contributor)
*/
public final class Battle extends BaseBattle {
private static final int DEBUG_TURN_WAIT_MILLIS = 10 * 60 * 1000; // 10 seconds
private final IHostManager hostManager;
private final long cpuConstant;
// Inactivity related items
private int inactiveTurnCount;
private double inactivityEnergy;
// Turn skip related items
private boolean parallelOn;
private long millisWait;
private int nanoWait;
// Objects in the battle
private int robotsCount;
private List<RobotPeer> robots = new ArrayList<RobotPeer>();
private List<ContestantPeer> contestants = new ArrayList<ContestantPeer>();
private final List<BulletPeer> bullets = new CopyOnWriteArrayList<BulletPeer>();
// Robot counters
private int activeParticipants;
private int activeSentries;
// Death events
private final List<RobotPeer> deathRobots = new CopyOnWriteArrayList<RobotPeer>();
// Initial robot setups (if any)
private RobotSetup[] initialRobotSetups;
public Battle(ISettingsManager properties, IBattleManager battleManager, IHostManager hostManager, ICpuManager cpuManager, BattleEventDispatcher eventDispatcher) { // NO_UCD (unused code)
super(
properties, battleManager, eventDispatcher);
this.hostManager = hostManager;
this.cpuConstant = cpuManager.getCpuConstant();
}
void setup(RobotSpecification[] battlingRobotsList, BattleProperties battleProps, boolean paused) {
isPaused = paused;
battleRules = HiddenAccess.createRules(battleProps.getBattlefieldWidth(), battleProps.getBattlefieldHeight(),
battleProps.getNumRounds(), battleProps.getGunCoolingRate(), battleProps.getInactivityTime(),
battleProps.getHideEnemyNames(), battleProps.getSentryBorderSize());
robotsCount = battlingRobotsList.length;
computeInitialPositions(battleProps.getInitialPositions());
createPeers(battlingRobotsList);
}
private void createPeers(RobotSpecification[] battlingRobotsList) {
// create teams
Map<String, Integer> countedNames = new HashMap<String, Integer>();
List<String> teams = new ArrayList<String>();
List<String> teamDuplicates = new ArrayList<String>();
List<Integer> robotDuplicates = new ArrayList<Integer>();
// count duplicate robots, enumerate teams, enumerate team members
for (RobotSpecification specification : battlingRobotsList) {
final String name = ((IRobotItem) HiddenAccess.getFileSpecification(specification)).getUniqueFullClassNameWithVersion();
if (countedNames.containsKey(name)) {
int value = countedNames.get(name);
countedNames.put(name, value == 1 ? 3 : value + 1);
} else {
countedNames.put(name, 1);
}
String teamFullName = HiddenAccess.getRobotTeamName(specification);
if (teamFullName != null) {
if (!teams.contains(teamFullName)) {
teams.add(teamFullName);
String teamName = teamFullName.substring(0, teamFullName.length() - 6);
if (countedNames.containsKey(teamName)) {
int value = countedNames.get(teamName);
countedNames.put(teamName, value == 1 ? 3 : value + 1);
} else {
countedNames.put(teamName, 1);
}
}
}
}
Map<String, List<String>> teamMembers = new HashMap<String, List<String>>();
// name teams
for (int i = teams.size() - 1; i >= 0; i--) {
String teamFullName = teams.get(i);
String name = teamFullName.substring(0, teamFullName.length() - 6);
Integer order = countedNames.get(name);
String newTeamName = name;
if (order > 1) {
newTeamName = name + " (" + (order - 1) + ")";
}
teamDuplicates.add(0, newTeamName);
teamMembers.put(teamFullName, new ArrayList<String>());
countedNames.put(name, order - 1);
}
// name robots
for (int i = battlingRobotsList.length - 1; i >= 0; i--) {
RobotSpecification specification = battlingRobotsList[i];
String name = ((IRobotItem) HiddenAccess.getFileSpecification(specification)).getUniqueFullClassNameWithVersion();
Integer order = countedNames.get(name);
int duplicate = -1;
String newName = name;
if (order > 1) {
duplicate = (order - 2);
newName = name + " (" + (order - 1) + ")";
}
countedNames.put(name, (order - 1));
robotDuplicates.add(0, duplicate);
String teamFullName = HiddenAccess.getRobotTeamName(specification);
if (teamFullName != null) {
List<String> members = teamMembers.get(teamFullName);
members.add(newName);
}
}
// create teams
Map<String, TeamPeer> namedTeams = new HashMap<String, TeamPeer>();
// create robots
for (int i = 0; i < battlingRobotsList.length; i++) {
RobotSpecification specification = battlingRobotsList[i];
TeamPeer team = null;
String teamFullName = HiddenAccess.getRobotTeamName(specification);
// The team index and robot index depends on current sizes of the contestant list and robot list
int teamIndex = contestants.size();
int robotIndex = robots.size();
if (teamFullName != null) {
if (!namedTeams.containsKey(teamFullName)) {
String newTeamName = teamDuplicates.get(teams.indexOf(teamFullName));
team = new TeamPeer(newTeamName, teamMembers.get(teamFullName), teamIndex);
namedTeams.put(teamFullName, team);
contestants.add(team);
} else {
team = namedTeams.get(teamFullName);
if (team != null) {
teamIndex = team.getTeamIndex();
}
}
}
Integer duplicate = robotDuplicates.get(i);
RobotPeer robotPeer = new RobotPeer(this, hostManager, specification, duplicate, team, robotIndex);
robots.add(robotPeer);
if (team == null) {
contestants.add(robotPeer);
}
}
}
public void registerDeathRobot(RobotPeer r) {
deathRobots.add(r);
}
public BattleRules getBattleRules() {
return battleRules;
}
public int getRobotsCount() {
return robotsCount;
}
public boolean isDebugging() {
return RobocodeProperties.isDebuggingOn();
}
public void addBullet(BulletPeer bullet) {
bullets.add(bullet);
}
public void resetInactiveTurnCount(double energyLoss) {
if (energyLoss < 0) {
return;
}
inactivityEnergy += energyLoss;
while (inactivityEnergy >= 10) {
inactivityEnergy -= 10;
inactiveTurnCount = 0;
}
}
/**
* Returns the number of active participants.
*
* @return the number of active participants.
*/
public int countActiveParticipants() {
return activeParticipants;
}
/**
* Returns the number of active sentry robots.
*
* @return the number of active sentry robots.
*/
public int countActiveSentries() {
return activeSentries;
}
@Override
public void cleanup() {
if (contestants != null) {
contestants.clear();
contestants = null;
}
if (robots != null) {
robots.clear();
robots = null;
}
super.cleanup();
battleManager = null;
// Request garbage collecting
for (int i = 4; i >= 0; i--) { // Make sure it is run
System.gc();
}
}
@Override
protected void initializeBattle() {
super.initializeBattle();
parallelOn = System.getProperty("PARALLEL", "false").equals("true");
if (parallelOn) {
// how could robots share CPUs ?
double parallelConstant = robots.size() / Runtime.getRuntime().availableProcessors();
// four CPUs can't run two single threaded robot faster than two CPUs
if (parallelConstant < 1) {
parallelConstant = 1;
}
final long waitTime = (long) (cpuConstant * parallelConstant);
millisWait = waitTime / 1000000;
nanoWait = (int) (waitTime % 1000000);
} else {
millisWait = cpuConstant / 1000000;
nanoWait = (int) (cpuConstant % 1000000);
}
if (nanoWait == 0) {
nanoWait = 1;
}
}
@Override
protected void finalizeBattle() {
eventDispatcher.onBattleFinished(new BattleFinishedEvent(isAborted()));
if (!isAborted()) {
eventDispatcher.onBattleCompleted(new BattleCompletedEvent(battleRules, computeBattleResults()));
}
for (RobotPeer robotPeer : robots) {
robotPeer.cleanup();
}
hostManager.resetThreadManager();
super.finalizeBattle();
}
@Override
protected void preloadRound() {
super.preloadRound();
computeActiveRobots(); // Used for robotPeer.initializeRound()
// At this point the unsafe loader thread will now set itself to wait for a notify
for (RobotPeer robotPeer : robots) {
robotPeer.initializeRound(robots, initialRobotSetups);
robotPeer.println("=========================");
robotPeer.println("Round " + (getRoundNum() + 1) + " of " + getNumRounds());
robotPeer.println("=========================");
}
if (getRoundNum() == 0) {
eventDispatcher.onBattleStarted(new BattleStartedEvent(battleRules, robots.size(), false));
if (isPaused) {
eventDispatcher.onBattlePaused(new BattlePausedEvent());
}
}
computeActiveRobots(); // Used for RoundEnded check hostManager.resetThreadManager();
}
@Override
protected void initializeRound() {
super.initializeRound();
inactiveTurnCount = 0;
// Start robots
long waitMillis;
int waitNanos;
if (isDebugging()) {
waitMillis = DEBUG_TURN_WAIT_MILLIS;
waitNanos = 0;
} else {
long waitTime = Math.min(300 * cpuConstant, 10000000000L);
waitMillis = waitTime / 1000000;
waitNanos = (int) (waitTime % 1000000);
}
for (RobotPeer robotPeer : getRobotsAtRandom()) {
robotPeer.startRound(waitMillis, waitNanos);
}
Logger.logMessage(""); // puts in a new-line in the log message
final ITurnSnapshot snapshot = new TurnSnapshot(this, robots, bullets, false);
eventDispatcher.onRoundStarted(new RoundStartedEvent(snapshot, getRoundNum()));
}
@Override
protected void finalizeRound() {
super.finalizeRound();
for (RobotPeer robotPeer : robots) {
robotPeer.waitForStop();
}
bullets.clear();
eventDispatcher.onRoundEnded(new RoundEndedEvent(getRoundNum(), currentTime, totalTurns));
}
@Override
protected void initializeTurn() {
super.initializeTurn();
eventDispatcher.onTurnStarted(new TurnStartedEvent());
}
@Override
protected void runTurn() {
super.runTurn();
loadCommands();
updateBullets();
updateRobots();
handleDeadRobots();
if (isAborted() || oneTeamRemaining()) {
shutdownTurn();
}
inactiveTurnCount++;
computeActiveRobots();
publishStatuses();
// Robot time!
wakeupRobots();
}
@Override
protected void shutdownTurn() {
if (endTimer == 0) {
if (isAborted()) {
for (RobotPeer robotPeer : getRobotsAtRandom()) {
if (robotPeer.isAlive()) {
robotPeer.println("SYSTEM: game aborted.");
}
}
} else if (oneTeamRemaining()) {
boolean leaderFirsts = false;
TeamPeer winningTeam = null;
robocode.RoundEndedEvent roundEndedEvent = new robocode.RoundEndedEvent(getRoundNum(), currentTime,
totalTurns);
for (RobotPeer robotPeer : getRobotsAtRandom()) {
robotPeer.addEvent(roundEndedEvent);
if (robotPeer.isAlive() && !robotPeer.isWinner() && !robotPeer.isSentryRobot()) {
robotPeer.getRobotStatistics().scoreLastSurvivor();
robotPeer.setWinner(true);
robotPeer.println("SYSTEM: " + robotPeer.getNameForEvent(robotPeer) + " wins the round.");
robotPeer.addEvent(new WinEvent());
if (robotPeer.getTeamPeer() != null) {
if (robotPeer.isTeamLeader()) {
leaderFirsts = true;
} else {
winningTeam = robotPeer.getTeamPeer();
}
}
}
// Generate totals as round has ended, but first when the last scores has been calculated
robotPeer.getRobotStatistics().generateTotals();
}
if (!leaderFirsts && winningTeam != null) {
winningTeam.getTeamLeader().getRobotStatistics().scoreFirsts();
}
}
if (isAborted() || isLastRound()) {
List<RobotPeer> orderedRobots = new ArrayList<RobotPeer>(robots);
Collections.sort(orderedRobots);
Collections.reverse(orderedRobots);
for (int rank = 0; rank < robots.size(); rank++) {
RobotPeer robotPeer = orderedRobots.get(rank);
robotPeer.getStatistics().setRank(rank + 1);
BattleResults battleResults = robotPeer.getStatistics().getFinalResults();
robotPeer.addEvent(new BattleEndedEvent(isAborted(), battleResults));
}
}
}
if (endTimer > 4 * TURNS_DISPLAYED_AFTER_ENDING) {
for (RobotPeer robotPeer : robots) {
robotPeer.setHalt(true);
}
}
super.shutdownTurn();
}
@Override
protected void finalizeTurn() {
eventDispatcher.onTurnEnded(new TurnEndedEvent(new TurnSnapshot(this, robots, bullets, true)));
super.finalizeTurn();
}
private BattleResults[] computeBattleResults() {
ArrayList<BattleResults> results = new ArrayList<BattleResults>();
for (int i = 0; i < contestants.size(); i++) {
results.add(null);
}
for (int rank = 0; rank < contestants.size(); rank++) {
ContestantPeer contestant = contestants.get(rank);
contestant.getStatistics().setRank(rank + 1);
BattleResults battleResults = contestant.getStatistics().getFinalResults();
RobotSpecification robotSpec = null;
if (contestant instanceof RobotPeer) {
robotSpec = ((RobotPeer) contestant).getRobotSpecification();
} else if (contestant instanceof TeamPeer) {
robotSpec = ((TeamPeer) contestant).getTeamLeader().getRobotSpecification();
}
results.set(rank, new RobotResults(robotSpec, battleResults));
}
return results.toArray(new BattleResults[results.size()]);
}
/**
* Returns a list of all robots in random order. This method is used to gain fair play in Robocode,
* so that a robot placed before another robot in the list will not gain any benefit when the game
* checks if a robot has won, is dead, etc.
* This method was introduced as two equal robots like sample.RamFire got different scores even
* though the code was exactly the same.
*
* @return a list of robot peers.
*/
private List<RobotPeer> getRobotsAtRandom() {
List<RobotPeer> shuffledList = new ArrayList<RobotPeer>(robots);
Collections.shuffle(shuffledList, RandomFactory.getRandom());
return shuffledList;
}
/**
* Returns a list of all bullets in random order. This method is used to gain fair play in Robocode.
*
* @return a list of bullet peers.
*/
private List<BulletPeer> getBulletsAtRandom() {
List<BulletPeer> shuffledList = new ArrayList<BulletPeer>(bullets);
Collections.shuffle(shuffledList, RandomFactory.getRandom());
return shuffledList;
}
/**
* Returns a list of all death robots in random order. This method is used to gain fair play in Robocode.
*
* @return a list of robot peers.
*/
private List<RobotPeer> getDeathRobotsAtRandom() {
List<RobotPeer> shuffledList = new ArrayList<RobotPeer>(deathRobots);
Collections.shuffle(shuffledList, RandomFactory.getRandom());
return shuffledList;
}
private void loadCommands() {
// this will load commands, including bullets from last turn
for (RobotPeer robotPeer : robots) {
robotPeer.performLoadCommands();
}
}
private void updateBullets() {
for (BulletPeer bullet : getBulletsAtRandom()) {
bullet.update(getRobotsAtRandom(), getBulletsAtRandom());
if (bullet.getState() == BulletState.INACTIVE) {
bullets.remove(bullet);
}
}
}
private void updateRobots() {
boolean zap = (inactiveTurnCount > battleRules.getInactivityTime());
final double zapEnergy = isAborted() ? 5 : zap ? .1 : 0;
// Move all bots
for (RobotPeer robotPeer : getRobotsAtRandom()) {
robotPeer.performMove(getRobotsAtRandom(), zapEnergy);
}
// Scan after moved all
for (RobotPeer robotPeer : getRobotsAtRandom()) {
robotPeer.performScan(getRobotsAtRandom());
}
}
private void handleDeadRobots() {
for (RobotPeer deadRobot : getDeathRobotsAtRandom()) {
// Compute scores for dead robots
if (deadRobot.getTeamPeer() == null) {
deadRobot.getRobotStatistics().scoreRobotDeath(getActiveContestantCount(deadRobot));
} else {
boolean teammatesalive = false;
for (RobotPeer tm : robots) {
if (tm.getTeamPeer() == deadRobot.getTeamPeer() && tm.isAlive()) {
teammatesalive = true;
break;
}
}
if (!teammatesalive) {
deadRobot.getRobotStatistics().scoreRobotDeath(getActiveContestantCount(deadRobot));
}
}
// Publish death to live robots
for (RobotPeer robotPeer : getRobotsAtRandom()) {
if (robotPeer.isAlive()) {
robotPeer.addEvent(new RobotDeathEvent(robotPeer.getNameForEvent(deadRobot)));
if (robotPeer.getTeamPeer() == null || robotPeer.getTeamPeer() != deadRobot.getTeamPeer()) {
robotPeer.getRobotStatistics().scoreSurvival();
}
}
}
}
deathRobots.clear();
}
private void publishStatuses() {
for (RobotPeer robotPeer : robots) {
robotPeer.publishStatus(currentTime);
}
}
private void computeActiveRobots() {
int countActiveParticipants = 0;
int countActiveSentries = 0;
for (RobotPeer robot : robots) {
if (robot.isAlive()) { // robot must be alive in order to be active
if (robot.isSentryRobot()) {
countActiveSentries++;
} else {
countActiveParticipants++;
}
}
}
this.activeParticipants = countActiveParticipants;
this.activeSentries = countActiveSentries;
}
private void wakeupRobots() {
// Wake up all robot threads
final List<RobotPeer> robotsAtRandom = getRobotsAtRandom();
if (parallelOn) {
wakeupParallel(robotsAtRandom);
} else {
wakeupSerial(robotsAtRandom);
}
}
private void wakeupSerial(List<RobotPeer> robotsAtRandom) {
for (RobotPeer robotPeer : robotsAtRandom) {
if (robotPeer.isRunning()) {
// This call blocks until the robot's thread actually wakes up.
robotPeer.waitWakeup();
if (robotPeer.isAlive()) {
if (isDebugging() || robotPeer.isPaintEnabled()) {
robotPeer.waitSleeping(DEBUG_TURN_WAIT_MILLIS, 1);
} else if (currentTime == 1) {
robotPeer.waitSleeping(millisWait * 10, 1);
} else {
robotPeer.waitSleeping(millisWait, nanoWait);
}
}
}
}
}
private void wakeupParallel(List<RobotPeer> robotsAtRandom) {
for (RobotPeer robotPeer : robotsAtRandom) {
if (robotPeer.isRunning()) {
// This call blocks until the robot's thread actually wakes up.
robotPeer.waitWakeup();
}
}
for (RobotPeer robotPeer : robotsAtRandom) {
if (robotPeer.isRunning() && robotPeer.isAlive()) {
if (isDebugging() || robotPeer.isPaintEnabled()) {
robotPeer.waitSleeping(DEBUG_TURN_WAIT_MILLIS, 1);
} else if (currentTime == 1) {
robotPeer.waitSleeping(millisWait * 10, 1);
} else {
robotPeer.waitSleeping(millisWait, nanoWait);
}
}
}
}
private int getActiveContestantCount(RobotPeer peer) {
int count = 0;
for (ContestantPeer c : contestants) {
if (c instanceof RobotPeer) {
RobotPeer robot = (RobotPeer) c;
if (!robot.isSentryRobot() && robot.isAlive()) {
count++;
}
} else if (c instanceof TeamPeer && c != peer.getTeamPeer()) {
for (RobotPeer robot: (TeamPeer) c) {
if (!robot.isSentryRobot() && robot.isAlive()) {
count++;
break;
}
}
}
}
return count;
}
private void computeInitialPositions(String initialPositions) {
initialRobotSetups = null;
if (initialPositions == null || initialPositions.trim().length() == 0) {
return;
}
List<String> positions = new ArrayList<String>();
Pattern pattern = Pattern.compile("([^,(]*[(][^)]*[)])?[^,]*,?");
Matcher matcher = pattern.matcher(initialPositions);
while (matcher.find()) {
String pos = matcher.group();
if (pos.length() > 0) {
positions.add(pos);
}
}
if (positions.size() == 0) {
return;
}
initialRobotSetups = new RobotSetup[positions.size()];
String[] coords;
double x, y, heading;
for (int i = 0; i < positions.size(); i++) {
coords = positions.get(i).split(",");
Random random = RandomFactory.getRandom();
x = RobotPeer.WIDTH + random.nextDouble() * (battleRules.getBattlefieldWidth() - 2 * RobotPeer.WIDTH);
y = RobotPeer.HEIGHT + random.nextDouble() * (battleRules.getBattlefieldHeight() - 2 * RobotPeer.HEIGHT);
heading = 2 * Math.PI * random.nextDouble();
int len = coords.length;
if (len >= 1 && coords[0].trim().length() > 0) {
try {
x = Double.parseDouble(coords[0].replaceAll("[^0-9.]", ""));
} catch (NumberFormatException ignore) {// Could be the '?', which is fine
}
if (len >= 2 && coords[1].trim().length() > 0) {
try {
y = Double.parseDouble(coords[1].replaceAll("[^0-9.]", ""));
} catch (NumberFormatException ignore) {// Could be the '?', which is fine
}
if (len >= 3 && coords[2].trim().length() > 0) {
try {
heading = Math.toRadians(Double.parseDouble(coords[2].replaceAll("[^0-9.]", "")));
} catch (NumberFormatException ignore) {// Could be the '?', which is fine
}
}
}
}
initialRobotSetups[i] = new RobotSetup(x, y, heading);
}
}
private boolean oneTeamRemaining() {
if (countActiveParticipants() <= 1) {
return true;
}
boolean found = false;
TeamPeer currentTeam = null;
for (RobotPeer currentRobot : robots) {
if (currentRobot.isAlive()) {
if (!found) {
found = true;
currentTeam = currentRobot.getTeamPeer();
} else {
if (currentTeam == null && currentRobot.getTeamPeer() == null) {
return false;
}
if (currentTeam != currentRobot.getTeamPeer()) {
return false;
}
}
}
}
return true;
}
// --------------------------------------------------------------------------
// Processing and maintaining robot and battle controls
// --------------------------------------------------------------------------
void killRobot(int robotIndex) {
sendCommand(new KillRobotCommand(robotIndex));
}
public void setPaintEnabled(int robotIndex, boolean enable) {
sendCommand(new EnableRobotPaintCommand(robotIndex, enable));
}
void setSGPaintEnabled(int robotIndex, boolean enable) {
sendCommand(new EnableRobotSGPaintCommand(robotIndex, enable));
}
void sendInteractiveEvent(Event e) {
sendCommand(new SendInteractiveEventCommand(e));
}
private class KillRobotCommand extends RobotCommand {
KillRobotCommand(int robotIndex) {
super(robotIndex);
}
public void execute() {
robots.get(robotIndex).kill();
}
}
private class EnableRobotPaintCommand extends RobotCommand {
final boolean enablePaint;
EnableRobotPaintCommand(int robotIndex, boolean enablePaint) {
super(robotIndex);
this.enablePaint = enablePaint;
}
public void execute() {
robots.get(robotIndex).setPaintEnabled(enablePaint);
}
}
private class EnableRobotSGPaintCommand extends RobotCommand {
final boolean enableSGPaint;
EnableRobotSGPaintCommand(int robotIndex, boolean enableSGPaint) {
super(robotIndex);
this.enableSGPaint = enableSGPaint;
}
public void execute() {
robots.get(robotIndex).setSGPaintEnabled(enableSGPaint);
}
}
private class SendInteractiveEventCommand extends Command {
public final Event event;
SendInteractiveEventCommand(Event event) {
this.event = event;
}
public void execute() {
for (RobotPeer robotPeer : robots) {
if (robotPeer.isInteractiveRobot()) {
robotPeer.addEvent(event);
}
}
}
}
}