View Javadoc

1   package cz.cuni.amis.pogamut.defcon.agent.module.sensor;
2   
3   import java.security.InvalidParameterException;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.Hashtable;
7   import java.util.Iterator;
8   import java.util.LinkedList;
9   import java.util.List;
10  import java.util.SortedMap;
11  import java.util.TreeMap;
12  import java.util.concurrent.BlockingQueue;
13  import java.util.concurrent.LinkedBlockingQueue;
14  
15  import javabot.JBot;
16  import javabot.JBotMethodsRepository;
17  import javabot.PogamutJBotSupport;
18  import javabot.events.IDefConBasicEvent;
19  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
20  import cz.cuni.amis.pogamut.defcon.base3d.worldview.object.DefConLocation;
21  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.DebugIsReplayingGameChanged;
22  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.DefConChanged;
23  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.GameRunningChanged;
24  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.GameSpeedChanged;
25  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.VictoryTimerActiveChanged;
26  import cz.cuni.amis.pogamut.defcon.consts.GameSpeed;
27  import cz.cuni.amis.pogamut.defcon.consts.UnitType;
28  import cz.cuni.amis.pogamut.defcon.consts.state.IState;
29  import cz.cuni.amis.pogamut.defcon.utils.AdvancedFlagListener;
30  import cz.cuni.amis.pogamut.defcon.utils.SyncMethodExecContainer;
31  import cz.cuni.amis.pogamut.defcon.utils.SimpleFlag;
32  
33  /**
34   * This class implements functionality that is not present within standard worldview,
35   * but it is important since it provides crucial information about the games' state.
36   * 
37   * @author Radek 'Black_Hand' Pibil
38   *
39   */
40  public class GameInfo {
41  
42  	private final static int MAX_FLEET_SIZE = 6;
43  
44  	/**
45  	 * Contains reverse mapping from territory to territory owner.
46  	 */
47  	private final int[] territoryOwners = new int[JBot.NumTerritories];
48  	/**
49  	 * Holds fleet diameters.
50  	 */
51  	private final double[] fleetDiameters = new double[MAX_FLEET_SIZE];
52  
53  	/**
54  	 * Holds fleet member offsets.
55  	 */
56  	private final Location[][] fleetMemberOffsets = new Location[MAX_FLEET_SIZE][];
57  
58  	/**
59  	 * Keeps track of game time.
60  	 */
61  	private Float gameTime = 0f;
62  
63  	/**
64  	 * Holds the amount of ticks (updates) the AI has passed.
65  	 */
66  	private Integer gameTick = 0;
67  
68  	/**
69  	 * Time remaining in game after victory timer has been started.
70  	 */
71  	private Float victoryTimer = 0f;
72  
73  	/**
74  	 * Own team id.
75  	 */
76  	private int ownTeamId;
77  
78  	/**
79  	 * Enemy cities ids list.
80  	 */
81  	private final SortedMap<Integer, List<Integer>> enemiesCityIds = new TreeMap<Integer, List<Integer>>();
82  
83  	/**
84  	 * Own cities ids list.
85  	 */
86  	private final List<Integer> ownCityIds = new ArrayList<Integer>();
87  
88  	/**
89  	 * List of cities locations.
90  	 */
91  	private final SortedMap<Integer, Location> cityLocations = new TreeMap<Integer, Location>();
92  
93  	/**
94  	 * Enemy team ids list.
95  	 */
96  	private int[] enemyIds;
97  
98  	/**
99  	 * Holds current defcon.
100 	 */
101 	private final SimpleFlag<Integer> defcon = new SimpleFlag<Integer>(5);
102 	/**
103 	 * Adds defcon changed event into event queue, which then gets passed to the worldview.
104 	 */
105 	private final AdvancedFlagListener<Integer> defconListener = new AdvancedFlagListener<Integer>() {
106 
107 		@Override
108 		public void flagChanged(Integer oldValue, Integer changedValue) {
109 			events.add(new DefConChanged(oldValue, changedValue, getGameTime()));
110 		}
111 
112 	};
113 
114 	/**
115 	 * Holds current game speed.
116 	 */
117 	private final SimpleFlag<GameSpeed> gameSpeed = new SimpleFlag<GameSpeed>(
118 			GameSpeed.PAUSED);
119 	/**
120 	 * Adds game speed changed event into event queue, which then gets passed to the worldview.
121 	 */	
122 	private final AdvancedFlagListener<GameSpeed> gameSpeedListener = new AdvancedFlagListener<GameSpeed>() {
123 
124 		@Override
125 		public void flagChanged(GameSpeed oldValue, GameSpeed changedValue) {
126 			events.add(new GameSpeedChanged(oldValue, changedValue,
127 					getGameTime()));
128 		}
129 
130 	};
131 
132 	/**
133 	 * Holds current state of victory timer.
134 	 */
135 	private final SimpleFlag<Boolean> victoryTimerActive = new SimpleFlag<Boolean>(
136 			false);
137 	/**
138 	 * Adds victory timer changed event into event queue, which then gets passed to the worldview.
139 	 */
140 	private AdvancedFlagListener<Boolean> victoryTimerActiveListener = new AdvancedFlagListener<Boolean>() {
141 
142 		@Override
143 		public void flagChanged(Boolean oldValue, Boolean changedValue) {
144 			events.add(new VictoryTimerActiveChanged(changedValue,
145 					getGameTime()));
146 		}
147 
148 	};
149 
150 	/**
151 	 * Holds current state of debug replaying.
152 	 */	
153 	private final SimpleFlag<Boolean> debugIsReplayingGame = new SimpleFlag<Boolean>(false);
154 	/**
155 	 * Adds debug is replaying game changed event into event queue, which then gets passed to the worldview.
156 	 */	
157 	private final AdvancedFlagListener<Boolean> debugIsReplayingGameListener = new AdvancedFlagListener<Boolean>() {
158 
159 		@Override
160 		public void flagChanged(Boolean oldValue, Boolean changedValue) {
161 			events.add(new DebugIsReplayingGameChanged(changedValue,
162 					getGameTime()));
163 		}
164 
165 	};
166 
167 	/**
168 	 * Holds current state of debug replaying.
169 	 */	
170 	private final SimpleFlag<Boolean> gameRunning = new SimpleFlag<Boolean>(
171 			false);
172 	/**
173 	 * Adds victory timer changed event into event queue, which then gets passed to the worldview.
174 	 */	
175 	private AdvancedFlagListener<Boolean> gameRunningListener = new AdvancedFlagListener<Boolean>() {
176 
177 		@Override
178 		public void flagChanged(Boolean oldValue, Boolean changedValue) {
179 			if (changedValue) {
180 				cacheOwnTeamId();
181 				populateTerritoryOwners();
182 				populateCityLocations();
183 			}
184 			events.add(new GameRunningChanged(changedValue, getGameTime()));
185 		}
186 	};
187 
188 	/**
189 	 * Holds the queue of events to be passed to worldview.
190 	 */
191 	private final BlockingQueue<IDefConBasicEvent> events = new LinkedBlockingQueue<IDefConBasicEvent>();
192 
193 	/**
194 	 * Instantiates and prepares GameInfo for operation.
195 	 */
196 	public GameInfo() {		
197 		gameTime = JBot.GetGameTime();
198 		gameTick = JBot.GetGameTick();
199 		victoryTimer = 0f;
200 		gameRunning.addStrongListener(gameRunningListener);
201 		defcon.addStrongListener(defconListener);
202 		gameSpeed.addStrongListener(gameSpeedListener);
203 		victoryTimerActive.addStrongListener(victoryTimerActiveListener);
204 		debugIsReplayingGame.addStrongListener(debugIsReplayingGameListener);
205 		populateFleetMemberOffsets();
206 		populateFleetDiameters();
207 	}
208 
209 	/**
210 	 * Caches all cities locations.
211 	 */
212 	private void populateCityLocations() {
213 
214 
215 		int[] cities = getCityIds();
216 
217 		for (int cityId : cities) {
218 
219 			Location location = getObjectLocation(cityId);
220 
221 			int territoryId = getTerritoryId(location);
222 
223 			int teamId = territoryOwners[territoryId];
224 
225 			cityLocations.put(cityId, location);
226 
227 			if (teamId == -1)
228 				continue;
229 
230 			if (teamId == getOwnTeamId()) {
231 				ownCityIds.add(cityId);
232 			} else {
233 				enemiesCityIds.get(teamId).add(cityId);
234 			}
235 		}
236 	}
237 
238 	/**
239 	 * Caches all territory to owner mappings.
240 	 */
241 	private final void populateTerritoryOwners() {
242 
243 		for (int i = 0; i < territoryOwners.length; ++i) {
244 			territoryOwners[i] = -1;
245 		}
246 
247 		ArrayList<Integer> enemies = new ArrayList<Integer>();
248 		int own_id = getOwnTeamId();
249 
250 		for (int teamId : JBot.GetTeamIds()) {
251 
252 			if (teamId != own_id) {
253 				enemies.add(teamId);
254 				enemiesCityIds.put(teamId, new ArrayList<Integer>());
255 			}
256 
257 			for (int territoryId : getTeamTerritories(teamId)) {
258 				territoryOwners[territoryId] = teamId;
259 			}
260 		}
261 
262 		enemyIds = new int[enemies.size()];
263 
264 		int i = 0;
265 		for (int enemyId : enemies) {
266 			enemyIds[i++] = enemyId;
267 		}
268 	}
269 
270 	/**
271 	 * Caches fleet members offsets.
272 	 */
273 	private final void populateFleetMemberOffsets() {
274 		for (int i = 0; i < MAX_FLEET_SIZE; ++i) {
275 			fleetMemberOffsets[i] = new Location[i + 1];
276 			for (int j = 0; j <= i; ++j) {
277 				fleetMemberOffsets[i][j] = getFleetMemberOffsetWorker(i + 1, j);
278 			}
279 		}
280 	}
281 
282 	/**
283 	 * Caches fleet diameters per fleet size.
284 	 */
285 	private final void populateFleetDiameters() {
286 		for (int i = 0; i < MAX_FLEET_SIZE; ++i) {
287 			fleetDiameters[i] = getFleetDiameterWorker(i + 1);
288 		}
289 	}
290 
291 	/**
292 	 * Caches own Id.
293 	 */
294 	private void cacheOwnTeamId() {
295 		ownTeamId = JBot.GetOwnTeamId();
296 	}
297 
298 	/**
299 	 * Updates all flags, which in turn generates events, which are then returned in a form
300 	 * of LinkedList<IDefconBasicEvent>.
301 	 * This method is periodically called by DefconMessageProdducer.populateQueue().
302 	 * @return LinkedList of acquired events.
303 	 */
304 	public LinkedList<IDefConBasicEvent> getEvents() {
305 		gameTime = JBot.GetGameTime();
306 		gameTick = JBot.GetGameTick();
307 
308 		victoryTimerActive.setFlag(JBot.IsVictoryTimerActive());
309 
310 		if (victoryTimerActive.getFlag()) {
311 			victoryTimer = JBot.GetVictoryTimer();
312 		}
313 
314 		gameRunning.setFlag(true);
315 		defcon.setFlag(JBot.GetDefcon());
316 		gameSpeed.setFlag(GameSpeed.getEnum(JBot.GetGameSpeed()));
317 		victoryTimerActive.setFlag(JBot.IsVictoryTimerActive());
318 		debugIsReplayingGame.setFlag(JBot.DebugIsReplayingGame());
319 
320 		LinkedList<IDefConBasicEvent> output = new LinkedList<IDefConBasicEvent>();
321 		events.drainTo(output);
322 		return output;
323 	}
324 	
325 	private final LinkedList<Location> aiPlacementPoints = new LinkedList<Location>();
326 	private final LinkedList<Location> targetCoords = new LinkedList<Location>();
327 	
328 	/**
329 	 * Initializes default aiPlacementPoints.
330 	 * Borrowed from the reimplementation of the original Defcon bot.
331 	 */
332 	private void populatePoints()
333 	{
334 		// This data was originally stored in bitmaps and then read out when the game starts.
335 		// However, the bitmap loading procedures are integrated deep into the game engine, so this has
336 		// been left away.
337 		checkedAddToAIPlacementPoints(new Location(-177.890625f,34.736842f));
338 		checkedAddToAIPlacementPoints(new Location(-177.187500f,46.666667f));
339 		checkedAddToAIPlacementPoints(new Location(-156.093750f,45.964912f));
340 		checkedAddToAIPlacementPoints(new Location(-151.875000f,35.438596f));
341 		checkedAddToAIPlacementPoints(new Location(-146.953125f,22.807018f));
342 		targetCoords.add(new Location(-142.031250f,45.964912f));
343 		targetCoords.add(new Location(-136.406250f,28.421053f));
344 		checkedAddToAIPlacementPoints(new Location(-133.593750f,20.701754f));
345 		checkedAddToAIPlacementPoints(new Location(-123.046875f,26.315789f));
346 		targetCoords.add(new Location(-118.828125f,9.473684f));
347 		checkedAddToAIPlacementPoints(new Location(-113.203125f,-9.473684f));
348 		checkedAddToAIPlacementPoints(new Location(-106.171875f,6.666667f));
349 		checkedAddToAIPlacementPoints(new Location(-93.515625f,-6.666667f));
350 		targetCoords.add(new Location(-93.515625f,-0.350877f));
351 		checkedAddToAIPlacementPoints(new Location(-91.406250f,-18.596491f));
352 		targetCoords.add(new Location(-88.593750f,-27.719298f));
353 		targetCoords.add(new Location(-64.687500f,29.122807f));
354 		checkedAddToAIPlacementPoints(new Location(-56.250000f,22.807018f));
355 		checkedAddToAIPlacementPoints(new Location(-55.546875f,35.438596f));
356 		targetCoords.add(new Location(-50.625000f,16.491228f));
357 		targetCoords.add(new Location(-47.812500f,36.842105f));
358 		checkedAddToAIPlacementPoints(new Location(-44.296875f,31.228070f));
359 		targetCoords.add(new Location(-43.593750f,48.771930f));
360 		checkedAddToAIPlacementPoints(new Location(-42.890625f,41.754386f));
361 		checkedAddToAIPlacementPoints(new Location(-40.078125f,4.561404f));
362 		checkedAddToAIPlacementPoints(new Location(-37.968750f,22.105263f));
363 		checkedAddToAIPlacementPoints(new Location(-37.265625f,-30.526316f));
364 		targetCoords.add(new Location(-31.640625f,-17.894737f));
365 		checkedAddToAIPlacementPoints(new Location(-31.640625f,35.438596f));
366 		targetCoords.add(new Location(-30.234375f,-35.438596f));
367 		checkedAddToAIPlacementPoints(new Location(-29.531250f,45.263158f));
368 		checkedAddToAIPlacementPoints(new Location(-28.828125f,9.473684f));
369 		checkedAddToAIPlacementPoints(new Location(-28.125000f,24.912281f));
370 		checkedAddToAIPlacementPoints(new Location(-26.718750f,0.350877f));
371 		checkedAddToAIPlacementPoints(new Location(-25.312500f,-17.894737f));
372 		targetCoords.add(new Location(-23.203125f,13.684211f));
373 		targetCoords.add(new Location(-22.500000f,31.228070f));
374 		checkedAddToAIPlacementPoints(new Location(-21.796875f,38.947368f));
375 		checkedAddToAIPlacementPoints(new Location(-21.796875f,52.280702f));
376 		targetCoords.add(new Location(-21.093750f,45.263158f));
377 		checkedAddToAIPlacementPoints(new Location(-14.765625f,35.438596f));
378 		checkedAddToAIPlacementPoints(new Location(-14.062500f,47.368421f));
379 		checkedAddToAIPlacementPoints(new Location(-11.953125f,-5.964912f));
380 		checkedAddToAIPlacementPoints(new Location(-10.546875f,60.000000f));
381 		targetCoords.add(new Location(-9.140625f,68.421053f));
382 		targetCoords.add(new Location(0.000000f,69.122807f));
383 		targetCoords.add(new Location(1.406250f,-4.561404f));
384 		checkedAddToAIPlacementPoints(new Location(2.812500f,73.333333f));
385 		checkedAddToAIPlacementPoints(new Location(3.515625f,-19.298246f));
386 		checkedAddToAIPlacementPoints(new Location(4.218750f,67.017544f));
387 		targetCoords.add(new Location(6.328125f,-31.929825f));
388 		checkedAddToAIPlacementPoints(new Location(12.656250f,71.929825f));
389 		checkedAddToAIPlacementPoints(new Location(28.828125f,76.140351f));
390 		targetCoords.add(new Location(35.859375f,75.438596f));
391 		checkedAddToAIPlacementPoints(new Location(38.671875f,74.736842f));
392 		checkedAddToAIPlacementPoints(new Location(48.515625f,75.438596f));
393 		targetCoords.add(new Location(49.218750f,-5.263158f));
394 		checkedAddToAIPlacementPoints(new Location(56.953125f,-10.175439f));
395 		checkedAddToAIPlacementPoints(new Location(58.359375f,1.754386f));
396 		targetCoords.add(new Location(64.687500f,8.771930f));
397 		targetCoords.add(new Location(70.312500f,-6.666667f));
398 		checkedAddToAIPlacementPoints(new Location(71.015625f,3.859649f));
399 		checkedAddToAIPlacementPoints(new Location(82.968750f,-3.859649f));
400 		targetCoords.add(new Location(90.000000f,-2.456140f));
401 		checkedAddToAIPlacementPoints(new Location(94.921875f,-12.280702f));
402 		targetCoords.add(new Location(132.890625f,21.403509f));
403 		targetCoords.add(new Location(134.296875f,10.175439f));
404 		checkedAddToAIPlacementPoints(new Location(144.843750f,27.017544f));
405 		checkedAddToAIPlacementPoints(new Location(149.062500f,15.087719f));
406 		targetCoords.add(new Location(150.468750f,31.228070f));
407 		checkedAddToAIPlacementPoints(new Location(154.687500f,34.035088f));
408 		checkedAddToAIPlacementPoints(new Location(157.500000f,21.403509f));
409 		targetCoords.add(new Location(158.906250f,41.052632f));
410 		checkedAddToAIPlacementPoints(new Location(163.828125f,46.666667f));
411 		checkedAddToAIPlacementPoints(new Location(165.234375f,8.771930f));
412 		checkedAddToAIPlacementPoints(new Location(167.343750f,32.631579f));
413 		checkedAddToAIPlacementPoints(new Location(172.265625f,20.701754f));
414 		checkedAddToAIPlacementPoints(new Location(172.968750f,45.964912f));
415 	}
416 	
417 	private final void checkedAddToAIPlacementPoints(Location location) {
418 		if (JBot.IsValidTerritory(
419 				JBot.GetOwnTeamId(),
420 				(float) location.getX(),
421 				(float) location.getY(),
422 				true) ||
423 				JBot.IsValidTerritory(
424 						JBot.GetOwnTeamId(),
425 						(float) location.getX(),
426 						(float) location.getY(),
427 						true))
428 			aiPlacementPoints.add(location);
429 	}
430 
431 	/**
432 	 * Returns current set of AI placement points.
433 	 * @return LinkedList of Locations
434 	 */
435 	public final LinkedList<Location> getAIPlacementPoints() {
436 		return aiPlacementPoints;
437 	}
438 
439 	/**
440 	 * Returns current set of AI target points.
441 	 * @return LinkedList of Locations
442 	 */	
443 	public final LinkedList<Location> getTargetCoords() {
444 		return targetCoords;
445 	}
446 
447 	/**
448 	 * Current Defcon Stage, game starts with 5
449 	 * 
450 	 * @return int Defcon stage
451 	 */
452 	public int getDefconLevel() {
453 		return defcon.getFlag();
454 	}
455 
456 	/**
457 	 * Current Game Time, measured in seconds. Each tick, the game progresses by
458 	 * 0.1 sec * GameSpeed
459 	 * 
460 	 * @return game time
461 	 */
462 	public float getGameTime() {
463 		return gameTime;
464 	}
465 
466 	/**
467 	 * Amount of update cycles (ticks) passed since game start
468 	 * 
469 	 * @return number of game ticks
470 	 */
471 	public int getGameTick() {
472 		return gameTick;
473 	}
474 
475 	/**
476 	 * Current speed-up factor of the game over the real time passed. Usually
477 	 * has values from 0 (paused), 1 (real time), 5, 10, 20, see enum GAMESPEED
478 	 * (GameSpeed class)
479 	 * 
480 	 * @return
481 	 */
482 	public GameSpeed getGameSpeed() {
483 		return gameSpeed.getFlag();
484 	}
485 
486 	/**
487 	 * Time remaining in game, if victory timer was started. Test this with
488 	 * IsVictoryTimerStarted
489 	 * 
490 	 * @return
491 	 */
492 	public float getVictoryTimer() {
493 		return victoryTimer;
494 	}
495 
496 	/**
497 	 * True iff the victory-timer has been started
498 	 * 
499 	 * @return
500 	 */
501 	public boolean isVictoryTimerActive() {
502 		return victoryTimerActive.getFlag();
503 	}
504 
505 	/**
506 	 * Value of certain option.
507 	 * <p><p>
508 	 * Black_Hand: no idea what it does, might be related to the game settings
509 	 * 	(advanced options in game hosting) like CityPopulations. Expected
510 	 * to be called like: getOptionValue("CityPopulations").
511 	 * 
512 	 * @param name
513 	 * @return
514 	 */
515 	public int getOptionValue(String name) {
516 		SyncMethodExecContainer container = new SyncMethodExecContainer(
517 				JBotMethodsRepository.GetOptionValue(), new Object[] { name });
518 		return (Integer) container.syncCallInMainThread();
519 	}
520 
521 	/**
522 	 * True if the game is currently replayed (Timeline has been clicked).
523 	 * TODO: e.g. game has been paused?
524 	 * 
525 	 * @return
526 	 */
527 	public boolean debugIsReplayingGame() {
528 		return debugIsReplayingGame.getFlag();
529 	}
530 
531 	/**
532 	 * Team Id of given unit.
533 	 * 
534 	 * @param id
535 	 * @return
536 	 */
537 	public int getTeamId(int id) {
538 		SyncMethodExecContainer container = new SyncMethodExecContainer(
539 				JBotMethodsRepository.GetTeamId(), new Object[] { id });
540 		return (Integer) container.syncCallInMainThread();
541 	}
542 
543 	/**
544 	 * Own fleet ids.
545 	 * 
546 	 * @return
547 	 */
548 	public int[] getOwnFleets() {
549 		SyncMethodExecContainer container = new SyncMethodExecContainer(
550 				JBotMethodsRepository.GetOwnFleets(), null);
551 		return (int[]) container.syncCallInMainThread();
552 	}
553 
554 	/**
555 	 * Fleet ids of given team. Only fleets ids with visible members are
556 	 * returned.
557 	 * 
558 	 * @param teamId
559 	 * @return
560 	 */
561 	public int[] getFleets(int teamId) {
562 		SyncMethodExecContainer container = new SyncMethodExecContainer(
563 				JBotMethodsRepository.GetFleets(), new Object[] { teamId });
564 		return (int[]) container.syncCallInMainThread();
565 	}
566 
567 	/**
568 	 * Ids of ships in given fleet.
569 	 * 
570 	 * @param fleetId
571 	 * @return
572 	 */
573 	public int[] getFleetMembers(int fleetId) {
574 		SyncMethodExecContainer container = new SyncMethodExecContainer(
575 				JBotMethodsRepository.GetFleetMembers(),
576 				new Object[] { fleetId });
577 		return (int[]) container.syncCallInMainThread();
578 	}
579 
580 	/**
581 	 * Id of fleet of given unit.
582 	 * 
583 	 * @param unitId
584 	 * @return
585 	 */
586 	public int getFleetId(int unitId) {
587 		SyncMethodExecContainer container = new SyncMethodExecContainer(
588 				JBotMethodsRepository.GetFleetId(), new Object[] { unitId });
589 		return (Integer) container.syncCallInMainThread();
590 	}
591 
592 	/**
593 	 * Time until current state is active.
594 	 * 
595 	 * @param unitId
596 	 * @return
597 	 */
598 	public float getStateTimer(int unitId) {
599 		SyncMethodExecContainer container = new SyncMethodExecContainer(
600 				JBotMethodsRepository.GetStateTimer(),
601 				new Object[] { unitId });
602 		return (Float) container.syncCallInMainThread();
603 	}
604 
605 	/**
606 	 * Array of unitIds of currently queued actions, for example nukes in a silo
607 	 * or planes on a carrier
608 	 * 
609 	 * @param unitId
610 	 * @return
611 	 */
612 	public int[] getActionQueue(int unitId) {
613 		SyncMethodExecContainer container = new SyncMethodExecContainer(
614 				JBotMethodsRepository.GetActionQueue(),
615 				new Object[] { unitId });
616 		return (int[]) container.syncCallInMainThread();
617 	}
618 
619 	/**
620 	 * True iff given unit is automatically retaliating an earlier attack.
621 	 * 
622 	 * @param unitId
623 	 * @return
624 	 */
625 	public boolean isRetaliating(int unitId) {
626 		SyncMethodExecContainer container = new SyncMethodExecContainer(
627 				JBotMethodsRepository.IsRetaliating(),
628 				new Object[] { unitId });
629 		return (Boolean) container.syncCallInMainThread();
630 	}
631 
632 	/**
633 	 * True iff given unit is visible to given team. In full information mode,
634 	 * visibility information about other teams is available. In limited
635 	 * information mode, only visible units are accessible.
636 	 * 
637 	 * @param unitId
638 	 * @param byTeamId
639 	 * @return
640 	 */
641 	public boolean isVisible(int unitId, int byTeamId) {
642 		SyncMethodExecContainer container = new SyncMethodExecContainer(
643 				JBotMethodsRepository.IsVisible(), new Object[] { unitId,
644 						byTeamId });
645 		return (Boolean) container.syncCallInMainThread();
646 	}
647 
648 	/**
649 	 * Movement direction of given unit, in longitude and latitude parts. The
650 	 * vector has the length of the unit speed (see also SPEED_*).
651 	 * 
652 	 * @param unitId
653 	 * @return
654 	 */
655 	public Location getVelocity(int unitId) {
656 		SyncMethodExecContainer container = new SyncMethodExecContainer(
657 				JBotMethodsRepository.GetVelocity(), new Object[] { unitId });
658 		return new Location((float[]) container.syncCallInMainThread());
659 	}
660 
661 	/**
662 	 * Remaining range of unit. If unlimited, -1 is returned.
663 	 * 
664 	 * @param unitId
665 	 * @return
666 	 */
667 	public float getRange(int unitId) {
668 		SyncMethodExecContainer container = new SyncMethodExecContainer(
669 				JBotMethodsRepository.GetRange(), new Object[] { unitId });
670 		return (Float) container.syncCallInMainThread();
671 	}
672 
673 	/**
674 	 * Amount of remaining units of given type that can be placed.
675 	 * 
676 	 * @param typeId
677 	 * @return
678 	 */
679 	public int getRemainingUnits(UnitType typeId) {
680 		SyncMethodExecContainer container = new SyncMethodExecContainer(
681 				JBotMethodsRepository.GetRemainingUnits(),
682 				new Object[] { typeId.id });
683 		return (Integer) container.syncCallInMainThread();
684 	}
685 
686 	/**
687 	 * True iff given location is valid for placement of given type. For fleets
688 	 * use getFleetMemberOffset to get offset from fleet center.
689 	 * 
690 	 * @param longitude
691 	 * @param latitude
692 	 * @param typeId
693 	 * @return
694 	 */
695 	public boolean isValidPlacementLocation(float longitude, float latitude,
696 			UnitType typeId) {
697 
698 		SyncMethodExecContainer container = new SyncMethodExecContainer(
699 				JBotMethodsRepository.IsValidPlacementLocation(),
700 				new Object[] { longitude, latitude, typeId.id });
701 
702 		Object return_value = container.syncCallInMainThread();
703 
704 		return (Boolean) return_value;
705 	}
706 	
707 	/**
708 	 * True iff given location is valid for placement of given type. For fleets
709 	 * use getFleetMemberOffset to get offset from fleet center.
710 	 * 
711 	 * @param longitude
712 	 * @param latitude
713 	 * @param typeId
714 	 * @return
715 	 */
716 	public boolean isValidPlacementLocation(double longitude, double latitude,
717 			UnitType typeId) {
718 		return isValidPlacementLocation(
719 				(float) longitude,
720 				(float) latitude,
721 				typeId);
722 	}
723 
724 	/**
725 	 * True iff given location is valid for ship placement. For fleets use
726 	 * getFleetMemberOffset to get offset from fleet center.
727 	 * 
728 	 * @param longitude
729 	 * @param latitude
730 	 * @return
731 	 */
732 	public boolean isValidShipPlacementLocation(Location location) {
733 		return isValidPlacementLocation(
734 				location.getX(),
735 				location.getY(),
736 				UnitType.BATTLE_SHIP);
737 	}
738 
739 	/**
740 	 * True iff given location is valid for ship placement. For fleets use
741 	 * getFleetMemberOffset to get offset from fleet center.
742 	 * 
743 	 * @param longitude
744 	 * @param latitude
745 	 * @return
746 	 */
747 	public boolean isValidShipPlacementLocation(double longitude,
748 			double latitude) {
749 		return isValidPlacementLocation(
750 				longitude,
751 				latitude,
752 				UnitType.BATTLE_SHIP);
753 	}
754 
755 	/**
756 	 * True iff given location is valid for building placement. For fleets use
757 	 * getFleetMemberOffset to get offset from fleet center.
758 	 * 
759 	 * @param location
760 	 * @return
761 	 */
762 	public boolean isValidBuildingPlacementLocation(Location location) {
763 		return isValidBuildingPlacementLocation(
764 				location.getX(),
765 				location.getY());
766 	}
767 
768 
769 	/**
770 	 * True iff given location is valid for building placement. For fleets use
771 	 * getFleetMemberOffset to get offset from fleet center.
772 	 * 
773 	 * @param longitude
774 	 * @param latitude
775 	 * @return
776 	 */
777 	public boolean isValidBuildingPlacementLocation(double longitude,
778 			double latitude) {
779 		return isValidPlacementLocation(
780 				longitude,
781 				latitude,
782 				UnitType.RADAR);
783 	}
784 	
785 	public boolean isValidFleetPlacementLocation(float longitude, float latitude,
786 			int fleetSize) {
787 		for( int i = 0; i < fleetSize; ++i )
788 		{
789 			Location offset = getFleetMemberOffset(fleetSize, i);
790 
791 			// the placement validity is equal for battleships, carriers and
792 			// subs.
793 			if (!isValidShipPlacementLocation(longitude + offset.getX(),
794 					latitude + offset.getY()))
795 				return false;
796 		}
797 		return true;
798 	}
799 
800 	/**
801 	 * Credits available for placement (if in variable unit mode).
802 	 * 
803 	 * @return
804 	 */
805 	public int getUnitCredits() {
806 		SyncMethodExecContainer container = new SyncMethodExecContainer(
807 				JBotMethodsRepository.GetUnitCredits(), null);
808 		Object return_value = container.syncCallInMainThread();
809 
810 		return (Integer) return_value;
811 	}
812 
813 	/**
814 	 * Value of given unit type (if in variable unit mode).
815 	 * 
816 	 * @param typeId
817 	 * @return
818 	 */
819 	public int getUnitValue(UnitType typeId) {
820 		SyncMethodExecContainer container = new SyncMethodExecContainer(
821 				JBotMethodsRepository.GetUnitValue(),
822 				new Object[] { typeId.id });
823 		return (Integer) container.syncCallInMainThread();
824 	}
825 
826 	/**
827 	 * Distance in game between given coordinates on sea (performs pathfinding).
828 	 * 
829 	 * @param locationA
830 	 * @param locationB
831 	 * @return distance
832 	 */
833 	public float getSailDistance(Location locationA, Location locationB) {
834 
835 		if (locationA == null)
836 			throw new IllegalArgumentException(
837 					"GetSailDistance requires non-null arguments. [locationA]");
838 		if (locationB == null)
839 			throw new IllegalArgumentException(
840 					"GetSailDistance requires non-null arguments. [locationB]");
841 
842 		return getSailDistance(locationA.getX(),
843 				locationA.getY(), locationB.getX(),
844 				locationB.getY());
845 	}
846 
847 	/**
848 	 * Distance in game between given coordinates on sea (performs pathfinding).
849 	 * 
850 	 * @param locationA
851 	 * @param locationB
852 	 * @return distance
853 	 */
854 	public float getSailDistance(double locationA_x, double locationA_y,
855 			double locationB_x, double locationB_y) {
856 		return getSailDistance(((float) locationA_x),
857 				(float) locationA_y, (float) locationB_x,
858 				(float) locationB_y);
859 	}
860 
861 	/**
862 	 * Distance in game between given coordinates on sea (performs pathfinding).
863 	 * 
864 	 * @param locationA_x
865 	 * @param locationB_x
866 	 * @return distance
867 	 */
868 	public float getSailDistance(float locationA_x, float locationA_y,
869 			float locationB_x, float locationB_y) {
870 		SyncMethodExecContainer container = new SyncMethodExecContainer(
871 				JBotMethodsRepository.GetSailDistance(),
872 				new Object[] { locationA_x, locationA_y, locationB_x,
873 						locationB_y });
874 
875 		Object return_value = container.syncCallInMainThread();
876 
877 		return (return_value instanceof Float) ? (Float) return_value : null;
878 	}
879 
880 
881 	private final static class LocationWithDistance {
882 				
883 		private double distance;
884 		private Location location;
885 		
886 		public LocationWithDistance(double distance, Location location) {
887 			this.distance = distance;
888 			this.location = location;
889 		}		
890 		
891 		public final double getDistance() {
892 			return distance;
893 		}
894 		
895 		public final void setDistance(double distance) {
896 			this.distance = distance;
897 		}
898 		
899 		public final Location getLocation() {
900 			return location;
901 		}
902 		
903 		public final void setLocation(Location location) {
904 			this.location = location;
905 		}
906 	}
907 	
908 	private final float MIN_STEP = 5f;
909 	
910 	/**
911 	 * Finds a closest point on given border to a given point. VERY ROUGH!!!
912 	 * That is because it uses getSailDistance and NOT the euclidean distance.
913 	 * 
914 	 * @param location
915 	 * @param border
916 	 * @return closest point
917 	 */
918 	public LocationWithDistance getClosestSailTarget(Location location,
919 			List<Location> border) {
920 		if (border == null || border.size() == 0)
921 			return null;
922 
923 		if (border.size() == 1)
924 			return new LocationWithDistance(getSailDistance(location, border.get(0)), border.get(0));
925 
926 		Iterator<Location> front = border.iterator();
927 		Location last = null;
928 		Location current = front.next();
929 		Location best = null;
930 		Location tmp = null;
931 		Location tmp2 = null;
932 		Location tmp3 = null;
933 		double tmp_distance = 0, tmp_distance2 = 0, tmp_distance3 = 0;
934 		double min_distance = Double.MAX_VALUE;
935 
936 		while (front.hasNext()) {
937 			last = current;
938 			current = front.next();
939 
940 			tmp = last;
941 			tmp2 = current;
942 
943 			while (tmp.getDistance2D(tmp2) > MIN_STEP) {
944 				tmp3 = tmp2.sub(tmp).scale(0.5f).add(tmp);
945 
946 				tmp_distance = getSailDistance(location, tmp);
947 				tmp_distance2 = getSailDistance(location, tmp2);
948 				tmp_distance3 = getSailDistance(location, tmp3);
949 
950 				if (tmp_distance2 < tmp_distance) {
951 					tmp = tmp2;
952 					tmp_distance = tmp_distance2;
953 				}
954 
955 				if (tmp_distance3 > tmp_distance) {
956 					break;
957 				}
958 
959 				tmp2 = tmp3;
960 				tmp_distance2 = tmp_distance3;
961 			}
962 
963 			if (tmp_distance2 < tmp_distance) {
964 				tmp_distance = tmp_distance2;
965 				tmp = tmp2;
966 			}
967 
968 			if (tmp_distance < min_distance) {
969 				min_distance = tmp_distance;
970 				best = tmp;
971 			}
972 		}
973 
974 		return new LocationWithDistance(min_distance, best);
975 	}
976 
977 	/**
978 	 * Finds a closest point on given border to a given point. VERY ROUGH!!!
979 	 * That is because it uses getSailDistance and NOT the euclidean distance.
980 	 * 
981 	 * @param borderA
982 	 * @param borderB
983 	 * @return two closest points
984 	 */
985 	public LocationPair getClosestSailDistance(List<Location> borderA,
986 			List<Location> borderB) {
987 
988 		double min_distance = Double.MAX_VALUE;
989 		LocationPair best = null;
990 
991 		if (borderA == null || borderA.size() == 0)
992 			return null;
993 
994 		Iterator<Location> front = borderA.iterator();
995 		Location current = front.next();
996 		Location last = null;
997 		LocationPair borderA_borderB = new LocationPair(current, borderB.get(0));
998 		LocationPair borderA_borderB_2 = new LocationPair(current,
999 				borderB.get(0));
1000 		LocationPair borderA_borderB_candidate = null;
1001 		double tmp_distance = 0, tmp_distance2 = 0, tmp_distance3 = 0;
1002 
1003 		while (front.hasNext()) {
1004 			last = current;
1005 			current = front.next();						
1006 			
1007 			borderA_borderB.A = last;
1008 			borderA_borderB_2.A = current;
1009 			
1010 			LocationWithDistance loc_with_dist;
1011 			
1012 			loc_with_dist = getClosestSailTarget(borderA_borderB.A, borderB);;
1013 			tmp_distance = loc_with_dist.getDistance();
1014 			borderA_borderB.B = loc_with_dist.getLocation();
1015 			
1016 			
1017 			loc_with_dist = getClosestSailTarget(borderA_borderB_2.A, borderB);			
1018 			tmp_distance2 = loc_with_dist.getDistance();
1019 			borderA_borderB_2.B = loc_with_dist.getLocation();
1020 
1021 			while (borderA_borderB.A.getDistance2D(borderA_borderB_2.A) > MIN_STEP) {
1022 				// tmp2.A.sub(tmp.A).scale(0.5f).add(tmp.A) === half way between tmp and tmp2
1023 				Location half_way = borderA_borderB_2.A.sub(borderA_borderB.A).scale(0.5f).add(borderA_borderB.A);
1024 				loc_with_dist = getClosestSailTarget(half_way, borderB);
1025 				borderA_borderB_candidate = new LocationPair(half_way,
1026 						loc_with_dist.getLocation());
1027 
1028 				tmp_distance3 = loc_with_dist.getDistance();
1029 
1030 				if (tmp_distance2 < tmp_distance) {
1031 					borderA_borderB = borderA_borderB_2;
1032 					tmp_distance = tmp_distance2;
1033 				}
1034 
1035 				if (tmp_distance3 > tmp_distance) {
1036 					break;
1037 				}
1038 
1039 				borderA_borderB_2 = borderA_borderB_candidate;
1040 				tmp_distance2 = tmp_distance3;
1041 			}
1042 
1043 			if (tmp_distance2 < tmp_distance) {
1044 				tmp_distance = tmp_distance2;
1045 				borderA_borderB = borderA_borderB_2;
1046 			}
1047 
1048 			if (tmp_distance < min_distance) {
1049 				min_distance = tmp_distance;
1050 				best = borderA_borderB;
1051 			}
1052 		}
1053 		System.gc();
1054 		return best;
1055 	}
1056 
1057 	public class LocationPair {
1058 		public Location A;
1059 		public Location B;
1060 
1061 		public LocationPair(Location A, Location B) {
1062 			this.A = A;
1063 			this.B = B;
1064 		}
1065 	}
1066 
1067 	/**
1068 	 * True if the given coordinates belong to the given Team. If seaArea is set
1069 	 * to true, then Coordinates must be on sea area, otherwise land. If teamId
1070 	 * = -1, then function returns if coordinates are land or sea terrain
1071 	 * respectively. Note that there can be coordinates which are neither land
1072 	 * nor sea
1073 	 * 
1074 	 * @param teamId
1075 	 * @param longitude
1076 	 * @param latitude
1077 	 * @param seaArea
1078 	 * @return
1079 	 */
1080 	public boolean isValidTerritory(int teamId, float longitude,
1081 			float latitude, boolean seaArea) {
1082 
1083 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1084 				JBotMethodsRepository.IsValidTerritory(),
1085 				new Object[] { teamId, longitude, latitude, seaArea });
1086 		return (Boolean) container.syncCallInMainThread();
1087 	}
1088 
1089 	/**
1090 	 * True if the given coordinates belong to the given Team. If seaArea is set
1091 	 * to true, then Coordinates must be on sea area, otherwise land. If teamId
1092 	 * = -1, then function returns if coordinates are land or sea terrain
1093 	 * respectively. Note that there can be coordinates which are neither land
1094 	 * nor sea
1095 	 * 
1096 	 * @param teamId
1097 	 * @param longitude
1098 	 * @param latitude
1099 	 * @param seaArea
1100 	 * @return
1101 	 */
1102 	public boolean isValidTerritory(int teamId, double longitude,
1103 			double latitude, boolean seaArea) {
1104 		return isValidTerritory(teamId, (float) longitude, (float) latitude,
1105 				seaArea);
1106 	}
1107 
1108 	/**
1109 	 * True if the given coordinates belong to the given Team. If seaArea is set
1110 	 * to true, then Coordinates must be on sea area, otherwise land. If teamId
1111 	 * = -1, then function returns if coordinates are land or sea terrain
1112 	 * respectively. Note that there can be coordinates which are neither land
1113 	 * nor sea
1114 	 * 
1115 	 * @param teamId
1116 	 * @param location
1117 	 * @param seaArea
1118 	 * @return
1119 	 */
1120 	public boolean isValidTerritory(int teamId, Location location,
1121 			boolean seaArea) {
1122 		return isValidTerritory(teamId, (float) location.getX(),
1123 				(float) location.getY(), seaArea);
1124 	}
1125 
1126 	/**
1127 	 * True if given coordinates are on the border. Compare
1128 	 * "data/earth/coastlines.bmp".
1129 	 * 
1130 	 * @param longitude
1131 	 * @param latitude
1132 	 * @return
1133 	 */
1134 	public boolean isBorder(float longitude, float latitude) {
1135 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1136 				JBotMethodsRepository.IsBorder(),
1137 				new Object[] { longitude, latitude });
1138 		return (Boolean) container.syncCallInMainThread();
1139 	}
1140 
1141 	/**
1142 	 * Territory Id of territory at given coordinates.
1143 	 * 
1144 	 * @param longitude
1145 	 * @param latitude
1146 	 * @return
1147 	 */
1148 	public int getTerritoryId(float longitude, float latitude) {
1149 
1150 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1151 				JBotMethodsRepository.GetTerritoryId(),
1152 				new Object[] { longitude, latitude });
1153 		return (Integer) container.syncCallInMainThread();
1154 	}
1155 
1156 	/**
1157 	 * Territory Id of territory at given coordinates.
1158 	 * 
1159 	 * @param longitude
1160 	 * @param latitude
1161 	 * @return
1162 	 */
1163 	public int getTerritoryId(double longitude, double latitude) {
1164 		return getTerritoryId((float) longitude, (float) latitude);
1165 	}
1166 
1167 	/**
1168 	 * Own team id.
1169 	 * 
1170 	 * @return
1171 	 */
1172 	public int getOwnTeamId() {
1173 		return ownTeamId;
1174 	}
1175 
1176 	/**
1177 	 * List of Team Ids in the game.
1178 	 * 
1179 	 * @return
1180 	 */
1181 	public int[] getTeamIds() {
1182 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1183 				JBotMethodsRepository.GetTeamIds(),
1184 				null);
1185 		return (int[]) container.syncCallInMainThread();
1186 	}
1187 
1188 	/**
1189 	 * List of Alliances Team Ids in the game.
1190 	 * 
1191 	 * @return List of enemy alliances and containing enemies WARNING: this
1192 	 *         SortedMap changes when someone leaves/enters alliance
1193 	 */
1194 	public SortedMap<Integer, LinkedList<Integer>> getAlliancesWithTeamIds() {
1195 		int[] teams = getTeamIds();
1196 
1197 		SortedMap<Integer, LinkedList<Integer>> alliances = new TreeMap<Integer, LinkedList<Integer>>();
1198 		for (int i : teams) {
1199 			int alliance = getAllianceId(i);
1200 			LinkedList<Integer> list = alliances.get(alliance);
1201 			if (list == null)
1202 				list = new LinkedList<Integer>();
1203 
1204 			list.add(i);
1205 			alliances.put(alliance, list);
1206 		}
1207 		return alliances;
1208 	}
1209 
1210 	/**
1211 	 * List of Enemy Team Ids in the game.
1212 	 * 
1213 	 * @return List of enemy alliances and containing enemies WARNING: this
1214 	 *         SortedMap changes when someone leaves/enters alliance
1215 	 */
1216 	public int[] getEnemyTeamIds() {
1217 		return enemyIds;
1218 	}
1219 
1220 	/**
1221 	 * List of Enemies city Ids in the game.
1222 	 * 
1223 	 * @return List of enemy alliances and containing enemies WARNING: this
1224 	 *         SortedMap changes when someone leaves/enters alliance
1225 	 */
1226 	public SortedMap<Integer, List<Integer>> getEnemiesCityIds() {
1227 		return Collections.unmodifiableSortedMap(enemiesCityIds);
1228 	}
1229 
1230 	/**
1231 	 * List of Enemies city Ids in the game.
1232 	 * 
1233 	 * @return List of enemy alliances and containing enemies WARNING: this
1234 	 *         SortedMap changes when someone leaves/enters alliance
1235 	 */
1236 	public List<Integer> getEnemyCityIds() {
1237 		ArrayList<Integer> enemyCityIds = new ArrayList<Integer>(20);
1238 
1239 		for (List<Integer> ids : enemiesCityIds.values()) {
1240 			for (int id : ids) {
1241 				enemyCityIds.add(id);
1242 			}
1243 		}
1244 		return enemyCityIds;
1245 	}
1246 
1247 	/**
1248 	 * List of Enemy city Ids in the game.
1249 	 * 
1250 	 * @return List of enemy alliances and containing enemies WARNING: this
1251 	 *         SortedMap changes when someone leaves/enters alliance
1252 	 */
1253 	public List<Integer> getEnemyCityIds(int enemyId) {
1254 		return Collections.unmodifiableList(enemiesCityIds.get(enemyId));
1255 	}
1256 
1257 	/**
1258 	 * Number of territories for given team, usually 1.
1259 	 * 
1260 	 * @return
1261 	 */
1262 	public int getTeamTerritoriesCount(int teamId) {
1263 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1264 				JBotMethodsRepository.GetTeamTerritoriesCount(),
1265 				new Object[] { teamId });
1266 		return (Integer) container.syncCallInMainThread();
1267 	}
1268 
1269 	/**
1270 	 * 
1271 	 * Territory Ids of territories that the given team owns. The enum
1272 	 * TERRITORY_* relates the ids to starting positions
1273 	 * 
1274 	 * @param teamId
1275 	 * @return territories belonging to the team
1276 	 */
1277 	public int[] getTeamTerritories(int teamId) {
1278 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1279 				JBotMethodsRepository.GetTeamTerritories(),
1280 				new Object[] { teamId });
1281 		return (int[]) container.syncCallInMainThread();
1282 	}
1283 
1284 	/**
1285 	 * 
1286 	 * Territory Ids of territories that the team the player is in owns. The
1287 	 * enum TERRITORY_* relates the ids to starting positions
1288 	 * 
1289 	 * @return territories belonging to the team
1290 	 */
1291 	public int[] getOwnTeamTerritories() {
1292 		return getTeamTerritories(getOwnTeamId());
1293 	}
1294 
1295 	/**
1296 	 * Id of alliance. Each team belongs to exactly one alliance.
1297 	 * 
1298 	 * @param teamId
1299 	 * @return
1300 	 */
1301 	public int getAllianceId(int teamId) {
1302 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1303 				JBotMethodsRepository.GetAllianceId(),
1304 				new Object[] { teamId });
1305 		return (Integer) container.syncCallInMainThread();
1306 	}
1307 
1308 	/**
1309 	 * Currently requested game speed of given team.
1310 	 * 
1311 	 * @param teamId
1312 	 * @return
1313 	 */
1314 	public int getDesiredGameSpeed(int teamId) {
1315 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1316 				JBotMethodsRepository.GetDesiredGameSpeed(),
1317 				new Object[] { teamId });
1318 		return (Integer) container.syncCallInMainThread();
1319 	}
1320 
1321 	/**
1322 	 * Sum of enemy kills of the given team (for scoring).
1323 	 * 
1324 	 * @param teamId
1325 	 * @return
1326 	 */
1327 	public int getEnemyKills(int teamId) {
1328 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1329 				JBotMethodsRepository.GetEnemyKills(),
1330 				new Object[] { teamId });
1331 		return (Integer) container.syncCallInMainThread();
1332 	}
1333 
1334 	/**
1335 	 * Sum of friendly deaths (deaths in allied populations) of the given team.
1336 	 * 
1337 	 * @param teamId
1338 	 * @return
1339 	 */
1340 	public int getFriendlyDeaths(int teamId) {
1341 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1342 				JBotMethodsRepository.GetFriendlyDeaths(),
1343 				new Object[] { teamId });
1344 		return (Integer) container.syncCallInMainThread();
1345 	}
1346 
1347 	/**
1348 	 * Sum of collateral damage deaths (deaths in own population) of the given
1349 	 * team.
1350 	 * 
1351 	 * @param teamId
1352 	 * @return
1353 	 */
1354 	public int getCollateralDamage(int teamId) {
1355 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1356 				JBotMethodsRepository.GetCollateralDamage(),
1357 				new Object[] { teamId });
1358 		return (Integer) container.syncCallInMainThread();
1359 	}
1360 
1361 	/**
1362 	 * Name of the given team.
1363 	 * 
1364 	 * @param teamId
1365 	 * @return
1366 	 */
1367 	public String getTeamName(int teamId) {
1368 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1369 				JBotMethodsRepository.GetTeamName(),
1370 				new Object[] { teamId });
1371 		return (String) container.syncCallInMainThread();
1372 	}
1373 
1374 	/**
1375 	 * True iff the first team is sharing its radar with the second team.
1376 	 * 
1377 	 * @param teamId1
1378 	 * @param teamId2
1379 	 * @return
1380 	 */
1381 	public boolean isSharingRadar(int teamId1, int teamId2) {
1382 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1383 				JBotMethodsRepository.IsSharingRadar(),
1384 				new Object[] { teamId1, teamId2 });
1385 		return (Boolean) container.syncCallInMainThread();
1386 	}
1387 
1388 	/**
1389 	 * True iff the first team is in cease fire mode with the second team.
1390 	 * 
1391 	 * @param teamId1
1392 	 * @param teamId2
1393 	 * @return
1394 	 */
1395 	public boolean isCeaseFire(int teamId1, int teamId2) {
1396 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1397 				JBotMethodsRepository.IsCeaseFire(),
1398 				new Object[] { teamId1, teamId2 });
1399 		return (Boolean) container.syncCallInMainThread();
1400 	}
1401 
1402 	/**
1403 	 * Offset of ship number memberId from center of fleet, given fleet has
1404 	 * memberCount ships
1405 	 * 
1406 	 * @param memberCount
1407 	 * @param memberId
1408 	 * @return
1409 	 */
1410 	public Location getFleetMemberOffset(int memberCount, int memberId) {
1411 		return fleetMemberOffsets[memberCount - 1][memberId];
1412 	}
1413 
1414 	/**
1415 	 * Used in prepopulation of fleet member offsets. DO NOT USE FROM OTHER
1416 	 * THREADS THAN MAIN, SINCE IT IS NOT ENTIRELY STABLE!!!
1417 	 * 
1418 	 * @param memberCount
1419 	 * @param memberId
1420 	 * @return
1421 	 */
1422 	private Location getFleetMemberOffsetWorker(int memberCount, int memberId) {
1423 		if (memberCount <= 0 || memberId >= memberCount)
1424 			throw new InvalidParameterException(
1425 					"GetFleetMemberOffset requires memberCount > 0 && "
1426 							+ "memberId < membercount. memberCount: "
1427 							+ memberCount + " memberId: " + memberId);
1428 
1429 		return new Location((float[]) JBot.GetFleetMemberOffset(
1430 				memberCount,
1431 				memberId));
1432 	}
1433 
1434 	/**
1435 	 * Queries gameRunning flag.
1436 	 * @return true if game is running.
1437 	 */
1438 	public boolean getRunning() {
1439 		return gameRunning.getFlag();
1440 	}
1441 
1442 	/**
1443 	 * Simplifies queries for possibility of placing a fleet.
1444 	 * Inspired by native bots implemented for Defcon API.
1445 	 * @param placement location, where to check the placement validity.
1446 	 * @param fleetSize number of units in the fleet.
1447 	 * @return true if this placement is valid.
1448 	 */
1449 	public boolean isValidFleetPlacement(Location placement, int fleetSize) {
1450 		for (int i = 0; i < fleetSize; ++i) {
1451 			Location offset = getFleetMemberOffset(fleetSize, i);
1452 
1453 			// the placement validity is equal for battleships, carriers and
1454 			// subs.
1455 			if (!isValidPlacementLocation(
1456 					placement.getX() + offset.getX(),
1457 					placement.getY() + offset.getY(),
1458 					UnitType.BATTLE_SHIP)) {
1459 				return false;
1460 			}
1461 		}
1462 		return true;
1463 	}
1464 
1465 
1466 	/**
1467 	 * Used in prepopulation of fleetSizes. DO NOT USE FROM OTHER THREADS THAN
1468 	 * MAIN, SINCE IT IS NOT ENTIRELY STABLE!!!
1469 	 * 
1470 	 * @param fleetSize
1471 	 * @return
1472 	 */
1473 	private double getFleetDiameterWorker(int fleetSize) {
1474 
1475 		double max = 0;
1476 		for (int i = 0; i < fleetSize; ++i) {
1477 			Location offset = getFleetMemberOffset(fleetSize, i);
1478 			double length = offset.getLength();
1479 			if (max < length)
1480 				max = length;
1481 		}
1482 
1483 		return max + 1d;
1484 	}
1485 
1486 	/**
1487 	 * Gets the smallest circle diameter, which could contain the whole fleet.
1488 	 * 
1489 	 * @param fleetSize
1490 	 * @return diameter of the fleet of given size
1491 	 */
1492 	public double getFleetDiameter(int fleetSize) {
1493 		return fleetDiameters[fleetSize - 1];
1494 	}
1495 
1496 	/**
1497 	 * Returns a flat list of territories possessed by enemies.
1498 	 * 
1499 	 * @return flat list of all enemy territories
1500 	 */
1501 	public LinkedList<Integer> getAllEnemyTerritories() {
1502 
1503 		LinkedList<Integer> territoryIds = new LinkedList<Integer>();
1504 
1505 		for (int enemyId : getEnemyTeamIds()) {
1506 			for (int territoryId : getTeamTerritories(enemyId)) {
1507 				territoryIds.add(territoryId);
1508 			}
1509 		}
1510 
1511 		return territoryIds;
1512 	}
1513 
1514 	/**
1515 	 * Checks validity of placement for structures.
1516 	 * @param placement location, where to check the placement validity.
1517 	 * @return true if this placement is valid.
1518 	 */
1519 	public boolean isValidStructureLocation(Location placement) {
1520 		return isValidPlacementLocation(placement.getX(),
1521 				placement.getY(), UnitType.AIR_BASE);
1522 	}
1523 
1524 	/**
1525 	 * Returns amount of nukes present in the given unit.
1526 	 * 
1527 	 * @param unitId
1528 	 * @return amount of nukes
1529 	 */
1530 	public int getNukeSupply(int unitId) {
1531 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1532 				JBotMethodsRepository.GetNukeSupply(),
1533 				new Object[] { unitId });
1534 		return (Integer) container.syncCallInMainThread();
1535 	}
1536 
1537 	/**
1538 	 * Returns type of unit with this id.
1539 	 * 
1540 	 * @param unitId
1541 	 *            id of unit
1542 	 * @return
1543 	 */
1544 	public UnitType getType(int unitId) {
1545 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1546 				JBotMethodsRepository.GetType(),
1547 				new Object[] { unitId });
1548 		return UnitType.getEnum((Integer) container.syncCallInMainThread());
1549 	}
1550 
1551 	/**
1552 	 * Returns number of activations of this state for this unit.
1553 	 * 
1554 	 * @param unitId
1555 	 *            id of unit
1556 	 * @param state
1557 	 *            state for the unit
1558 	 * @return
1559 	 */
1560 	public int getStateCount(int unitId, IState state) {
1561 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1562 				JBotMethodsRepository.GetStateCount(),
1563 				new Object[] { unitId, state.getStateId() });
1564 
1565 		return (Integer) container.syncCallInMainThread();
1566 	}
1567 
1568 	/**
1569 	 * Returns number of activations of this state for this unit.
1570 	 * 
1571 	 * @param unitId
1572 	 *            id of unit
1573 	 * @param stateId
1574 	 *            stateId for the unit
1575 	 * @return
1576 	 */
1577 	public int getStateCount(int unitId, int stateId) {
1578 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1579 				JBotMethodsRepository.GetStateCount(),
1580 				new Object[] { unitId, stateId });
1581 
1582 		return (Integer) container.syncCallInMainThread();
1583 	}
1584 
1585 	/**
1586 	 * Returns the territory to owner mapping array.
1587 	 * 
1588 	 * @return
1589 	 */
1590 	public int[] getTerritoryOwners() {
1591 		return territoryOwners;
1592 	}
1593 
1594 	/**
1595 	 * Returns the owner of a land territory, which contains given location or
1596 	 * -1 if noone owns it.
1597 	 * 
1598 	 * @param center
1599 	 * @return
1600 	 */
1601 	public int getTerritoryOwner(Location center) {
1602 		return getTerritoryOwner(center.getX(), center.getY());
1603 	}
1604 
1605 	/**
1606 	 * Returns the owner of a land territory, which contains given location or
1607 	 * -1 if noone owns it.
1608 	 * 
1609 	 * @param longitude
1610 	 * @param latitude
1611 	 * @return
1612 	 */
1613 	public int getTerritoryOwner(double longitude, double latitude) {
1614 		return getTerritoryOwner(getTerritoryId(longitude, latitude));
1615 	}
1616 
1617 	/**
1618 	 * Returns the owner of a territory with a given Id.
1619 	 * 
1620 	 * @param territoryId
1621 	 * @return
1622 	 */
1623 	public int getTerritoryOwner(int territoryId) {
1624 
1625 		if (territoryId == -1)
1626 			return -1;
1627 
1628 		return territoryOwners[territoryId];
1629 	}
1630 
1631 	/**
1632 	 * Get id of a land territory at a given location.
1633 	 * 
1634 	 * @param location
1635 	 * @return
1636 	 */
1637 	public int getTerritoryId(Location location) {
1638 		return getTerritoryId(location.getX(), location.getY());
1639 	}
1640 
1641 	/**
1642 	 * Returns the number of territories.
1643 	 * 
1644 	 * @return
1645 	 */
1646 	public int getTerritoriesCount() {
1647 		return JBot.NumTerritories;
1648 	}
1649 
1650 	/**
1651 	 * Gets a list of cities.
1652 	 * 
1653 	 * @return
1654 	 */
1655 	public int[] getCityIds() {
1656 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1657 				JBotMethodsRepository.GetCityIds(),
1658 				null);
1659 		return (int[]) container.syncCallInMainThread();
1660 	}
1661 
1662 	/**
1663 	 * Returns a location of the given object.
1664 	 * 
1665 	 * @param objectId
1666 	 * @return
1667 	 */
1668 	public Location getObjectLocation(int objectId) {
1669 
1670 		SyncMethodExecContainer container = new SyncMethodExecContainer(
1671 				JBotMethodsRepository.GetLongitude(),
1672 				new Object[] { objectId });
1673 		float longitude = (Float) container.syncCallInMainThread();
1674 
1675 		container = new SyncMethodExecContainer(
1676 				JBotMethodsRepository.GetLatitude(),
1677 				new Object[] { objectId });
1678 		float latitude = (Float) container.syncCallInMainThread();
1679 
1680 		return new Location(longitude, latitude);
1681 	}
1682 
1683 	/**
1684 	 * Checks whether are there enough units of each type to create a fleet of a
1685 	 * given composition.
1686 	 * 
1687 	 * @param ships
1688 	 * @return
1689 	 */
1690 	public boolean canCreateFleet(UnitType[] ships) {
1691 
1692 		final Hashtable<Integer, Integer> table = new Hashtable<Integer, Integer>();
1693 
1694 		for (UnitType type : ships) {
1695 
1696 			if (table.containsKey(type.id)) {
1697 				int newCount = table.get(type.id) + 1;
1698 
1699 				int remaining = getRemainingUnits(type);
1700 
1701 				if (newCount > remaining) {
1702 					return false;
1703 				}
1704 
1705 				table.put(type.id, newCount);
1706 			} else {
1707 				table.put(type.id, 1);
1708 			}
1709 		}
1710 		table.clear();
1711 
1712 		return true;
1713 	}
1714 
1715 	/**
1716 	 * Returns range of submarine nukes (SRBM).
1717 	 * 
1718 	 * @return
1719 	 */
1720 	public double getSubNukeRange() {
1721 		return 45d;
1722 	}
1723 
1724 	/**
1725 	 * Returns range of a bomber.
1726 	 * 
1727 	 * @return
1728 	 */
1729 	public double getBomberRange() {
1730 		return 180d;
1731 	}
1732 
1733 	/**
1734 	 * Returns range of a single radar.
1735 	 * 
1736 	 * @return
1737 	 */
1738 	public double getRadarRange() {
1739 		return 30d;
1740 	}
1741 
1742 	/**
1743 	 * Returns range of a fighter.
1744 	 * 
1745 	 * @return
1746 	 */
1747 	public double getFighterRange() {
1748 		return 40;
1749 	}
1750 
1751 	/**
1752 	 * Returns range of AA mode of a silo.
1753 	 * 
1754 	 * @return
1755 	 */
1756 	public double getSiloRange() {
1757 		return 30d;
1758 	}
1759 
1760 	/**
1761 	 * Returns center of gravity of given enemy's cities.
1762 	 * 
1763 	 * @param teamId
1764 	 * @return
1765 	 */
1766 	public DefConLocation getCitiesGravityCenter(int teamId) {
1767 		
1768 		double x = 0;
1769 		double y = 0;
1770 		
1771 		
1772 		List<Integer> cityList;
1773 		if (teamId == getOwnTeamId())
1774 			cityList = ownCityIds;
1775 		else
1776 			cityList = enemiesCityIds.get(teamId);
1777 
1778 		if (cityList == null) {
1779 			PogamutJBotSupport.writeToConsole("cityList is empty: " + teamId
1780 					+ " " + enemiesCityIds);
1781 		}
1782 
1783 		for (int cityId : cityList) {
1784 			DefConLocation location = getCityLocation(cityId);
1785 				
1786 			x += location.getX();
1787 			y += location.getY();
1788 		}
1789 		
1790 		Location sum = new Location(x, y);
1791 		
1792 		return new DefConLocation(sum.scale(1d / cityList.size()));
1793 	}
1794 
1795 	/**
1796 	 * Returns location of the given city.
1797 	 * 
1798 	 * @param cityId
1799 	 * @return
1800 	 */
1801 	public DefConLocation getCityLocation(int cityId) {
1802 		return new DefConLocation(cityLocations.get(cityId));
1803 	}
1804 
1805 }