View Javadoc

1   package javabot;
2   
3   import java.io.PrintWriter;
4   import java.io.StringWriter;
5   import java.util.ArrayList;
6   import java.util.HashMap;
7   import java.util.List;
8   import java.util.Map;
9   import java.util.Map.Entry;
10  import java.util.concurrent.ConcurrentLinkedQueue;
11  import java.util.concurrent.LinkedBlockingQueue;
12  import java.util.concurrent.TimeUnit;
13  import java.util.logging.Level;
14  
15  import javabot.JBot.EventData;
16  import javabot.JBot.FleetData;
17  import javabot.JBot.UnitData;
18  import javabot.events.DefConBasicUpdate;
19  import javabot.events.IDefConBasicEvent;
20  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentState;
21  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStarted;
22  import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldChangeEvent;
23  import cz.cuni.amis.pogamut.defcon.agent.DefConAgent;
24  import cz.cuni.amis.pogamut.defcon.agent.module.sensor.GameInfo;
25  import cz.cuni.amis.pogamut.defcon.agentmanager.DefConAgentManager;
26  import cz.cuni.amis.pogamut.defcon.agentmanager.exception.CantInstantiateAgentException;
27  import cz.cuni.amis.pogamut.defcon.agentmanager.exception.ModuleForAgentClassNotFoundException;
28  import cz.cuni.amis.pogamut.defcon.base3d.worldview.object.DefConLocation;
29  import cz.cuni.amis.pogamut.defcon.communication.messages.commands.DefConCommand;
30  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.City;
31  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.DefConEvent;
32  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.DefConObject;
33  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.DefConUnitObject;
34  import cz.cuni.amis.pogamut.defcon.communication.messages.infos.Fleet;
35  import cz.cuni.amis.pogamut.defcon.consts.Event;
36  import cz.cuni.amis.pogamut.defcon.consts.UnitType;
37  import cz.cuni.amis.pogamut.defcon.factory.DefConAgentModule;
38  import cz.cuni.amis.pogamut.defcon.utils.SyncMethodExecContainer;
39  import cz.cuni.amis.utils.ExceptionToString;
40  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
41  import cz.cuni.amis.utils.flag.Flag;
42  import cz.cuni.amis.utils.flag.IFlag;
43  import cz.cuni.amis.utils.flag.WaitForFlagChange;
44  import cz.cuni.amis.utils.flag.WaitForFlagChange.IAccept;
45  
46  /**
47   * Layer that takes care of incoming and outgoing requests on defcon.
48   * 
49   * @author Radek 'Black_Hand' Pibil
50   * 
51   */
52  public class PogamutJBotSupport {
53  
54  	private static final String NEW_LINE = System.getProperty("line.separator");
55  
56  	public static final String PROPERTY_CLASS = "module_class";
57  	public static final String PROPERTY_START_TIMEOUT_SECS = "start_timeout";
58  
59  	private static final int DEFAULT_START_TIMEOUT_SECS = 10;
60  	private static final long MAX_UPDATE_TIME = 75;
61  
62  	private static DefConAgent bot = null;
63  
64  	private static final ConcurrentLinkedQueue<SyncMethodExecContainer> queries =
65  			new ConcurrentLinkedQueue<SyncMethodExecContainer>();
66  
67  	private static final LinkedBlockingQueue<IDefConBasicEvent> events = new LinkedBlockingQueue<IDefConBasicEvent>();
68  
69  	private static final LinkedBlockingQueue<IWorldChangeEvent> unitUpdates = new LinkedBlockingQueue<IWorldChangeEvent>();
70  
71  	private static final int COMMANDS_PER_TICK = 20;
72  
73  	/**
74  	 * Leave as not-immutable for testing purposes!
75  	 */
76  
77  	public static Flag<Boolean> botIsRunning = new Flag<Boolean>(false);
78  
79  	private static ConcurrentLinkedQueue<DefConCommand> commands = new ConcurrentLinkedQueue<DefConCommand>();
80  
81  	private static ActExecutor actExecutor = new ActExecutor();
82  
83  	private static String mapToString(Map<?, ?> map) {
84  		StringBuffer sb = new StringBuffer();
85  		for (Entry<?, ?> entry : map.entrySet()) {
86  			sb.append(entry.getKey() + " = " + entry.getValue());
87  			sb.append(NEW_LINE);
88  		}
89  		return sb.toString();
90  	}
91  
92  	private static boolean start(Map<String, String> options) {
93  		logInit(Level.INFO, "Instantiating bot with options: " + NEW_LINE
94  				+ mapToString(options));
95  
96  		String clsStr = options.get(PROPERTY_CLASS);
97  		Class<DefConAgentModule> cls = null;
98  		if (clsStr != null) {
99  			try {
100 				cls = (Class<DefConAgentModule>) Class.forName(clsStr);
101 			} catch (Exception e) {
102 				logInit(
103 						Level.SEVERE,
104 						"Bot module class '"
105 								+ clsStr
106 								+ "' could not be found, did you put your jar into 'java' directory?");
107 				logInit(Level.SEVERE, "BOT CAN NOT BE STARTED!");
108 				logInit(Level.SEVERE, "RESTART DEFCON! " + e.toString() + " "
109 						+ System.getProperty("java.class.path"));
110 				return false;
111 			}
112 			logInit(Level.INFO, "Bot module class '" + clsStr + "'.");
113 		} else {
114 			logInit(Level.INFO, "Bot module class ('" + PROPERTY_CLASS
115 					+ "' options) not specified.");
116 		}
117 
118 		logInit(Level.INFO, "Instantiating bot.");
119 
120 		try {
121 			bot = (DefConAgent<?>) DefConAgentManager.getInstance()
122 					.getAgentInstance(cls);
123 
124 		} catch (CantInstantiateAgentException e) {
125 			logInit(Level.SEVERE, ExceptionToString.process(e));
126 			logInit(Level.SEVERE, "BOT CAN NOT BE STARTED!");
127 			logInit(Level.SEVERE, "RESTART DEFCON!");
128 			return false;
129 		} catch (ModuleForAgentClassNotFoundException e) {
130 			logInit(Level.SEVERE, ExceptionToString.process(e));
131 			logInit(Level.SEVERE, "BOT CAN NOT BE STARTED!");
132 			logInit(Level.SEVERE, "RESTART DEFCON!");
133 			return false;
134 		}
135 
136 		logInit(Level.INFO, "Bot was instantiated.");
137 
138 		String startTimeoutStr = options.get(PROPERTY_START_TIMEOUT_SECS);
139 		int startTimeoutSecs = DEFAULT_START_TIMEOUT_SECS;
140 		if (startTimeoutStr != null) {
141 			try {
142 				startTimeoutSecs = Integer.parseInt(startTimeoutStr);
143 			} catch (Exception e) {
144 				logInit(Level.WARNING, "Option '" + PROPERTY_START_TIMEOUT_SECS
145 						+ "' does not contain integer number.");
146 			}
147 		}
148 
149 		logInit(Level.INFO, "Starting bot (" + startTimeoutSecs
150 				+ " secs timeout).");
151 
152 		bot.setOptions(options);
153 		try {
154 			bot.start();
155 			try {
156 				new WaitForFlagChange<AgentState>((IFlag) bot.getState(),
157 						new IAccept<AgentState>() {
158 							@Override
159 							public boolean accept(AgentState flagValue) {
160 								return flagValue
161 										.isState(AgentStateStarted.class);
162 							}
163 						}).await(startTimeoutSecs, TimeUnit.SECONDS);
164 			} catch (PogamutInterruptedException e) {
165 				logInit(Level.SEVERE, "Bot fails to start in "
166 						+ startTimeoutSecs + " secs, killing bot.");
167 				bot.kill();
168 				logInit(Level.SEVERE, "BOT CAN NOT BE STARTED!");
169 				logInit(Level.SEVERE, "RESTART DEFCON!");
170 				return false;
171 			}
172 		} catch (Exception e) {
173 			logInit(Level.SEVERE, ExceptionToString.process(
174 					"Bot fails to start.", e));
175 			bot = null;
176 			logInit(Level.SEVERE, "BOT CAN NOT BE STARTED!");
177 			logInit(Level.SEVERE, "RESTART DEFCON!");
178 			return false;
179 		}
180 		logInit(Level.INFO, "Bot running!");
181 		botIsRunning.setFlag(true);
182 		return true;
183 	}
184 
185 	public static DefConAgent<?> getBot() {
186 		return bot;
187 	}
188 
189 	private static String[] split(String msg, int len) {
190 		if (msg.length() < len)
191 			return new String[] { msg };
192 		String[] result = new String[msg.length() / len + 1];
193 		for (int i = 0; i < msg.length() / len + 1; ++i) {
194 			if ((i + 1) * len < msg.length()) {
195 				result[i] = msg.substring(i * len, (i + 1) * len);
196 			} else {
197 				result[i] = msg.substring(i * len);
198 			}
199 		}
200 		return result;
201 	}
202 
203 	public static void logInitException(Throwable e) {
204 		StringWriter writer = new StringWriter();
205 		e.printStackTrace(new PrintWriter(writer));
206 		writeToConsole(writer.toString());
207 	}
208 
209 	public static void logGameException(Throwable e) {
210 		StringWriter writer = new StringWriter();
211 		e.printStackTrace(new PrintWriter(writer));
212 		writeToConsole(writer.toString());
213 	}
214 
215 	/**
216 	 * Formats and send a log message during INIT phase.
217 	 * 
218 	 * @param level
219 	 * @param msg
220 	 */
221 	public static void logInit(Level level, String msg) {
222 		if (botIsRunning.getFlag()) {
223 			String[] msgs = split("[" + level + "] " + msg, 50);
224 			for (String m : msgs) {
225 				writeToConsole(m);
226 			}
227 		} else {
228 			System.out.println("[" + level + "] " + msg);
229 		}
230 	}
231 
232 	/**
233 	 * Formats and send a log message during GAME phase.
234 	 * 
235 	 * @param level
236 	 * @param msg
237 	 */
238 	public static void logGame(Level level, String msg) {
239 		if (botIsRunning.getFlag()) {
240 			JBot.DebugLog("[" + level + "] " + msg);
241 		} else {
242 			System.out.println("[" + level + "] " + msg);
243 		}
244 	}
245 
246 	public static GameInfo gameInfo = null;
247 
248 	/**
249 	 * Called from JBot.initialise().
250 	 * 
251 	 * @param commandLineOptions
252 	 * @return
253 	 */
254 	public static boolean initialise(String[][] commandLineOptions) {
255 		PogamutJBotSupport
256 				.writeToConsole("Executing PogamutJBotSupport initialise");
257 		logGame(Level.INFO, "Executing PogamutJBotSupport initialise");
258 		StringBuffer sb = new StringBuffer();
259 		sb.append("Command line options:");
260 		Map<String, String> optionMap = new HashMap<String, String>();
261 		for (String[] option : commandLineOptions) {
262 			sb.append(NEW_LINE);
263 			if (option.length > 0) {
264 				logInit(Level.INFO, option[0]
265 						+ (option.length > 1 && option[1].length() > 0 ? " = "
266 								+ option[1] : ""));
267 				optionMap.put(option[0], option.length > 1 ? option[1] : null);
268 			}
269 		}
270 		logGame(Level.INFO, "Calling start");
271 		boolean result = start(optionMap);
272 		JBot.WriteToConsole("Finished start");
273 
274 		Thread.currentThread().setPriority(
275 				(int) ((Thread.MAX_PRIORITY - Thread.NORM_PRIORITY) / 3d)
276 						+ Thread.NORM_PRIORITY);
277 
278 		return result;
279 	}
280 
281 	/**
282 	 * Called from JBot.addEvent().
283 	 * 
284 	 * @param data
285 	 */
286 	public static void addEvent(EventData data) {
287 		synchronized (events) {
288 
289 			if (data.m_eventType != JBot.EventDestroyed) {	
290 
291 				DefConEvent event = Event.getInstanceOfUnitTypeFromEventType(
292 						data,
293 						bot
294 								.getWorldView().getCurrentTime());
295 
296 				events.add(event);
297 			} else {
298 
299 				UnitData unitdata = new UnitData();
300 
301 				unitdata.m_objectId = data.m_targetObjectId;
302 				unitdata.m_teamId = JBot.GetTeamId(data.m_targetObjectId);
303 				unitdata.m_type = JBot.GetType(data.m_targetObjectId);
304 				unitdata.m_currentState = -1;
305 				unitdata.m_visible = true;
306 				unitdata.m_longitude = data.m_longitude;
307 				unitdata.m_latitude = data.m_latitude;				
308 
309 				if (UnitType.naval.contains(UnitType
310 						.getEnum(unitdata.m_type))) {
311 
312 					int fleetId = JBot.GetFleetId(data.m_targetObjectId);
313 
314 					int[] fleetMembers = JBot.GetFleetMembers(fleetId);
315 
316 					if (fleetMembers.length == 1) {
317 
318 						Fleet fleet_object = new Fleet(fleetId,
319 								unitdata.m_teamId,
320 								new DefConLocation(data.m_longitude,
321 										data.m_latitude),
322 								false, fleetMembers, bot
323 										.getWorldView().getCurrentTime());
324 
325 						unitUpdates.add(fleet_object.createDestroyedEvent());
326 					}
327 
328 				}
329 
330 				DefConUnitObject<?> update_object = (DefConUnitObject<?>) UnitType
331 						.getInstanceOfUnitTypeFromUnitData(unitdata, bot
332 								.getWorldView().getCurrentTime());
333 				unitUpdates.add(update_object.createDestroyedEvent());
334 			}
335 		}
336 	}
337 
338 	/**
339 	 * Called from JBot.update().
340 	 * 
341 	 * @return
342 	 */
343 	public static boolean update() {
344 		// JBot.WriteToConsole("PogamutJBotSupport.update(); Time: " +
345 		// System.currentTimeMillis());
346 		long current_time = System.currentTimeMillis();
347 
348 		if (JBot.IsVictoryTimerActive() && JBot.GetVictoryTimer() == 0) {
349 			writeToConsole("Match result");
350 			for (int teamId : JBot.GetTeamIds()) {
351 				writeToConsole("TeamId: " + teamId);
352 				writeToConsole("EnemyKills: " + JBot.GetEnemyKills(teamId));
353 				writeToConsole("CollateralDamage: "
354 						+ JBot.GetCollateralDamage(teamId));
355 				writeToConsole("Casualties: "
356 						+ JBot.GetFriendlyDeaths(teamId));
357 				writeToConsole("RemainingPopulation: "
358 						+ JBot.GetRemainingPopulation(teamId));
359 			}
360 			try {
361 				Thread.sleep(1000);
362 			} catch (InterruptedException e) {
363 				e.printStackTrace();
364 			}
365 			System.exit(0);
366 		}
367 
368 		performCommands();
369 
370 		prepareUnitsUpdate();
371 
372 		synchronized (events) {
373 			// TODO: FIX! IDIOTIC!
374 			events.add(new DefConBasicUpdate((long) JBot.GetGameTime()));
375 		}
376 
377 		while (System.currentTimeMillis() - current_time < MAX_UPDATE_TIME) {
378 			checkQueries();
379 		}
380 
381 		return true;
382 	}
383 
384 	/**
385 	 * Collects all updates of all visible units.
386 	 * 
387 	 * @return LinkedList of all collected events.
388 	 */
389 	private static void prepareUnitsUpdate() {
390 
391 		synchronized (unitUpdates) {
392 			List<UnitData> unit_data = JBot.GetAllUnitData();
393 
394 			float time = JBot.GetGameTime();
395 			for (UnitData unit : unit_data) {
396 				DefConObject object = UnitType
397 						.getInstanceOfUnitTypeFromUnitData(
398 								unit,
399 								time);
400 				unitUpdates.add(object.createUpdateEvent());
401 			}
402 
403 
404 			List<FleetData> fleet_data = JBot.GetAllFleetData();
405 
406 			for (FleetData fleet : fleet_data) {
407 
408 				Fleet object = new Fleet(fleet.m_fleetId,
409 						fleet.m_teamId,
410 						new DefConLocation(fleet.m_longitude, fleet.m_latitude),
411 						fleet.m_visible, fleet.m_fleetMembers, time);
412 
413 				if (object.getFleetMembers().length > 0)
414 					unitUpdates.add(object.createUpdateEvent());
415 			}
416 			for (int cityId : JBot.GetCityIds()) {
417 				DefConLocation cityLocation = new DefConLocation(
418 						JBot.GetLongitude(cityId),
419 						JBot.GetLatitude(cityId));
420 
421 				int ownerId = JBot.GetTeamId(cityId);
422 				City city = new City(cityId, ownerId, cityLocation, true,
423 						JBot.GetCityPopulation(cityId), JBot.GetGameTime());
424 
425 				unitUpdates.add(city.createUpdateEvent());
426 			}
427 		}
428 	}
429 
430 	public static List<IWorldChangeEvent> getUnitsUpdate() {
431 		List<IWorldChangeEvent> update = new ArrayList<IWorldChangeEvent>();
432 		unitUpdates.drainTo(update);
433 
434 		return update;
435 	}
436 
437 	private static void checkQueries() {
438 		SyncMethodExecContainer container = queries.poll();
439 
440 		if (container != null) {
441 			container.execute();			
442 		}
443 	}
444 
445 	public synchronized static void addQuery(SyncMethodExecContainer container) {
446 		queries.add(container);
447 	}
448 
449 	private static void performCommands() {		
450 		int counter = COMMANDS_PER_TICK;
451 
452 		while (--counter > 0 && !commands.isEmpty()) {
453 			actExecutor.sendCommand(commands.poll());
454 		}
455 	}
456 
457 	public static void addCommand(DefConCommand command) {
458 		synchronized (commands) {
459 			commands.add(command);
460 		}
461 	}
462 
463 	public static List<IDefConBasicEvent> getEvents() {
464 		List<IDefConBasicEvent> result = new ArrayList<IDefConBasicEvent>();
465 
466 		synchronized (events) {
467 			events.drainTo(result);
468 		}
469 		return result;
470 	}
471 
472 	public static void writeToConsole(String logLine) {
473 		if (logLine != null) {
474 			JBot.WriteToConsole(logLine);
475 		} else {
476 			JBot.WriteToConsole("NULL LOGLINE");
477 			for (StackTraceElement element : Thread.currentThread()
478 					.getStackTrace()) {
479 				JBot.WriteToConsole(element.toString());
480 			}
481 		}
482 	}
483 
484 	public static ActExecutor getDefConActExecutor() {
485 		return actExecutor;
486 	}
487 
488 	public static void setName(String name) {
489 		JBot.SendChatMessage(String.format("/name [Bot]%s", name), 0);
490 	}
491 
492 }