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 	private Throwable exception;
112 
113 	/**
114 	 * Parameter-less constructor, don't forget to initialize everything!	 
115 	 */
116 	public UT2004DeathMatch1v1() {
117 		log = new LogCategory(getMatchName());
118 	}
119 	
120 	/**
121 	 * Match-name only constructor, don't forget to initialize everything!	 
122 	 */
123 	public UT2004DeathMatch1v1(String matchName) {
124 		this.matchName = matchName;
125 		log = new LogCategory(getMatchName());
126 	}
127 	
128 	/**
129 	 * Initializes all needed fields for combat of 2 bots.
130 	 * <p><p>
131 	 * Used time limit: 20 minutes,<p>
132 	 * Used frag limit: 20<p>
133 	 * Recommended mapName: DM-1on1-Albatross
134 	 * 
135 	 * @param unrealHome
136 	 * @param mapName
137 	 * @param bot1Name
138 	 * @param bot1JarPath
139 	 * @param bot2Name
140 	 * @param bot2JarPath
141 	 */
142 	public UT2004DeathMatch1v1(String unrealHome, String mapName, String bot1Name, String bot1JarPath, String bot2Name, String bot2JarPath) {
143 		this.unrealHome = unrealHome;
144 		this.mapName = mapName;
145 		this.bot1Name = bot1Name;
146 		this.bot1JarPath = bot1JarPath;
147 		this.bot2Name = bot2Name;
148 		this.bot2JarPath = bot2JarPath;
149 		this.matchName = bot1Name + "-vs-" + bot2Name;
150 		this.outputDir = "results" + File.separator + "matches";
151 		this.fragLimit = 20;
152 		this.timeLimitInMinutes = 20;
153 		log = new LogCategory(getMatchName());
154 	}
155 	
156 	/**
157 	 * Logger used for outputting info about the match.
158 	 * <p><p>
159 	 * If you want to output stuff to console, use {@link LogCategory#addConsoleHandler()}.
160 	 * @return
161 	 */
162 	public LogCategory getLog() {
163 		return log;
164 	}
165 
166 	/**
167 	 * Used as {@link UT2004DeathMatchConfig#setFragLimit(int)}.
168 	 * 
169 	 * @return
170 	 */
171 	public int getFragLimit() {
172 		return fragLimit;
173 	}
174 
175 	/**
176 	 * Used as {@link UT2004DeathMatchConfig#setTimeLimit(int)}.
177 	 * 
178 	 * @return
179 	 */
180 	public int getTimeLimitInMinutes() {
181 		return timeLimitInMinutes;
182 	}
183 
184 	/**
185 	 * Used as {@link UT2004DeathMatchConfig#setMatchId(String)}.
186 	 * 
187 	 * @return
188 	 */
189 	public String getMatchName() {
190 		return matchName;
191 	}
192 	
193 	/**
194 	 * Used as {@link UT2004BotConfig#setBotId(String)} for the first bot.
195 	 * 
196 	 * @return
197 	 */
198 	public String getBot1Name() {
199 		return bot1Name;
200 	}
201 	
202 	/**
203 	 * Used as {@link UT2004BotExecutionConfig#setPathToBotJar(String)} for the first bot.
204 	 * 
205 	 * @return
206 	 */
207 	public String getBot1JarPath() {
208 		return bot1JarPath;
209 	}
210 	
211 	/**
212 	 * Used as {@link UT2004BotConfig#setBotId(String)} for the second bot.
213 	 * 
214 	 * @return
215 	 */
216 	public String getBot2Name() {
217 		return bot2Name;
218 	}
219 	
220 	/**
221 	 * Used as {@link UT2004BotExecutionConfig#setPathToBotJar(String)} for the second bot.
222 	 * 
223 	 * @return
224 	 */
225 	public String getBot2JarPath() {
226 		return bot2JarPath;
227 	}
228 	
229 	/**
230 	 * Used as {@link UCCWrapperConf#setMapName(String)}.
231 	 * 
232 	 * @return
233 	 */
234 	public String getUnrealHome() {
235 		return unrealHome;
236 	}
237 	
238 	/**
239 	 * Used as {@link UCCWrapperConf#setMapName(String)}.
240 	 * @return
241 	 */
242 	public String getMapName() {
243 		return mapName;
244 	}
245 	
246 	/**
247 	 * Used as {@link UT2004DeathMatchConfig#setOutputDirectory(File)}.
248 	 * 
249 	 * @return
250 	 */
251 	public String getOutputDir() {
252 		return outputDir;
253 	}
254 	
255 	/**
256 	 * Last result of the match. Available after first successful execution of {@link UT2004DeathMatch1v1#run()}.
257 	 * 
258 	 * @return
259 	 */
260 	public UT2004DeathMatchResult getResult() {
261 		return result;
262 	}
263 
264 	/**
265 	 * If {@link UT2004DeathMatch1v1#run()} terminates with an exception, it will be made available through this getter.
266 	 * @return
267 	 */
268 	public Throwable getException() {
269 		return exception;
270 	}
271 
272 	/**
273 	 * Sets logger used for outputting info about the match.
274 	 * @param log
275 	 */
276 	public void setLog(LogCategory log) {
277 		this.log = log;
278 	}
279 
280 	public UT2004DeathMatch1v1 setFragLimit(int fragLimit) {
281 		this.fragLimit = fragLimit;
282 		return this;
283 	}
284 
285 	public UT2004DeathMatch1v1 setTimeLimitInMinutes(int timeLimitInMinutes) {
286 		this.timeLimitInMinutes = timeLimitInMinutes;
287 		return this;
288 	}
289 
290 	public UT2004DeathMatch1v1 setMatchName(String matchName) {
291 		this.matchName = matchName;
292 		return this;
293 	}
294 
295 	public UT2004DeathMatch1v1 setBot1Name(String bot1Name) {
296 		this.bot1Name = bot1Name;
297 		return this;
298 	}
299 
300 	public UT2004DeathMatch1v1 setBot1JarPath(String bot1JarPath) {
301 		this.bot1JarPath = bot1JarPath;
302 		return this;
303 	}
304 
305 	public UT2004DeathMatch1v1 setBot2Name(String bot2Name) {
306 		this.bot2Name = bot2Name;
307 		return this;
308 	}
309 
310 	public UT2004DeathMatch1v1 setBot2JarPath(String bot2JarPath) {
311 		this.bot2JarPath = bot2JarPath;
312 		return this;
313 	}
314 
315 	public UT2004DeathMatch1v1 setUnrealHome(String unrealHome) {
316 		this.unrealHome = unrealHome;
317 		return this;
318 	}
319 
320 	public UT2004DeathMatch1v1 setMapName(String mapName) {
321 		this.mapName = mapName;
322 		return this;
323 	}
324 
325 	public UT2004DeathMatch1v1 setOutputDir(String outputDir) {
326 		this.outputDir = outputDir;
327 		return this;
328 	}
329 
330 	/**
331 	 * Removes directory with match results, called automatically in {@link UT2004DeathMatch1v1#run()}.
332 	 */
333 	public void cleanUp() {
334 		try {
335 			FileUtils.deleteQuietly(new File(getOutputDir() + File.separator + getMatchName()));
336 		} catch (Exception e) {			
337 		}		
338 	}	
339 	
340 	/**
341 	 * Contains main code that setups the {@link UT2004DeathMatchConfig}, {@link UT2004BotConfig} and {@link UCCWrapper}
342 	 * instances (it might be interesting for you to check the code for yourself if you wish to customize it further...).
343 	 * 
344 	 * @return
345 	 */
346 	protected UT2004DeathMatchConfig configure1Vs1() {
347 		UT2004DeathMatchConfig matchConfig = new UT2004DeathMatchConfig();
348 		
349 		matchConfig.setMatchId(getMatchName());
350 		matchConfig.setOutputDirectory(new File(getOutputDir() == null ? "results" + File.separator + "matches" : getOutputDir()));
351 				
352 		matchConfig.setFragLimit(getFragLimit());
353 		matchConfig.setTimeLimit(getTimeLimitInMinutes()); // in minutes
354 		
355 		matchConfig.getUccConf().setStartOnUnusedPort(true);
356 		matchConfig.getUccConf().setUnrealHome(getUnrealHome());
357 		matchConfig.getUccConf().setGameType("BotDeathMatch");
358 		matchConfig.getUccConf().setMapName(getMapName());
359 		
360 		UT2004BotConfig botConfig;
361 		
362 		botConfig = new UT2004BotConfig();
363 		botConfig.setBotId(getBot1Name());
364 		botConfig.setPathToBotJar(getBot1JarPath());
365 		botConfig.setTeamNumber(255);
366 		botConfig.setRedirectStdErr(true);
367 		botConfig.setRedirectStdOut(true);		
368 		matchConfig.addBot(botConfig);
369 		
370 		botConfig = new UT2004BotConfig();
371 		botConfig.setBotId(getBot2Name());
372 		botConfig.setPathToBotJar(getBot2JarPath());
373 		botConfig.setTeamNumber(255);
374 		botConfig.setRedirectStdErr(true);
375 		botConfig.setRedirectStdOut(true);		
376 		matchConfig.addBot(botConfig);
377 		
378 		return matchConfig;
379 	}
380 	
381 	/**
382 	 * Creates new instance of {@link UT2004DeathMatch} with desired configuration + validates it (i.e., if it won't throw an exception
383 	 * you may be sure, that the {@link UT2004DeathMatch} instance is correctly configured).
384 	 * @return
385 	 */
386 	public UT2004DeathMatch createMatch() {
387 		log.info("Configuring match: " + getMatchName());
388 		UT2004DeathMatchConfig matchConfig = configure1Vs1();
389 		UT2004DeathMatch match = new UT2004DeathMatch(matchConfig, log);
390 		match.validate();
391 		return match;
392 	}
393 	
394 	/**
395 	 * Executes the match. It first {@link UT2004DeathMatch1v1#cleanUp()}, than it configures {@link UT2004DeathMatch} via {@link UT2004DeathMatch1v1#createMatch()}
396 	 * and {@link UT2004DeathMatch#execute()}s it.
397 	 * <p><p>
398 	 * If no exception occurs during the match (== match will successfully finish), match result will be available through {@link UT2004DeathMatch1v1#getResult()}.
399 	 * <p><p>
400 	 * If exception occurs (due to whatever reason), this exception will be available through {@link UT2004DeathMatch1v1#getException()}.
401 	 * 
402 	 * @throws PogamutException wraps any exception into this one
403 	 */
404 	@Override
405 	public void run() {
406 		try {
407 			cleanUp();
408 			
409 			UT2004DeathMatch match = createMatch();
410 	
411 			log.info("Executing match: " + getMatchName());
412 			
413 			this.result = match.execute();
414 			
415 			log.info("Match " + getMatchName() + " result: " + result);
416 				
417 			log.info("---/// MATCH OK ///---");
418 		} catch (Exception e) {
419 			if (log != null && log.isLoggable(Level.SEVERE)) log.severe(ExceptionToString.process("Failed to execute the match: " + getMatchName() + ".", e));
420 			this.exception = e;
421 			if (e instanceof RuntimeException) throw (RuntimeException)e;
422 			throw new PogamutException("Failed to execute the match: " + getMatchName(), e, this);
423 		}
424 	}
425 
426 	@Override
427 	public UT2004DeathMatchResult call() throws Exception {
428 		run();
429 		return getResult();
430 	}
431 
432 }