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