View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.tournament.match;
2   
3   import java.io.File;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.Comparator;
7   import java.util.HashMap;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Map.Entry;
11  
12  import cz.cuni.amis.pogamut.base.utils.Pogamut;
13  import cz.cuni.amis.pogamut.ut2004.tournament.GameBots2004Ini;
14  import cz.cuni.amis.pogamut.ut2004.tournament.UT2004Ini;
15  import cz.cuni.amis.pogamut.ut2004.tournament.utils.UT2004TournamentProperty;
16  import cz.cuni.amis.pogamut.ut2004.utils.UCCWrapperConf;
17  import cz.cuni.amis.utils.Const;
18  import cz.cuni.amis.utils.NullCheck;
19  import cz.cuni.amis.utils.SafeEquals;
20  import cz.cuni.amis.utils.exception.PogamutException;
21  import cz.cuni.amis.utils.token.IToken;
22  import cz.cuni.amis.utils.token.Tokens;
23  
24  /**
25   * Base configuration of the UT2004 match, you have to specify:
26   * <ol>
27   * <li>Match id</li>
28   * <li>Concrete GB2004Ini file to be used via {@link GameBots2004Ini}</li>
29   * <li>Concrete UCC to run via {@link UCCWrapperConf}</li>
30   * <li>Concrete list of bots to be used via {@link UT2004BotConfig}</li>
31   * </ol>
32   * Guess what... everything is preinitialized... but you should at least adjust uccConf.
33   * 
34   * @author Jimmy
35   */
36  
37  public class UT2004MatchConfig {
38  	
39  	public enum BotType {
40  		BOT,// user/custom bot
41  		NATIVE,
42  		HUMAN
43  	}
44  	
45  	protected File outputDirectory = new File("./results/matches");
46  	
47  	protected IToken matchId = Tokens.get("DMMatch");
48  	
49  	protected UCCWrapperConf uccConf = new UCCWrapperConf();
50  	
51  	protected UT2004Ini ut2004Ini;
52  	
53  	protected GameBots2004Ini gb2004Ini;
54  	
55  	protected Map<IToken, UT2004BotConfig> bots = new HashMap<IToken, UT2004BotConfig>();
56  	
57  	protected Map<IToken, UT2004NativeBotConfig> nativeBots = new HashMap<IToken, UT2004NativeBotConfig>();
58  	
59  	protected Map<IToken, UT2004HumanConfig> humans = new HashMap<IToken, UT2004HumanConfig>();
60  
61  	private boolean humanLikeLogEnabled;
62  	
63  	public UT2004MatchConfig() {
64  		String unrealHome = Pogamut.getPlatform().getProperty(UT2004TournamentProperty.UT2004_DIR.getKey());
65  		if (unrealHome != null) {
66  			uccConf.setUnrealHome(unrealHome);
67  		}
68  		
69  		File ut2004File = new File(unrealHome + "/System/UT2004.ini");
70  		if (ut2004File.isFile() && ut2004File.exists()) {
71  			ut2004Ini = new UT2004Ini(ut2004File);
72  		} else {
73  			ut2004Ini = new UT2004Ini();
74  		}
75  		
76  		gb2004Ini = new GameBots2004Ini();		
77  	}
78  	
79  	/**
80  	 * Copy-constructor.
81  	 * @param orig
82  	 */
83  	public UT2004MatchConfig(UT2004MatchConfig orig) {
84  		this.matchId = orig.matchId;
85  		this.uccConf = new UCCWrapperConf(orig.getUccConf());
86  		this.ut2004Ini = new UT2004Ini(orig.getUT2004Ini());
87  		this.gb2004Ini = new GameBots2004Ini(orig.getGb2004Ini());
88  		for (Entry<IToken, UT2004BotConfig> bot : orig.getBots().entrySet()) {
89  			this.bots.put(bot.getKey(), new UT2004BotConfig(bot.getValue()));
90  		}
91  		for (Entry<IToken, UT2004NativeBotConfig> bot : orig.getNativeBots().entrySet()) {
92  			this.nativeBots.put(bot.getKey(), new UT2004NativeBotConfig(bot.getValue()));
93  		}
94  		for (Entry<IToken, UT2004HumanConfig> bot : orig.getHumans().entrySet()) {
95  			this.humans.put(bot.getKey(), new UT2004HumanConfig(bot.getValue()));
96  		}
97  	}
98  		
99  	@Override
100 	public int hashCode() {
101 		return matchId == null ? super.hashCode() : matchId.hashCode();
102 	}
103 	
104 	@Override
105 	public boolean equals(Object obj) {
106 		if (this == obj) return true;
107 		if (obj == null) return false;
108 		if (!(obj instanceof UT2004MatchConfig)) return false;
109 		return SafeEquals.equals(matchId, ((UT2004MatchConfig)obj).getMatchId());
110 	}
111 
112 	public File getOutputDirectory() {
113 		return outputDirectory;
114 	}
115 
116 	public IToken getMatchId() {
117 		return matchId;
118 	}
119 
120 	public void setMatchId(IToken matchId) {
121 		this.matchId = matchId;
122 	}
123 	
124 	public void setMatchId(String matchId) {
125 		this.matchId = Tokens.get(matchId);
126 	}
127 
128 	public UCCWrapperConf getUccConf() {
129 		return uccConf;
130 	}
131 
132 	/**
133 	 * Preinitialized automatically.
134 	 * @return
135 	 */
136 	public GameBots2004Ini getGb2004Ini() {
137 		return gb2004Ini;
138 	}
139 	
140 	/**
141 	 * Preinitialized automatically. 
142 	 * @return
143 	 */
144 	public UT2004Ini getUT2004Ini() {
145 		return ut2004Ini;
146 	}
147 	
148 	public Map<IToken, UT2004BotConfig> getBots() {
149 		return bots;
150 	}
151 	
152 	public Map<IToken, UT2004NativeBotConfig> getNativeBots() {
153 		return nativeBots;
154 	}
155 	
156 	public Map<IToken, UT2004HumanConfig> getHumans() {
157 		return humans;
158 	} 
159 	
160 	/**
161 	 * Ids are sorted: 1) custom bots, 2) native bots.
162 	 * <p><p>
163 	 * WARNING: O(n*log(n)) complexity!
164 	 * 
165 	 * @return
166 	 */
167 	public List<IToken> getAllBotIds() {
168 		List<IToken> bots = new ArrayList<IToken>(getBots().keySet());
169 		List<IToken> nativeBots = new ArrayList<IToken>(getNativeBots().keySet());
170 		List<IToken> humans = new ArrayList<IToken>(getHumans().keySet());
171 		Collections.sort(bots, new Comparator<IToken>() {
172 			@Override
173 			public int compare(IToken o1, IToken o2) {
174 				return o1.getToken().compareTo(o2.getToken());
175 			}				
176 		});
177 		Collections.sort(nativeBots, new Comparator<IToken>() {
178 			@Override
179 			public int compare(IToken o1, IToken o2) {
180 				return o1.getToken().compareTo(o2.getToken());
181 			}				
182 		});	
183 		Collections.sort(humans, new Comparator<IToken>() {
184 			@Override
185 			public int compare(IToken o1, IToken o2) {
186 				return o1.getToken().compareTo(o2.getToken());
187 			}				
188 		});
189 		List<IToken> botIds = new ArrayList<IToken>(bots);
190 		botIds.addAll(nativeBots);
191 		botIds.addAll(humans);
192 		return botIds;
193 	}
194 	
195 	public UT2004MatchConfig setOutputDirectory(File outputDirectory) {
196 		this.outputDirectory = outputDirectory;
197 		return this;
198 	}
199 	
200 	public UT2004MatchConfig setUccConf(UCCWrapperConf uccConf) {
201 		this.uccConf = uccConf;
202 		return this;
203 	}
204 	
205 	/**
206 	 * No need to call, preinitialized automatically.
207 	 * @return
208 	 */
209 	public UT2004MatchConfig setGb2004Ini(GameBots2004Ini gb2004Ini) {
210 		this.gb2004Ini = gb2004Ini;
211 		return this;
212 	}
213 
214 	public UT2004MatchConfig setBots(Map<IToken, UT2004BotConfig> bots) {
215 		this.bots = bots;
216 		return this;
217 	}
218 	
219 	public UT2004MatchConfig setNativeBots(Map<IToken, UT2004NativeBotConfig> nativeBots) {
220 		this.nativeBots = nativeBots;
221 		return this;
222 	}
223 	
224 	public UT2004MatchConfig clearBots() {
225 		this.bots.clear();
226 		return this;
227 	}
228 	
229 	public UT2004MatchConfig clearNativeBots() {
230 		this.nativeBots.clear();
231 		return this;
232 	}
233 	
234 	public UT2004MatchConfig clearHumans() {
235 		this.humans.clear();
236 		return this;
237 	}
238 	
239 	/**
240 	 * Adds NEW bot configuration into the object, checks whether there is no BotId clash (if so, throws an exception).
241 	 * @param bots
242 	 * @return
243 	 */
244 	public UT2004MatchConfig addBot(UT2004BotConfig... bots) {
245 		if (bots == null) return this;
246 		for (UT2004BotConfig bot : bots) {
247 			NullCheck.check(bot.getBotId(), "bot.getBotId()");
248 			if (this.bots.containsKey(bot.getBotId())) {
249 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing custom bot configuration under this ID. If you need to override it, use setBot().", this);
250 			}
251 			if (this.nativeBots.containsKey(bot.getBotId())) {
252 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing native bot configuration under this ID. If you need to override it, use setBot().", this);
253 			}
254 			if (this.humans.containsKey(bot.getBotId())) {
255 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing human configuration under this ID. If you need to override it, use setBot().", this);
256 			}
257 			this.bots.put(bot.getBotId(), bot);
258 		}
259 		return this;
260 	}
261 	
262 	/**
263 	 * Sets bot configuration into the object, does not checks whether there is BotId clash.
264 	 * 
265 	 * @param bots
266 	 * @return
267 	 */
268 	public UT2004MatchConfig setBot(UT2004BotConfig... bots) {
269 		if (bots == null) return this;
270 		for (UT2004BotConfig bot : bots) {
271 			NullCheck.check(bot.getBotId(), "bot.getBotId()");
272 			nativeBots.remove(bot.getBotId());
273 			humans.remove(bot.getBotId());			
274 			this.bots.put(bot.getBotId(), bot);
275 		}
276 		return this;
277 	}
278 	
279 	/**
280 	 * Adds NEW native bot configuration into the object, checks whether there is no BotId clash (if so, throws an exception).
281 	 * @param bots
282 	 * @return
283 	 */
284 	public UT2004MatchConfig addNativeBot(UT2004NativeBotConfig... bots) {
285 		if (bots == null) return this;
286 		for (UT2004NativeBotConfig bot : bots) {
287 			NullCheck.check(bot.getBotId(), "bot.getBotId()");
288 			if (this.bots.containsKey(bot.getBotId())) {
289 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing custom bot configuration under this ID. If you need to override it, use setNativeBot().", this);
290 			}
291 			if (this.nativeBots.containsKey(bot.getBotId())) {
292 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing native bot configuration under this ID. If you need to override it, use setNativeBot().", this);
293 			}
294 			if (this.humans.containsKey(bot.getBotId())) {
295 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing human configuration under this ID. If you need to override it, use setNativeBot().", this);
296 			}
297 			this.nativeBots.put(bot.getBotId(), bot);
298 		}
299 		return this;
300 	}
301 	
302 	/**
303 	 * Sets native bot configuration into the object, does not checks whether there is BotId clash.
304 	 * @param bots
305 	 * @return
306 	 */
307 	public UT2004MatchConfig setNativeBot(UT2004NativeBotConfig... bots) {
308 		if (bots == null) return this;
309 		for (UT2004NativeBotConfig bot : bots) {
310 			NullCheck.check(bot.getBotId(), "bot.getBotId()");
311 			this.bots.remove(bot.getBotId());
312 			humans.remove(bot.getBotId());		
313 			this.nativeBots.put(bot.getBotId(), bot);
314 		}
315 		return this;
316 	}
317 	
318 	/**
319 	 * Adds NEW bot configuration into the object, checks whether there is no BotId clash (if so, throws an exception).
320 	 * @param bots
321 	 * @return
322 	 */
323 	public UT2004MatchConfig addHuman(UT2004HumanConfig... humans) {
324 		if (humans == null) return this;
325 		for (UT2004HumanConfig human : humans) {
326 			NullCheck.check(human.getHumanId(), "human.getHumanId()");
327 			if (this.bots.containsKey(human.getHumanId())) {
328 				throw new PogamutException("Can't add another bot under the id " + human.getHumanId().getToken() + ", there is already an existing custom bot configuration under this ID. If you need to override it, use setNativeBot().", this);
329 			}
330 			if (this.nativeBots.containsKey(human.getHumanId())) {
331 				throw new PogamutException("Can't add another bot under the id " + human.getHumanId().getToken() + ", there is already an existing native bot configuration under this ID. If you need to override it, use setNativeBot().", this);
332 			}
333 			if (this.humans.containsKey(human.getHumanId())) {
334 				throw new PogamutException("Can't add another bot under the id " + human.getHumanId().getToken() + ", there is already an existing human configuration under this ID. If you need to override it, use setNativeBot().", this);
335 			}
336 			this.humans.put(human.getHumanId(), human);
337 		}
338 		return this;
339 	}
340 	
341 	/**
342 	 * Sets human configuration into the object, does not checks whether there is BotId clash.
343 	 * 
344 	 * @param humans
345 	 * @return
346 	 */
347 	public UT2004MatchConfig setHuman(UT2004HumanConfig... humans) {
348 		if (humans == null) return this;
349 		for (UT2004HumanConfig human : humans) {
350 			NullCheck.check(human.getHumanId(), "human.getHumanId()");
351 			this.bots.remove(human.getHumanId());
352 			this.nativeBots.remove(human.getHumanId());		
353 			this.humans.put(human.getHumanId(), human);
354 		}
355 		return this;
356 	}
357 	
358 	public boolean isBot(IToken botId) {
359 		return bots.containsKey(botId);
360 	}
361 	
362 	public boolean isNativeBot(IToken botId) {
363 		return nativeBots.containsKey(botId);
364 	}
365 	
366 	public boolean isHuman(IToken botId) {
367 		return humans.containsKey(botId);
368 	}
369 	
370 	public BotType getBotType(IToken botId) {
371 		if (isBot(botId)) return BotType.BOT;
372 		if (isNativeBot(botId)) return BotType.NATIVE;
373 		if (isHuman(botId)) return BotType.HUMAN;
374 		return null;
375 	}
376 	
377 	public boolean isHumanLikeLogEnabled() {
378 		return humanLikeLogEnabled;
379 	}
380 	
381 	public UT2004MatchConfig setHumanLikeLogEnabled(boolean humanLikeLog) {
382 		this.humanLikeLogEnabled = humanLikeLog;
383 		return this;
384 	}
385 	
386 	//
387 	// VALIDATION
388 	//
389 	
390 	protected final StringBuffer validationBuffer = new StringBuffer();
391 	
392 	protected boolean validationError = false;
393 	
394 	/**
395 	 * Performs validation of the match configuration:
396 	 * <ol>
397 	 * <li>Checks whether the match id is non-null.</li>
398 	 * <li>Checks whether all (custom+native) bots have path-to-jar configured + the file exists.</li>
399 	 * <li>{@link UccWrapper} is not null and its directory exists.</li>
400 	 * <li>Presence of GameBots2004.u as a new game type inside UT2004.</li> 
401 	 * <li>{@link GameBots2004Ini} is not null.</li>
402 	 * <li>Whether there are at least 2 bots defined for the match.</li>
403 	 * </ol>
404 	 * <p><p>
405 	 * Override to provide/add custom validation (might require copy-paste of the code from this method). 
406 	 */
407 	protected void validateInner() {
408 		if (matchId == null) {
409 			validationError = true;
410 			validationBuffer.append(Const.NEW_LINE);
411 			validationBuffer.append("Match ID is NULL");
412 		}
413 		for (UT2004BotConfig config : bots.values()) {
414 			if (config.getBotId() == null) {
415 				validationError = true;
416 				validationBuffer.append(Const.NEW_LINE);
417 				validationBuffer.append("One of custom bots has NULL bot-id.");
418 			}
419 			if (config.getPathToBotJar() == null) {
420 				validationError = true;
421 				validationBuffer.append(Const.NEW_LINE);
422 				validationBuffer.append(config.getBotId() == null ? "One of custom bots" : "Bot " + config.getBotId().getToken() + " has NULL path-to-jar.");
423 			} else {
424 				if (!config.isBotJarExist()) {
425 					validationError = true;
426 					validationBuffer.append(Const.NEW_LINE);
427 					validationBuffer.append(config.getBotId() == null ? "One of custom bots" : "Bot " + config.getBotId().getToken() + " has path-to-jar pointing to non-existing file " + config.getJarFile().getAbsolutePath());
428 				}
429 			}
430 		}
431 		for (UT2004NativeBotConfig config : nativeBots.values()) {
432 			if (config.getBotId() == null) {
433 				validationError = true;
434 				validationBuffer.append(Const.NEW_LINE);
435 				validationBuffer.append("One of native bots has NULL bot-id.");
436 			}			
437 		}
438 		if (gb2004Ini == null) {
439 			validationError = true;
440 			validationBuffer.append(Const.NEW_LINE);
441 			validationBuffer.append("GameBots2004Ini is NULL.");			
442 		}
443 		if (uccConf == null) {
444 			validationError = true;
445 			validationBuffer.append(Const.NEW_LINE);
446 			validationBuffer.append("UccWrapper is NULL.");
447 		} else {
448 			if (uccConf.getUnrealHome() == null) {
449 				validationError = true;
450 				validationBuffer.append(Const.NEW_LINE);
451 				validationBuffer.append("UccWrapper does not have UnrealHome set, is NULL.");
452 			} else {
453 				File uccHome = new File(uccConf.getUnrealHome());
454 				if (!uccHome.exists() || !uccHome.isDirectory()) {
455 					validationError = true;
456 					validationBuffer.append(Const.NEW_LINE);
457 					validationBuffer.append("UccWrapper.UnrealHome does not point to directory, used path: " + uccHome.getAbsolutePath());
458 				} else {
459 					File uccHome2 = new File(uccConf.getUnrealHome() + File.separator + "System");
460 					if (!uccHome2.exists() || !uccHome2.isDirectory()) {
461 						validationError = true;
462 						validationBuffer.append(Const.NEW_LINE);
463 						validationBuffer.append("UccWrapper.UnrealHome" + File.separator + "System does not point to directory, used path: " + uccHome2.getAbsolutePath());
464 					} else { 
465 						File gb = new File(uccConf.getUnrealHome() + File.separator + "System" + File.separator + "GameBots2004.u");
466 						if (!gb.exists() || !gb.isFile()) {
467 							validationError = true;
468 							validationBuffer.append(Const.NEW_LINE);
469 							validationBuffer.append("GameBots2004 was not installed into specified UT2004, the file GameBots2004.u was not found at: " + gb.getAbsolutePath());
470 						}
471 					}
472 				}
473 			}
474 		}
475 		if (bots.size() + nativeBots.size() + humans.size() < 2) {
476 			validationError = true;
477 			validationBuffer.append(Const.NEW_LINE);
478 			validationBuffer.append("There are not enough bot specified for the match. Custom bots: " + bots.size() + ", native bots: " + nativeBots.size() + ", humans: " + humans.size() + ". There must be at least 2 bots/humans to perform the match!");
479 		}
480 	}
481 	
482 	/**
483 	 * Checks the contents, whether everything is set-up correctly, if not, raises an exception with explanation.
484 	 * <p><p>
485 	 * Recalls {@link UT2004MatchConfig#validateInner()} that should fill {@link UT2004MatchConfig#validationBuffer} with
486 	 * messages and if there is a fatal error in the configuration, it should set true to {@link UT2004MatchConfig#validationError}.
487 	 */
488 	public final void validate() {
489 		synchronized(validationBuffer) {
490 			validationError = false;
491 			if (validationBuffer.length() > 0) {
492 				validationBuffer.delete(0, validationBuffer.length());
493 			}
494 			validateInner();
495 			if (validationError) {
496 				throw new PogamutException(this + " validation error!" + validationBuffer.toString(), this);
497 			}
498 		}
499 	}
500 	
501 }