/******************************************************************************* * 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: * Mathew A. Nelson * - Initial API and implementation * Flemming N. Larsen * - Code cleanup * - Replaced the ContestantPeerVector, BulletPeerVector, and RobotPeerVector * with plain Vector * - Integration of new render classes placed under the robocode.gfx package * - BattleView is not given via the constructor anymore, but is retrieved * from the RobocodeManager. In addition, the battleView is now allowed to * be null, e.g. if no GUI is available * - Ported to Java 5.0 * - Bugfixed sounds that were cut off after first battle * - Changed initialize() to use loadClass() instead of loadRobotClass() if * security is turned off * - Changed the way the TPS is loaded and updated * - Added updateTitle() in order to manage and update the title on the * RobocodeFrame * - Added replay feature * - Updated to use methods from the Logger, which replaces logger methods * that has been (re)moved from the robocode.util.Utils class * - Changed so robots die faster graphically when the battles are over * - Changed cleanup to only remove the robot in the robot peers, as the * robot peers themselves are used for replay recording * - Added support for playing background music when the battle is ongoing * - Removed unnecessary catches of NullPointerExceptions * - Added support for setting the initial robot positions on the battlefield * - Removed the showResultsDialog field which is replaced by the * getOptionsCommonShowResults() from the properties * - Simplified the code in the run() method when battle is stopped * - Changed so that stop() makes the current round stop immediately * - Added handling keyboard events thru a KeyboardEventDispatcher * - Added mouseMoved(), mouseClicked(), mouseReleased(), mouseEntered(), * mouseExited(), mouseDragged(), mouseWheelMoved() * - Changed to take the new JuniorRobot class into account * - When cleaning up robots their static fields are now being cleaned up * - Bugfix: Changed the runRound() so that the robot are painted after * they have made their turn * - The thread handling for unsafe robot loading has been put in an * independent UnsafeLoadRobotsThread class. In addition, the battle * thread is not sharing it's run() method anymore with the * UnsafeLoadRobotsThread, which has now got its own run() method * - The 'running' and 'aborted' flags are now synchronized towards * 'battleMonitor' instead of 'this' object * - Added waitTillRunning() method so another thread can be blocked until * the battle has started running * - Replaced synchronizedList on lists for deathEvent, robots, bullets, * and contestants with a CopyOnWriteArrayList in order to prevent * ConcurrentModificationExceptions when accessing these list via * Iterators using public methods to this class * - The moveBullets() was simplified and moved inside the runRound() method * - The flushOldEvents() method was moved into the runRound() method * - Major bugfix: Two robots running with exactly the same code was getting * different scores. Robots listed before other robots always got a better * score in the end. Hence, the getRobotsAtRandom() method has been added * in order to gain fair play, and this method should be used where robots * are checked and awakened in turn * - Simplified the repainting of the battle * - Bugfix: In wakeupRobots(), only wakeup a robot that is running and alive * - A StatusEvent is now send to all alive robot each turn * - Extended allowed max. length of a robot's full package name from 16 to * 32 characters * Luis Crespo * - Added sound features using the playSounds() method * - Added debug step feature * - Added isRunning() * Robert D. Maupin * - Replaced old collection types like Vector and Hashtable with * synchronized List and HashMap * Titus Chen * - Bugfix: Added Battle parameter to the constructor that takes a * BulletRecord as parameter due to a NullPointerException that was raised * as the battleField variable was not intialized * Nathaniel Troutman * - Bugfix: In order to prevent memory leaks, the cleanup() method has now * been extended to cleanup all robots, but also all classes that this * class refers to in order to avoid circular references. In addition, * cleanup has been added to the KeyEventHandler * Julian Kent * - Fix: Method for using only nano second precision when using * RobotPeer.wait(0, nanoSeconds) in order to prevent the millisecond * granularity issue, which is typically were coarse compared to the one * with nano seconds * Pavel Savara * - Re-work of robot interfaces * - Refactored large methods into several smaller methods * - decomposed RobotPeer from RobotProxy, now sending messages beteen them *******************************************************************************/ 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.BulletPeer; import net.sf.robocode.battle.peer.ProjectilePeer; import net.sf.robocode.battle.peer.RobotPeer; import net.sf.robocode.battle.peer.RobotPeerEngine; import net.sf.robocode.battle.peer.RobotPeerWeaponSystem; 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.repository.IRobotRepositoryItem; 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.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> projectiles = new CopyOnWriteArrayList<BulletPeer>(); private int activeRobots; // Death events private final List<RobotPeer> deathRobots = new CopyOnWriteArrayList<RobotPeer>(); // Flag specifying if debugging is enabled thru the debug command line option private final boolean isDebugging; // Initial robot start positions (if any) private double[][] initialRobotPositions; public Battle(ISettingsManager properties, IBattleManager battleManager, IHostManager hostManager, ICpuManager cpuManager, BattleEventDispatcher eventDispatcher) { super(properties, battleManager, eventDispatcher); isDebugging = System.getProperty("debug", "false").equals("true"); this.hostManager = hostManager; this.cpuConstant = cpuManager.getCpuConstant(); } public void setup(RobotSpecification[] battlingRobotsList, BattleProperties battleProperties, boolean paused) { isPaused = paused; battleRules = HiddenAccess.createRules(battleProperties.getBattlefieldWidth(), battleProperties.getBattlefieldHeight(), battleProperties.getNumRounds(), battleProperties.getGunCoolingRate(), battleProperties.getInactivityTime(), battleProperties.getHideEnemyNames()); robotsCount = battlingRobotsList.length; computeInitialPositions(battleProperties.getInitialPositions()); createPeers(battlingRobotsList); } private void createPeers(RobotSpecification[] battlingRobotsList) { // create teams Hashtable<String, Integer> countedNames = new Hashtable<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 = ((IRobotRepositoryItem) 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); } } } } Hashtable<String, List<String>> teamMembers = new Hashtable<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 = ((IRobotRepositoryItem) 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 Hashtable<String, TeamPeer> namedTeams = new Hashtable<String, TeamPeer>(); // create robots for (int i = 0; i < battlingRobotsList.length; i++) { RobotSpecification specification = battlingRobotsList[i]; TeamPeer team = null; String teamFullName = HiddenAccess.getRobotTeamName(specification); int cindex = contestants.size(); if (teamFullName != null) { if (!namedTeams.containsKey(teamFullName)) { final int teamIndex = teams.indexOf(teamFullName); String newTeamName = teamDuplicates.get(teamIndex); team = new TeamPeer(newTeamName, teamMembers.get(teamFullName), contestants.size()); namedTeams.put(teamFullName, team); contestants.add(team); } else { team = namedTeams.get(teamFullName); if (team != null) { cindex = team.getContestIndex(); } } } Integer duplicate = robotDuplicates.get(i); RobotPeer robotPeer = new RobotPeer(this, hostManager, specification, duplicate, team, robots.size(), cindex); 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 isDebugging; } public void removeBullet(ProjectilePeer bullet) { projectiles.remove(bullet); } public void addBullet(BulletPeer bullet) { projectiles.add(bullet); } public void resetInactiveTurnCount(double energyLoss) { if (energyLoss < 0) { return; } inactivityEnergy += energyLoss; while (inactivityEnergy >= 10) { inactivityEnergy -= 10; inactiveTurnCount = 0; } } /** * Gets the activeRobots. * * @return Returns a int */ public int getActiveRobots() { return activeRobots; } @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(); // At this point the unsafe loader thread will now set itself to wait for a notify for (RobotPeer robotPeer : robots) { robotPeer.initializeRound(robots, initialRobotPositions); 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(); 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, projectiles, false); eventDispatcher.onRoundStarted(new RoundStartedEvent(snapshot, getRoundNum())); } @Override protected void finalizeRound() { super.finalizeRound(); for (RobotPeer robotPeer : robots) { robotPeer.waitForStop(); robotPeer.getRobotStatistics().generateTotals(); } projectiles.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 (getEndTimer() == 0) { if (isAborted()) { for (RobotPeer robotPeer : getRobotsAtRandom()) { if (!robotPeer.isDead()) { robotPeer.println("SYSTEM: game aborted."); } } } else if (oneTeamRemaining()) { boolean leaderFirsts = false; TeamPeer winningTeam = null; final robocode.RoundEndedEvent roundEndedEvent = new robocode.RoundEndedEvent(getRoundNum(), currentTime, totalTurns); for (RobotPeer robotPeer : getRobotsAtRandom()) { robotPeer.addEvent(roundEndedEvent); if (!robotPeer.isDead()) { if (!robotPeer.isWinner()) { 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(); } } } } } if (!leaderFirsts && winningTeam != null) { winningTeam.getTeamLeader().getRobotStatistics().scoreFirsts(); } } } if (getEndTimer() == 1 && (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 resultsForRobot = robotPeer.getStatistics().getFinalResults(); robotPeer.addEvent(new BattleEndedEvent(isAborted(), resultsForRobot)); } } if (getEndTimer() > 4 * 30) { for (RobotPeer robotPeer : robots) { robotPeer.setHalt(true); } } super.shutdownTurn(); } @Override protected void finalizeTurn() { eventDispatcher.onTurnEnded(new TurnEndedEvent(new TurnSnapshot(this, robots, projectiles, true))); super.finalizeTurn(); } private BattleResults[] computeBattleResults() { ArrayList<BattleResults> results = new ArrayList<BattleResults>(); List<ContestantPeer> orderedContestants = new ArrayList<ContestantPeer>(contestants); Collections.sort(orderedContestants); Collections.reverse(orderedContestants); // noinspection ForLoopReplaceableByForEach for (int i = 0; i < contestants.size(); i++) { results.add(null); } for (int rank = 0; rank < contestants.size(); rank++) { RobotSpecification robotSpec = null; ContestantPeer contestant = orderedContestants.get(rank); contestant.getStatistics().setRank(rank + 1); BattleResults battleResults = contestant.getStatistics().getFinalResults(); if (contestant instanceof RobotPeer) { robotSpec = ((RobotPeer) contestant).getRobotSpecification(); } else if (contestant instanceof TeamPeer) { robotSpec = ((TeamPeer) contestant).getTeamLeader().getRobotSpecification(); } results.set(contestant.getContestIndex(), 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>(projectiles); 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 (ProjectilePeer bullet : getBulletsAtRandom()) { bullet.update(getRobotsAtRandom(), getBulletsAtRandom()); if (bullet.getState() == BulletState.INACTIVE) { projectiles.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.isDead())) { teammatesalive = true; break; } } if (!teammatesalive) { deadRobot.getRobotStatistics().scoreRobotDeath(getActiveContestantCount(deadRobot)); } } // Publish death to live robots for (RobotPeer robotPeer : getRobotsAtRandom()) { if (!robotPeer.isDead()) { 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 ar = 0; // Compute active robots for (RobotPeer robotPeer : robots) { if (!robotPeer.isDead()) { ar++; } } this.activeRobots = ar; } 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); } robotPeer.setSkippedTurns(); } } } } private void wakeupParallel(List<RobotPeer> robotsAtRandom) { for (RobotPeer robotPeer : robotsAtRandom) { if (robotPeer.isRunning()) { 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); } } } for (RobotPeer robotPeer : robotsAtRandom) { if (robotPeer.isAlive()) { robotPeer.setSkippedTurns(); } } } private int getActiveContestantCount(RobotPeer peer) { int count = 0; for (ContestantPeer c : contestants) { if (c instanceof RobotPeer && !((RobotPeer) c).isDead()) { count++; } else if (c instanceof TeamPeer && c != peer.getTeamPeer()) { for (RobotPeer robotPeer : (TeamPeer) c) { if (!robotPeer.isDead()) { count++; break; } } } } return count; } private void computeInitialPositions(String initialPositions) { initialRobotPositions = 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; } initialRobotPositions = new double[positions.size()][3]; String[] coords; double x, y, heading; for (int i = 0; i < positions.size(); i++) { coords = positions.get(i).split(","); final Random random = RandomFactory.getRandom(); x = RobotPeerEngine.WIDTH + random.nextDouble() * (battleRules.getBattlefieldWidth() - 2 * RobotPeerEngine.WIDTH); y = RobotPeerEngine.HEIGHT + random.nextDouble() * (battleRules.getBattlefieldHeight() - 2 * RobotPeerEngine.HEIGHT); heading = 2 * Math.PI * random.nextDouble(); int len = coords.length; if (len >= 1) { // noinspection EmptyCatchBlock try { x = Double.parseDouble(coords[0].replaceAll("[\\D]", "")); } catch (NumberFormatException e) {} if (len >= 2) { // noinspection EmptyCatchBlock try { y = Double.parseDouble(coords[1].replaceAll("[\\D]", "")); } catch (NumberFormatException e) {} if (len >= 3) { // noinspection EmptyCatchBlock try { heading = Math.toRadians(Double.parseDouble(coords[2].replaceAll("[\\D]", ""))); } catch (NumberFormatException e) {} } } } initialRobotPositions[i][0] = x; initialRobotPositions[i][1] = y; initialRobotPositions[i][2] = heading; } } private boolean oneTeamRemaining() { if (getActiveRobots() <= 1) { return true; } boolean found = false; TeamPeer currentTeam = null; for (RobotPeer currentRobot : robots) { if (!currentRobot.isDead()) { 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 // -------------------------------------------------------------------------- public void killRobot(int robotIndex) { sendCommand(new KillRobotCommand(robotIndex)); } public void setPaintEnabled(int robotIndex, boolean enable) { sendCommand(new EnableRobotPaintCommand(robotIndex, enable)); } public void setSGPaintEnabled(int robotIndex, boolean enable) { sendCommand(new EnableRobotSGPaintCommand(robotIndex, enable)); } public 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); } } } } }