View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.tournament.deathmatch;
2   
3   import java.io.File;
4   import java.util.concurrent.Callable;
5   import java.util.concurrent.ThreadPoolExecutor;
6   import java.util.logging.Level;
7   
8   import org.apache.commons.io.FileUtils;
9   
10  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
11  import cz.cuni.amis.pogamut.ut2004.tournament.botexecution.UT2004BotExecutionConfig;
12  import cz.cuni.amis.pogamut.ut2004.tournament.match.UT2004BotConfig;
13  import cz.cuni.amis.pogamut.ut2004.utils.UCCWrapper;
14  import cz.cuni.amis.pogamut.ut2004.utils.UCCWrapperConf;
15  import cz.cuni.amis.utils.ExceptionToString;
16  import cz.cuni.amis.utils.exception.PogamutException;
17  
18  /**
19   * Performs 1v1 death-match for two Pogamut bots (no native bots here).
20   * <p><p>
21   * You may run it inside thread by invoking new Thread(ut2004DeathMatch1v1).start() or use with {@link ThreadPoolExecutor} as it
22   * is implementing {@link Callable}.
23   * <p><p>
24   * But you will usually want just to execute it using {@link UT2004DeathMatch1v1#call()}.
25   * <p><p>
26   * Encapsulates classes: {@link UT2004DeathMatch}, {@link UT2004DeathMatchConfig}, {@link UT2004BotConfig} and {@link UCCWrapperConf}.
27   * <p><p>
28   * If you want to know how all instanes of {@link UT2004DeathMatch} configuration are setup, see {@link UT2004DeathMatch1v1#configure1Vs1()}
29   * code.
30   * 
31   * @author Jimmy
32   */
33  public class UT2004DeathMatch1v1 implements Callable<UT2004DeathMatchResult>, Runnable {
34  	
35  	/**
36  	 * Used as {@link UT2004DeathMatchConfig#setFragLimit(int)}.
37  	 */
38  	private int fragLimit;
39  	
40  	/**
41  	 * Used as {@link UT2004DeathMatchConfig#setTimeLimit(int)}.
42  	 */
43  	private int timeLimitInMinutes;
44  	
45  	/**
46  	 * Used as {@link UT2004DeathMatchConfig#setMatchId(String)}.
47  	 * <p><p>
48  	 * Example: DMMatch1v1
49  	 */
50  	private String matchName;
51  	
52  	/**
53  	 * Used as {@link UT2004BotConfig#setBotId(String)} for the first bot.
54  	 * <p><p>
55  	 * Example: Bot1
56  	 */
57  	private String bot1Name;
58  	
59  	/**
60  	 * Used as {@link UT2004BotExecutionConfig#setPathToBotJar(String)} for the first bot.
61  	 * <p><p>
62  	 * Example: "bots" + File.separator + "Bot1" + File.separator + "Bot1.jar"
63  	 */
64  	private String bot1JarPath;
65  	
66  	/**
67  	 * Used as {@link UT2004BotConfig#setBotId(String)} for the second bot.
68  	 * <p><p>
69  	 * Example: Bot1
70  	 */
71  	private String bot2Name;
72  	
73  	/**
74  	 * Used as {@link UT2004BotExecutionConfig#setPathToBotJar(String)} for the second bot.
75  	 * <p><p>
76  	 * Example: "bots" + File.separator + "Bot2" + File.separator + "Bot2.jar"
77  	 */
78  	private String bot2JarPath;
79  	
80  	/**
81  	 * Used as {@link UCCWrapperConf#setUnrealHome(String)}.
82  	 * <p><p>
83  	 * Example: "d:" + File.separator + "Games" + File.separator + "UT2004"
84  	 */
85  	private String unrealHome;
86  	
87  	/**
88  	 * Used as {@link UCCWrapperConf#setMapName(String)}.
89  	 * <p><p>
90  	 * Example: DM-1on1-Albatross
91  	 */
92  	private String mapName;
93  	
94  	/**
95  	 * Used as {@link UT2004DeathMatchConfig#setOutputDirectory(File)}.
96  	 * <p><p>
97  	 * Example: "results" + File.separator + "matches"
98  	 */
99  	private String outputDir;
100 	
101 	/**
102 	 * Used for logging.
103 	 */
104 	private LogCategory log;
105 
106 	/**
107 	 * Last result of the match. Available after first successful execution of {@link UT2004DeathMatch1v1#run()}.
108 	 */
109 	private UT2004DeathMatchResult result = null;
110 	
111 	/**
112 	 * Whether to produce output for "HumanLikeBot project" analysis.
113 	 */
114 	private boolean humanLikeLogEnabled = false;
115 
116 	private Throwable exception;
117 
118 	/**
119 	 * Parameter-less constructor, don't forget to initialize everything!	 
120 	 */
121 	public UT2004DeathMatch1v1() {
122 		log = new LogCategory(getMatchName());
123 	}
124 	
125 	/**
126 	 * Match-name only constructor, don't forget to initialize everything!	 
127 	 */
128 	public UT2004DeathMatch1v1(String matchName) {
129 		this.matchName = matchName;
130 		log = new LogCategory(getMatchName());
131 	}
132 	
133 	/**
134 	 * Initializes all needed fields for combat of 2 bots.
135 	 * <p><p>
136 	 * Used time limit: 20 minutes,<p>
137 	 * Used frag limit: 20<p>
138 	 * Recommended mapName: DM-1on1-Albatross
139 	 * 
140 	 * @param unrealHome
141 	 * @param mapName
142 	 * @param bot1Name
143 	 * @param bot1JarPath
144 	 * @param bot2Name
145 	 * @param bot2JarPath
146 	 */
147 	public UT2004DeathMatch1v1(String unrealHome, String mapName, String bot1Name, String bot1JarPath, String bot2Name, String bot2JarPath) {
148 		this.unrealHome = unrealHome;
149 		this.mapName = mapName;
150 		this.bot1Name = bot1Name;
151 		this.bot1JarPath = bot1JarPath;
152 		this.bot2Name = bot2Name;
153 		this.bot2JarPath = bot2JarPath;
154 		this.matchName = bot1Name + "-vs-" + bot2Name;
155 		this.outputDir = "results" + File.separator + "matches";
156 		this.fragLimit = 20;
157 		this.timeLimitInMinutes = 20;
158 		log = new LogCategory(getMatchName());
159 	}
160 	
161 	/**
162 	 * Logger used for outputting info about the match.
163 	 * <p><p>
164 	 * If you want to output stuff to console, use {@link LogCategory#addConsoleHandler()}.
165 	 * @return
166 	 */
167 	public LogCategory getLog() {
168 		return log;
169 	}
170 
171 	/**
172 	 * Used as {@link UT2004DeathMatchConfig#setFragLimit(int)}.
173 	 * 
174 	 * @return
175 	 */
176 	public int getFragLimit() {
177 		return fragLimit;
178 	}
179 
180 	/**
181 	 * Used as {@link UT2004DeathMatchConfig#setTimeLimit(int)}.
182 	 * 
183 	 * @return
184 	 */
185 	public int getTimeLimitInMinutes() {
186 		return timeLimitInMinutes;
187 	}
188 
189 	/**
190 	 * Used as {@link UT2004DeathMatchConfig#setMatchId(String)}.
191 	 * 
192 	 * @return
193 	 */
194 	public String getMatchName() {
195 		return matchName;
196 	}
197 	
198 	/**
199 	 * Used as {@link UT2004BotConfig#setBotId(String)} for the first bot.
200 	 * 
201 	 * @return
202 	 */
203 	public String getBot1Name() {
204 		return bot1Name;
205 	}
206 	
207 	/**
208 	 * Used as {@link UT2004BotExecutionConfig#setPathToBotJar(String)} for the first bot.
209 	 * 
210 	 * @return
211 	 */
212 	public String getBot1JarPath() {
213 		return bot1JarPath;
214 	}
215 	
216 	/**
217 	 * Used as {@link UT2004BotConfig#setBotId(String)} for the second bot.
218 	 * 
219 	 * @return
220 	 */
221 	public String getBot2Name() {
222 		return bot2Name;
223 	}
224 	
225 	/**
226 	 * Used as {@link UT2004BotExecutionConfig#setPathToBotJar(String)} for the second bot.
227 	 * 
228 	 * @return
229 	 */
230 	public String getBot2JarPath() {
231 		return bot2JarPath;
232 	}
233 	
234 	/**
235 	 * Used as {@link UCCWrapperConf#setMapName(String)}.
236 	 * 
237 	 * @return
238 	 */
239 	public String getUnrealHome() {
240 		return unrealHome;
241 	}
242 	
243 	/**
244 	 * Used as {@link UCCWrapperConf#setMapName(String)}.
245 	 * @return
246 	 */
247 	public String getMapName() {
248 		return mapName;
249 	}
250 	
251 	/**
252 	 * Used as {@link UT2004DeathMatchConfig#setOutputDirectory(File)}.
253 	 * 
254 	 * @return
255 	 */
256 	public String getOutputDir() {
257 		return outputDir;
258 	}
259 	
260 	/**
261 	 * Last result of the match. Available after first successful execution of {@link UT2004DeathMatch1v1#run()}.
262 	 * 
263 	 * @return
264 	 */
265 	public UT2004DeathMatchResult getResult() {
266 		return result;
267 	}
268 
269 	/**
270 	 * If {@link UT2004DeathMatch1v1#run()} terminates with an exception, it will be made available through this getter.
271 	 * @return
272 	 */
273 	public Throwable getException() {
274 		return exception;
275 	}
276 
277 	/**
278 	 * Sets logger used for outputting info about the match.
279 	 * @param log
280 	 */
281 	public void setLog(LogCategory log) {
282 		this.log = log;
283 	}
284 
285 	public UT2004DeathMatch1v1 setFragLimit(int fragLimit) {
286 		this.fragLimit = fragLimit;
287 		return this;
288 	}
289 
290 	public UT2004DeathMatch1v1 setTimeLimitInMinutes(int timeLimitInMinutes) {
291 		this.timeLimitInMinutes = timeLimitInMinutes;
292 		return this;
293 	}
294 
295 	public UT2004DeathMatch1v1 setMatchName(String matchName) {
296 		this.matchName = matchName;
297 		return this;
298 	}
299 
300 	public UT2004DeathMatch1v1 setBot1Name(String bot1Name) {
301 		this.bot1Name = bot1Name;
302 		return this;
303 	}
304 
305 	public UT2004DeathMatch1v1 setBot1JarPath(String bot1JarPath) {
306 		this.bot1JarPath = bot1JarPath;
307 		return this;
308 	}
309 
310 	public UT2004DeathMatch1v1 setBot2Name(String bot2Name) {
311 		this.bot2Name = bot2Name;
312 		return this;
313 	}
314 
315 	public UT2004DeathMatch1v1 setBot2JarPath(String bot2JarPath) {
316 		this.bot2JarPath = bot2JarPath;
317 		return this;
318 	}
319 
320 	public UT2004DeathMatch1v1 setUnrealHome(String unrealHome) {
321 		this.unrealHome = unrealHome;
322 		return this;
323 	}
324 
325 	public UT2004DeathMatch1v1 setMapName(String mapName) {
326 		this.mapName = mapName;
327 		return this;
328 	}
329 
330 	public UT2004DeathMatch1v1 setOutputDir(String outputDir) {
331 		this.outputDir = outputDir;
332 		return this;
333 	}
334 	
335 	public UT2004DeathMatch1v1 setHumanLikeLogEnabled(boolean humanLikeLogEnabled) {
336 		this.humanLikeLogEnabled = humanLikeLogEnabled;
337 		return this;
338 	}
339 
340 	/**
341 	 * Removes directory with match results, called automatically in {@link UT2004DeathMatch1v1#run()}.
342 	 */
343 	public void cleanUp() {
344 		try {
345 			FileUtils.deleteQuietly(new File(getOutputDir() + File.separator + getMatchName()));
346 		} catch (Exception e) {			
347 		}		
348 	}	
349 	
350 	/**
351 	 * Contains main code that setups the {@link UT2004DeathMatchConfig}, {@link UT2004BotConfig} and {@link UCCWrapper}
352 	 * instances (it might be interesting for you to check the code for yourself if you wish to customize it further...).
353 	 * 
354 	 * @return
355 	 */
356 	protected UT2004DeathMatchConfig configure1Vs1() {
357 		UT2004DeathMatchConfig matchConfig = new UT2004DeathMatchConfig();
358 		
359 		matchConfig.setMatchId(getMatchName());
360 		matchConfig.setOutputDirectory(new File(getOutputDir() == null ? "results" + File.separator + "matches" : getOutputDir()));
361 				
362 		matchConfig.setFragLimit(getFragLimit());
363 		matchConfig.setTimeLimit(getTimeLimitInMinutes()); // in minutes
364 		
365 		matchConfig.setHumanLikeLogEnabled(humanLikeLogEnabled);
366 		
367 		matchConfig.getUccConf().setStartOnUnusedPort(true);
368 		matchConfig.getUccConf().setUnrealHome(getUnrealHome());
369 		matchConfig.getUccConf().setGameType("BotDeathMatch");
370 		matchConfig.getUccConf().setMapName(getMapName());
371 		
372 		UT2004BotConfig botConfig;
373 		
374 		botConfig = new UT2004BotConfig();
375 		botConfig.setBotId(getBot1Name());
376 		botConfig.setPathToBotJar(getBot1JarPath());
377 		botConfig.setTeamNumber(255);
378 		botConfig.setRedirectStdErr(true);
379 		botConfig.setRedirectStdOut(true);		
380 		matchConfig.addBot(botConfig);
381 		
382 		botConfig = new UT2004BotConfig();
383 		botConfig.setBotId(getBot2Name());
384 		botConfig.setPathToBotJar(getBot2JarPath());
385 		botConfig.setTeamNumber(255);
386 		botConfig.setRedirectStdErr(true);
387 		botConfig.setRedirectStdOut(true);		
388 		matchConfig.addBot(botConfig);
389 		
390 		return matchConfig;
391 	}
392 	
393 	/**
394 	 * Creates new instance of {@link UT2004DeathMatch} with desired configuration + validates it (i.e., if it won't throw an exception
395 	 * you may be sure, that the {@link UT2004DeathMatch} instance is correctly configured).
396 	 * @return
397 	 */
398 	public UT2004DeathMatch createMatch() {
399 		log.info("Configuring match: " + getMatchName());
400 		UT2004DeathMatchConfig matchConfig = configure1Vs1();
401 		UT2004DeathMatch match = new UT2004DeathMatch(matchConfig, log);
402 		match.validate();
403 		return match;
404 	}
405 	
406 	/**
407 	 * Executes the match. It first {@link UT2004DeathMatch1v1#cleanUp()}, than it configures {@link UT2004DeathMatch} via {@link UT2004DeathMatch1v1#createMatch()}
408 	 * and {@link UT2004DeathMatch#execute()}s it.
409 	 * <p><p>
410 	 * If no exception occurs during the match (== match will successfully finish), match result will be available through {@link UT2004DeathMatch1v1#getResult()}.
411 	 * <p><p>
412 	 * If exception occurs (due to whatever reason), this exception will be available through {@link UT2004DeathMatch1v1#getException()}.
413 	 * 
414 	 * @throws PogamutException wraps any exception into this one
415 	 */
416 	@Override
417 	public void run() {
418 		try {
419 			cleanUp();
420 			
421 			UT2004DeathMatch match = createMatch();
422 	
423 			log.info("Executing match: " + getMatchName());
424 			
425 			this.result = match.execute();
426 			
427 			log.info("Match " + getMatchName() + " result: " + result);
428 				
429 			log.info("---/// MATCH OK ///---");
430 		} catch (Exception e) {
431 			if (log != null && log.isLoggable(Level.SEVERE)) log.severe(ExceptionToString.process("Failed to execute the match: " + getMatchName() + ".", e));
432 			this.exception = e;
433 			if (e instanceof RuntimeException) throw (RuntimeException)e;
434 			throw new PogamutException("Failed to execute the match: " + getMatchName(), e, this);
435 		}
436 	}
437 
438 	@Override
439 	public UT2004DeathMatchResult call() throws Exception {
440 		run();
441 		return getResult();
442 	}
443 
444 }