View Javadoc

1   package cz.cuni.amis.pogamut.defcon.communication.worldview.modules.managers.fleets;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.Iterator;
6   import java.util.LinkedList;
7   import java.util.List;
8   import java.util.Queue;
9   import java.util.SortedMap;
10  import java.util.TreeMap;
11  
12  import com.google.inject.Inject;
13  
14  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
15  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
16  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectDestroyedEvent;
17  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectFirstEncounteredEvent;
18  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
19  import cz.cuni.amis.pogamut.defcon.agent.impl.DefConAgentLogicController;
20  import cz.cuni.amis.pogamut.defcon.agent.impl.ILogicUpdateListener;
21  import cz.cuni.amis.pogamut.defcon.ai.fleetai.IFleetAI;
22  import cz.cuni.amis.pogamut.defcon.base3d.worldview.object.DefConLocation;
23  import cz.cuni.amis.pogamut.defcon.communication.messages.commands.PlaceFleet;
24  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.Fleet;
25  import cz.cuni.amis.pogamut.defcon.consts.UnitType;
26  
27  /**
28   * Manages all known fleets. Be they either own or enemies. Also takes care of
29   * updating of all you fleets AIs.
30   * 
31   * @author Radek 'Black_Hand' Pibil
32   * 
33   */
34  public class FleetsManager {
35  
36  	protected final DefConAgentLogicController<?> logic;
37  
38  	protected final SortedMap<Integer, List<Fleet>> enemyFleets = new TreeMap<Integer, List<Fleet>>();
39  	protected final ArrayList<FleetWithAI> ownFleets = new ArrayList<FleetWithAI>();
40  	protected final int ownTeamId;
41  
42  	protected final LinkedList<QueuedPlacing> queuedPlacings = new LinkedList<QueuedPlacing>();
43  	protected final LinkedList<QueuedPlacing> oldPlacings = new LinkedList<QueuedPlacing>();
44  
45  	/**
46  	 * Fleet and AI pair.
47  	 * 
48  	 * @author Radek 'Black_Hand' Pibil
49  	 * 
50  	 */
51  	public class FleetWithAI {
52  		Fleet fleet;
53  		IFleetAI ai;
54  
55  		public FleetWithAI(Fleet fleet, IFleetAI ai) {
56  			this.fleet = fleet;
57  			this.ai = ai;
58  		}
59  
60  		public final IFleetAI getAI() {
61  			return ai;
62  		}
63  
64  		public final void setAI(IFleetAI ai) {
65  			this.ai = ai;
66  		}
67  
68  		public final Fleet getFleet() {
69  			return fleet;
70  		}
71  	}
72  
73  	protected class QueuedPlacing {
74  		protected final Queue<DefConLocation> placementOptions;
75  		protected final IPlacingFinishedListener callback;
76  		protected final int expectedCount;
77  		protected final Object data;
78  		protected final ArrayList<Fleet> succeeded = new ArrayList<Fleet>();
79  		protected final List<DefConLocation> last = new ArrayList<DefConLocation>();
80  		protected final UnitType[] fleetComposition;
81  		protected double lastPlacementTime;
82  		protected static final double LAST_PLACEMENT_MS_LIMIT = 2500d;
83  
84  		public QueuedPlacing(int expectedCount,
85  				UnitType[] fleetComposition,
86  				Queue<DefConLocation> placementOptions,
87  				Object data,
88  				IPlacingFinishedListener callback) {
89  
90  			if (expectedCount <= 0 || fleetComposition == null
91  					|| placementOptions == null
92  					|| placementOptions.size() < expectedCount) {
93  				throw new IllegalArgumentException(
94  						"Invalid parameters for QueuedPlacing.");
95  			}
96  
97  			for (int teamId : logic.getGameInfo().getEnemyTeamIds()) {
98  				enemyFleets
99  						.put(teamId, new ArrayList<Fleet>());
100 			}
101 
102 			this.expectedCount = expectedCount;
103 			this.placementOptions = placementOptions;
104 			this.callback = callback;
105 			this.data = data;
106 			this.fleetComposition = fleetComposition;
107 		}
108 
109 		public final Queue<DefConLocation> getPlacementOptions() {
110 			return placementOptions;
111 		}
112 
113 		public final int getExpectedCount() {
114 			return expectedCount;
115 		}
116 
117 		public final List<DefConLocation> getLast() {
118 			return last;
119 		}
120 
121 		public final void doPlace() {
122 			if (succeeded.size() + placementOptions.size() < expectedCount ||
123 					!logic.getGameInfo().canCreateFleet(fleetComposition)) {
124 				queuedPlacings.remove(this);
125 				oldPlacings.add(this);
126 				if (callback != null) {
127 					callback.placingFinished(succeeded, data, false);
128 				}
129 				return;
130 			}
131 
132 			int lastCount = last.size();
133 			DefConLocation location = null;
134 			while (!placementOptions.isEmpty() && lastCount == last.size()) {
135 				location = placementOptions.poll();
136 
137 				if (queuePlacingsContain(location))
138 					continue;
139 
140 				if (!isValidPlacement(location, fleetComposition.length))
141 					continue;
142 
143 				last.add(location);
144 				break;
145 			}
146 
147 			if (lastCount == last.size()) {
148 				return;
149 			}
150 
151 			lastPlacementTime = System.currentTimeMillis();
152 			if (!placeFleetWorker(location, fleetComposition)) {
153 				oldPlacings.add(this);
154 				queuedPlacings.remove(this);
155 				if (callback != null) {
156 					callback.placingFinished(succeeded, data, false);
157 				}
158 			}
159 		}
160 
161 		public final void successfulPlacement(Fleet fleet) {
162 			succeeded.add(fleet);
163 
164 			if (succeeded.size() == expectedCount) {
165 				queuedPlacings.remove(this);
166 				oldPlacings.remove(this);
167 				if (callback != null) {
168 					callback.placingFinished(succeeded, data, true);
169 				}
170 			} else {
171 				doPlace();
172 			}
173 		}
174 
175 		public void refresh() {
176 			if (System.currentTimeMillis() - lastPlacementTime > LAST_PLACEMENT_MS_LIMIT) {
177 				doPlace();
178 			}
179 		}
180 	}
181 
182 	protected boolean queuePlacingsContain(Location location) {
183 		for (QueuedPlacing placing : queuedPlacings) {
184 			if (placing.getLast() != null && placing.getLast().equals(location)) {
185 				return true;
186 			}
187 		}
188 		return false;
189 	}
190 
191 	@Inject
192 	public FleetsManager(DefConAgentLogicController<?> logic) {
193 		ownTeamId = logic.getWorldView().getGameInfo()
194 				.getOwnTeamId();
195 		this.logic = logic;
196 		logic.getWorldView().addObjectListener(Fleet.class, listener);
197 		logic.addGameLogicListener(logicUpdateListener);
198 	}
199 
200 	protected final IWorldObjectListener<Fleet> listener =
201 			new IWorldObjectListener<Fleet>() {
202 
203 				@Override
204 				public void notify(IWorldObjectEvent<Fleet> event) {
205 
206 					if (event instanceof WorldObjectFirstEncounteredEvent<?>) {
207 						addFleet(event.getObject());
208 					}
209 					if (event instanceof WorldObjectDestroyedEvent<?>) {
210 						removeFleet(event.getObject());
211 					}
212 				}
213 			};
214 
215 	protected final ILogicUpdateListener logicUpdateListener = new ILogicUpdateListener() {
216 
217 		@Override
218 		public void update() {
219 			if (!queuedPlacings.isEmpty())
220 				queuedPlacings.peek().refresh();
221 
222 			for (FleetWithAI fleet : ownFleets) {
223 				IFleetAI ai = fleet.getAI();
224 
225 				if (ai != null)
226 					ai.update();
227 			}
228 		}
229 
230 	};
231 
232 	protected void addFleet(Fleet fleet) {
233 		if (fleet.getTeamId() == ownTeamId) {
234 			addOwnFleet(fleet);
235 		} else {
236 			addEnemyFleet(fleet);
237 		}
238 	}
239 
240 	protected void removeFleet(Fleet fleet) {
241 		if (fleet.getTeamId() == ownTeamId) {
242 			removeOwnFleet(fleet);
243 		} else {
244 			removeEnemyFleet(fleet);
245 		}
246 	}
247 
248 	protected void addEnemyFleet(Fleet fleet) {
249 
250 		int enemyId = fleet.getTeamId();
251 		List<Fleet> singleEnemyFleets = enemyFleets
252 				.get(enemyId);
253 
254 		if (!singleEnemyFleets.contains(fleet))
255 			singleEnemyFleets.add(fleet);
256 
257 	}
258 
259 	protected void removeEnemyFleet(Fleet fleet) {
260 
261 		int enemyId = fleet.getTeamId();
262 		List<Fleet> singleEnemyFleets = enemyFleets
263 				.get(enemyId);
264 
265 		if (singleEnemyFleets.contains(fleet)) {
266 			logic.getLog().info("Removing enemy fleet: " + fleet.getId());
267 			singleEnemyFleets.remove(fleet);
268 		}
269 	}
270 
271 	protected void addOwnFleet(Fleet fleet) {
272 		if (!ownFleets.contains(fleet)) {
273 			ownFleets.add(new FleetWithAI(fleet, null));
274 
275 			logic.getLog().info(
276 					"Placed fleet to: " + fleet.getLocation() + " "
277 							+ fleet.getId());
278 		} else {
279 			logic.getLog().info(
280 					"WUT?! Placed fleet to: " + fleet.getLocation() + " "
281 							+ fleet.getId());
282 		}
283 		
284 
285 		for (QueuedPlacing placings : queuedPlacings) {
286 
287 			for (Location loc : placings.getLast()) {
288 				if (loc.equals(fleet.getLocation())) {
289 					placings.successfulPlacement(fleet);
290 					return;
291 				}
292 			}
293 		}
294 
295 		for (QueuedPlacing placings : oldPlacings) {
296 
297 			for (Location loc : placings.getLast()) {
298 				if (loc.equals(fleet.getLocation())) {
299 					placings.successfulPlacement(fleet);
300 					return;
301 				}
302 			}
303 		}
304 
305 	}
306 
307 	protected void removeOwnFleet(Fleet fleet) {
308 		
309 		Iterator<FleetWithAI> ownFleetsIterator = ownFleets.iterator();
310 
311 		while (ownFleetsIterator.hasNext()) {
312 			FleetWithAI ownFleet = ownFleetsIterator.next();
313 			
314 			if (ownFleet.fleet.getId().getLongId() != fleet.getId().getLongId())
315 				continue;
316 
317 			logic.getLog().info("Removing own fleet: " + fleet.getId());
318 
319 			ownFleetsIterator.remove();
320 
321 			if (ownFleet.getAI() != null) {
322 				ownFleet.getAI().dispose();
323 				ownFleet.setAI(null);
324 				return;
325 			}
326 
327 		}
328 	}
329 
330 	/**
331 	 * Returns the list of enemy fleets.
332 	 * 
333 	 * @return
334 	 */
335 	public SortedMap<Integer, List<Fleet>> getEnemyFleets() {
336 		return Collections.unmodifiableSortedMap(enemyFleets);
337 	}
338 
339 	/**
340 	 * Returns the list of enemy fleets.
341 	 * 
342 	 * @return
343 	 */
344 	public List<Fleet> getEnemyFleets(int enemyId) {
345 		// logic.getLog().info(
346 		// "getting enemy fleets: " + enemyId + " "
347 		// + enemyFleets.toString());
348 		return Collections.unmodifiableList(enemyFleets.get(enemyId));
349 	}
350 
351 	/**
352 	 * Returns the list of own fleets.
353 	 * 
354 	 * @return
355 	 */
356 	public List<FleetWithAI> getOwnFleets() {
357 		return Collections.unmodifiableList(ownFleets);
358 	}
359 
360 	/**
361 	 * Assigns AI to the given fleet.
362 	 * 
363 	 * @param fleet
364 	 * @param ai
365 	 * @return
366 	 */
367 	public boolean assignAI(Fleet fleet, IFleetAI ai) {
368 		for (FleetWithAI fleetWithAI : ownFleets) {
369 			if (fleetWithAI.getFleet().equals(fleet)) {
370 				fleetWithAI.setAI(ai);
371 				return true;
372 			}
373 		}
374 
375 		return false;
376 	}
377 
378 	/**
379 	 * This wont work most likely, because of the quirky WorldObjectId
380 	 * 
381 	 * @param fleetId
382 	 * @param ai
383 	 * @return
384 	 */
385 	public boolean assignAI(int fleetId, IFleetAI ai) {
386 		for (FleetWithAI fleetWithAI : ownFleets) {
387 			if (fleetWithAI.getFleet().getId().getLongId() == fleetId) {
388 				fleetWithAI.setAI(ai);
389 				return true;
390 			}
391 		}
392 
393 		return false;
394 	}
395 
396 
397 	/**
398 	 * Tries to place fleet on a given location (queues the command).
399 	 * 
400 	 * @param location
401 	 * @param ships
402 	 * @return tentative success in placing of the fleet. if true then check the
403 	 *         event for definitive confirmation.
404 	 */
405 	public boolean placeFleet(DefConLocation location, UnitType[] ships,
406 			Object initData,
407 			IPlacingFinishedListener finishedListener) {
408 
409 		LinkedList<DefConLocation> tmp = new LinkedList<DefConLocation>();
410 		tmp.add(location);
411 		
412 		return placeFleet(tmp, ships, 1, initData, finishedListener);
413 	}
414 
415 	protected boolean isValidPlacement(Location location, int count) {
416 		if (logic.getGameInfo()
417 				.isValidFleetPlacement(location, count)) {
418 			return true;
419 		} else {
420 			return false;
421 		}
422 	}
423 
424 	protected boolean placeFleetWorker(DefConLocation location, UnitType[] ships) {
425 
426 
427 		logic.getGameInfo().canCreateFleet(ships);
428 
429 		logic.getLog().info(
430 				String.format("Placing fleet to: %s.", location.toString()));
431 
432 
433 		logic.getAct().act(new PlaceFleet(location, ships));
434 
435 		return true;
436 	}
437 
438 	/**
439 	 * Tries to place the fleet on one of the given positions (queues the
440 	 * command, if it finds suitable place), starting from the head.
441 	 * 
442 	 * @param orderedPlacements
443 	 *            given positions with preference
444 	 * @param fleetComposition
445 	 *            ship types
446 	 * @return true if at least one fleet might be placeable (listener is going
447 	 *         to be called)
448 	 */
449 	public boolean placeFleet(
450 			List<DefConLocation> orderedPlacements,
451 			UnitType[] fleetComposition, int fleetsCount,
452 			Object initData,
453 			IPlacingFinishedListener finishedListener) {
454 
455 		if (fleetsCount <= 0 || orderedPlacements.size() < fleetsCount)
456 			return false;
457 
458 		LinkedList<DefConLocation> placements = new LinkedList<DefConLocation>();
459 
460 		for (DefConLocation placement : orderedPlacements) {
461 			placements.addLast(placement);
462 		}
463 
464 		Iterator<DefConLocation> placementsIterator = placements.listIterator();
465 
466 		while (placementsIterator.hasNext()) {
467 
468 			Location location = placementsIterator.next();
469 			if (!isValidPlacement(location, fleetComposition.length)) {
470 				placementsIterator.remove();
471 				if (placements.size() < fleetsCount) {
472 					break;
473 				}
474 			}
475 		}
476 
477 		if (placements.size() < fleetsCount) {
478 			return false;
479 		}
480 
481 		QueuedPlacing placing = new QueuedPlacing(fleetsCount,
482 				fleetComposition, placements, initData, finishedListener);
483 
484 		queuedPlacings.add(placing);
485 		
486 		if (queuedPlacings.size() == 1) {
487 			placing.doPlace();
488 		}
489 
490 		return true;
491 	}
492 }