1 package cz.cuni.amis.pogamut.ut2004.tournament.dm.table;
2
3 import java.io.File;
4 import java.io.FileWriter;
5 import java.io.IOException;
6 import java.io.PrintWriter;
7 import java.text.SimpleDateFormat;
8 import java.util.Date;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.logging.Level;
12 import java.util.regex.Pattern;
13
14 import org.apache.commons.io.FileUtils;
15
16 import com.martiansoftware.jsap.FlaggedOption;
17 import com.martiansoftware.jsap.JSAP;
18 import com.martiansoftware.jsap.JSAPException;
19 import com.martiansoftware.jsap.JSAPResult;
20 import com.martiansoftware.jsap.Switch;
21
22 import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
23 import cz.cuni.amis.pogamut.ut2004.tournament.UT2004Ini;
24 import cz.cuni.amis.pogamut.ut2004.tournament.deathmatch.UT2004DeathMatch;
25 import cz.cuni.amis.pogamut.ut2004.tournament.deathmatch.UT2004DeathMatch1v1;
26 import cz.cuni.amis.pogamut.ut2004.tournament.deathmatch.UT2004DeathMatchConfig;
27 import cz.cuni.amis.pogamut.ut2004.tournament.match.UT2004BotConfig;
28
29 public class Main {
30
31 private static final char ARG_UT2004_HOME_DIR_SHORT = 'u';
32
33 private static final String ARG_UT2004_HOME_DIR_LONG = "ut2004-home-dir";
34
35 private static final char ARG_BOT_JARs_SHORT = 'a';
36
37 private static final String ARG_BOT_JARs_LONG = "bot-jars";
38
39 private static final char ARG_BOT_NAMEs_SHORT = 'b';
40
41 private static final String ARG_BOT_NAMEs_LONG = "bot-names";
42
43 private static final char ARG_MAP_NAME_SHORT = 'm';
44
45 private static final String ARG_MAP_NAME_LONG = "map-name";
46
47 private static final char ARG_RESULT_DIR_SHORT = 'r';
48
49 private static final String ARG_RESULT_DIR_LONG = "result-directory";
50
51 private static final char ARG_SERVER_NAME_SHORT = 's';
52
53 private static final String ARG_SERVER_NAME_LONG = "server-name";
54
55 private static final char ARG_FRAG_LIMIT_SHORT = 'f';
56
57 private static final String ARG_FRAG_LIMIT_LONG = "frag-limit";
58
59 private static final char ARG_TIMEOUT_MINUTES_SHORT = 't';
60
61 private static final String ARG_TIMEOUT_MINUTES_LONG = "timeout-minutes";
62
63 private static final char ARG_HUMAN_LIKE_LOG_SHORT = 'h';
64
65 private static final String ARG_HUMAN_LIKE_LOG_LONG = "human-like-log";
66
67 private static final char ARG_UT2004_PORT_SHORT = 'p';
68
69 private static final String ARG_UT2004_PORT_LONG = "ut2004-port";
70
71 private static final char ARG_CONCURRENT_THREADS_SHORT = 'c';
72
73 private static final String ARG_CONCURRENT_THREADS_LONG = "threads";
74
75 private static final char ARG_GENERATE_BATCH_FILES_SHORT = 'g';
76
77 private static final String ARG_GENERATE_BATCH_FILES_LONG = "generate-batch-files";
78
79 private static final char ARG_DEBUG_SHORT = 'd';
80
81 private static final String ARG_DEBUG_LONG = "debug";
82
83 private static final char ARG_CONTINUE_SHORT = 'o';
84
85 private static final String ARG_CONTINUE_LONG = "continue";
86
87 private static final long MATCH_START_INTERLEAVE_MILLIS = 2 * 60 * 1000;
88
89 private static JSAP jsap;
90
91 private static boolean headerOutput = false;
92
93 private static String ut2004HomeDir;
94
95 private static String botJars;
96
97 private static String[] botJarsSeparated;
98
99 private static String botNames;
100
101 private static String[] botNamesSeparated;
102
103 private static File[] botJarFiles;
104
105 private static String map;
106
107 private static String serverName;
108
109 private static String resultDir;
110
111 private static int fragLimit;
112
113 private static int timeoutMinutes;
114
115 private static JSAPResult config;
116
117 private static File ut2004HomeDirFile;
118
119 private static File mapsDirFile;
120
121 private static File mapFile;
122
123 private static File ut2004SystemDirFile;
124
125 private static File ut2004IniFile;
126
127 private static boolean humanLikeLog;
128
129 private static int ut2004Port;
130
131 private static int threadCount;
132
133 private static boolean generateBatchFiles;
134
135 private static boolean debug;
136
137 private static boolean shouldContinue;
138
139 private static void fail(String errorMessage) {
140 fail(errorMessage, null);
141 }
142
143 private static void fail(String errorMessage, Throwable e) {
144 header();
145 System.out.println("ERROR: " + errorMessage);
146 System.out.println();
147 if (e != null) {
148 e.printStackTrace();
149 System.out.println("");
150 }
151 System.out.println("Usage: java -jar ut2004-tournament-dm-table.jar ");
152 System.out.println(" " + jsap.getUsage());
153 System.out.println();
154 System.out.println(jsap.getHelp());
155 System.out.println();
156 throw new RuntimeException("FAILURE: " + errorMessage);
157 }
158
159 private static void header() {
160 if (headerOutput) return;
161 System.out.println();
162 System.out.println("========================================");
163 System.out.println("Pogamut UT2004 DeathMatch Table Executor");
164 System.out.println("========================================");
165 System.out.println();
166 headerOutput = true;
167 }
168
169 private static void info(String msg) {
170 System.out.println("[INFO] " + msg);
171 }
172
173 private static void warning(String msg) {
174 System.out.println("[WARNING] " + msg);
175 }
176
177 private static void severe(String msg) {
178 System.out.println("[SEVERE] " + msg);
179 }
180
181 private static void initJSAP() throws JSAPException {
182 jsap = new JSAP();
183
184 FlaggedOption opt1 = new FlaggedOption(ARG_UT2004_HOME_DIR_LONG)
185 .setStringParser(JSAP.STRING_PARSER)
186 .setRequired(true)
187 .setShortFlag(ARG_UT2004_HOME_DIR_SHORT)
188 .setLongFlag(ARG_UT2004_HOME_DIR_LONG);
189 opt1.setHelp("UT2004 home directory containing GameBots2004 (System/GameBots2004.u) present.");
190
191 jsap.registerParameter(opt1);
192
193 FlaggedOption opt2 = new FlaggedOption(ARG_BOT_JARs_LONG)
194 .setStringParser(JSAP.STRING_PARSER)
195 .setRequired(true)
196 .setShortFlag(ARG_BOT_JARs_SHORT)
197 .setLongFlag(ARG_BOT_JARs_LONG);
198 opt2.setHelp("Semicolon separated PATH/TO/JAR/file1;PATH/TO/JAR/file2 containing executable jars of bots.");
199
200 jsap.registerParameter(opt2);
201
202 FlaggedOption opt3 = new FlaggedOption(ARG_BOT_NAMEs_LONG)
203 .setStringParser(JSAP.STRING_PARSER)
204 .setRequired(true)
205 .setShortFlag(ARG_BOT_NAMEs_SHORT)
206 .setLongFlag(ARG_BOT_NAMEs_LONG);
207 opt3.setHelp("Semicolon separated name1;name2;name3 (ids) that should be given to bots.");
208
209 jsap.registerParameter(opt3);
210
211 FlaggedOption opt6 = new FlaggedOption(ARG_MAP_NAME_LONG)
212 .setStringParser(JSAP.STRING_PARSER)
213 .setRequired(true)
214 .setShortFlag(ARG_MAP_NAME_SHORT)
215 .setLongFlag(ARG_MAP_NAME_LONG);
216 opt6.setHelp("Map where the game should be played (e.g. DM-1on1-Albatross).");
217
218 jsap.registerParameter(opt6);
219
220 FlaggedOption opt8 = new FlaggedOption(ARG_RESULT_DIR_LONG)
221 .setStringParser(JSAP.STRING_PARSER)
222 .setRequired(false)
223 .setShortFlag(ARG_RESULT_DIR_SHORT)
224 .setLongFlag(ARG_RESULT_DIR_LONG)
225 .setDefault(".");
226 opt8.setHelp("PATH/TO/directory where to output results (does not need to exist).");
227
228 jsap.registerParameter(opt8);
229
230 FlaggedOption opt9 = new FlaggedOption(ARG_SERVER_NAME_LONG)
231 .setStringParser(JSAP.STRING_PARSER)
232 .setRequired(false)
233 .setShortFlag(ARG_SERVER_NAME_SHORT)
234 .setLongFlag(ARG_SERVER_NAME_LONG)
235 .setDefault("DMMatch1v1");
236 opt9.setHelp("Server name that should be advertised via LAN.");
237
238 jsap.registerParameter(opt9);
239
240 FlaggedOption opt10 = new FlaggedOption(ARG_FRAG_LIMIT_LONG)
241 .setStringParser(JSAP.INTEGER_PARSER)
242 .setRequired(false)
243 .setShortFlag(ARG_FRAG_LIMIT_SHORT)
244 .setLongFlag(ARG_FRAG_LIMIT_LONG)
245 .setDefault("20");
246 opt10.setHelp("Frag limit for the match.");
247
248 jsap.registerParameter(opt10);
249
250 FlaggedOption opt11 = new FlaggedOption(ARG_TIMEOUT_MINUTES_LONG)
251 .setStringParser(JSAP.INTEGER_PARSER)
252 .setRequired(false)
253 .setShortFlag(ARG_TIMEOUT_MINUTES_SHORT)
254 .setLongFlag(ARG_TIMEOUT_MINUTES_LONG)
255 .setDefault("20");
256 opt11.setHelp("Match timeout in minutes.");
257
258 jsap.registerParameter(opt11);
259
260 Switch opt12 = new Switch(ARG_HUMAN_LIKE_LOG_LONG)
261 .setShortFlag(ARG_HUMAN_LIKE_LOG_SHORT)
262 .setLongFlag(ARG_HUMAN_LIKE_LOG_LONG)
263 .setDefault("false");
264 opt12.setHelp("Whether to produce log for 'HumanLike Project' analysis.");
265
266 jsap.registerParameter(opt12);
267
268 FlaggedOption opt13 = new FlaggedOption(ARG_UT2004_PORT_LONG)
269 .setStringParser(JSAP.INTEGER_PARSER)
270 .setRequired(false)
271 .setShortFlag(ARG_UT2004_PORT_SHORT)
272 .setLongFlag(ARG_UT2004_PORT_LONG)
273 .setDefault("7777");
274 opt13.setHelp("UT2004 port for the dedicated server (1-32000).");
275
276 jsap.registerParameter(opt13);
277
278 FlaggedOption opt14 = new FlaggedOption(ARG_CONCURRENT_THREADS_LONG)
279 .setStringParser(JSAP.INTEGER_PARSER)
280 .setRequired(false)
281 .setShortFlag(ARG_CONCURRENT_THREADS_SHORT)
282 .setLongFlag(ARG_CONCURRENT_THREADS_LONG)
283 .setDefault("1");
284 opt14.setHelp("How many matches should be executed in parallel.");
285
286 jsap.registerParameter(opt14);
287
288 Switch opt15 = new Switch(ARG_GENERATE_BATCH_FILES_LONG)
289 .setShortFlag(ARG_GENERATE_BATCH_FILES_SHORT)
290 .setLongFlag(ARG_GENERATE_BATCH_FILES_LONG);
291 opt15.setHelp("Generate batch file for every match executed (requires ut2004-tournament.jar and java on PATH to execute then).");
292
293 jsap.registerParameter(opt15);
294
295 Switch opt16 = new Switch(ARG_DEBUG_LONG)
296 .setShortFlag(ARG_DEBUG_SHORT)
297 .setLongFlag(ARG_DEBUG_LONG);
298 opt16.setHelp("DEBUG - Whether to output stdout/err of respective executed bots.");
299
300 jsap.registerParameter(opt16);
301
302 Switch opt17 = new Switch(ARG_CONTINUE_LONG)
303 .setShortFlag(ARG_CONTINUE_SHORT)
304 .setLongFlag(ARG_CONTINUE_LONG);
305 opt17.setHelp("Whether to skip matches for which the result folder already exists.");
306
307 jsap.registerParameter(opt17);
308 }
309
310 private static void readConfig(String[] args) {
311 System.out.println("Parsing command arguments.");
312
313 try {
314 config = jsap.parse(args);
315 } catch (Exception e) {
316 fail(e.getMessage());
317 System.out.println("");
318 e.printStackTrace();
319 throw new RuntimeException("FAILURE!");
320 }
321
322 if (!config.success()) {
323 String error = "Invalid arguments specified.";
324 Iterator errorIter = config.getErrorMessageIterator();
325 if (!errorIter.hasNext()) {
326 error += "\n-- No details given.";
327 } else {
328 while (errorIter.hasNext()) {
329 error += "\n-- " + errorIter.next();
330 }
331 }
332 fail(error);
333 }
334
335 ut2004HomeDir = config.getString(ARG_UT2004_HOME_DIR_LONG);
336 botJars = config.getString(ARG_BOT_JARs_LONG);
337 botJarsSeparated = botJars == null ? null : botJars.split(";");
338 botNames = config.getString(ARG_BOT_NAMEs_LONG);
339 botNamesSeparated = botNames == null ? null : botNames.split(";");
340 map = config.getString(ARG_MAP_NAME_LONG);
341 serverName = config.getString(ARG_SERVER_NAME_LONG);
342 resultDir = config.getString(ARG_RESULT_DIR_LONG);
343 fragLimit = config.getInt(ARG_FRAG_LIMIT_LONG);
344 timeoutMinutes = config.getInt(ARG_TIMEOUT_MINUTES_LONG);
345 humanLikeLog = config.getBoolean(ARG_HUMAN_LIKE_LOG_LONG);
346 ut2004Port = config.getInt(ARG_UT2004_PORT_LONG);
347 threadCount = config.getInt(ARG_CONCURRENT_THREADS_LONG);
348 generateBatchFiles = config.getBoolean(ARG_GENERATE_BATCH_FILES_LONG);
349 debug = config.getBoolean(ARG_DEBUG_LONG);
350 shouldContinue = config.getBoolean(ARG_CONTINUE_LONG);
351 }
352
353 private static void sanityChecks() {
354 System.out.println("Sanity checks...");
355
356 ut2004HomeDirFile = new File(ut2004HomeDir);
357 if (!ut2004HomeDirFile.exists() || !ut2004HomeDirFile.isDirectory()) {
358 fail("UT2004 directory was not found at '" + ut2004HomeDirFile.getAbsolutePath() + "', path resolved from configuration read as '" + ut2004HomeDir + "'.");
359 }
360 System.out.println("-- UT2004 directory found at '" + ut2004HomeDirFile.getAbsolutePath() + "'");
361
362 ut2004SystemDirFile = new File(ut2004HomeDirFile, "System");
363 if (!ut2004SystemDirFile.exists() || !ut2004SystemDirFile.isDirectory()) {
364 fail("UT2004/System directory was not found at '" + ut2004SystemDirFile.getAbsolutePath() + "', invalid UT2004 installation.");
365 }
366 System.out.println("-- UT2004/System directory found at '" + ut2004SystemDirFile.getAbsolutePath() + "'");
367
368 ut2004IniFile = new File(ut2004SystemDirFile, "UT2004.ini");
369 if (!ut2004IniFile.exists() || !ut2004IniFile.isFile()) {
370 fail("UT2004/System/UT2004.ini file was not found at '" + ut2004IniFile.getAbsolutePath() + "', invalid UT2004 installation.");
371 }
372 System.out.println("-- UT2004/System/UT2004.ini file found at '" + ut2004IniFile.getAbsolutePath() + "'");
373
374 if (botJarsSeparated == null) {
375 fail("Bot jar(s) was/were not specified correctly.");
376 }
377
378 if (botNamesSeparated == null) {
379 fail("Bot name(s) was/were not specified correctly.");
380 }
381
382 if (botJarsSeparated.length != botNamesSeparated.length) {
383 fail("Bot jar(s) and name(s) numbers mismatch. I've parsed " + botJarsSeparated.length + " bot jar files != " + botNamesSeparated.length + " of bot names.");
384 }
385
386 if (botJarsSeparated.length < 2) {
387 fail("Not enough bot jars/names specified: " + botJarsSeparated.length + " < 2");
388 }
389
390 botJarFiles = new File[botJarsSeparated.length];
391 for (int i = 0; i < botJarsSeparated.length; ++i) {
392 botJarFiles[i] = new File(botJarsSeparated[i]);
393 if (!botJarFiles[i].exists() || !botJarFiles[i].isFile()) {
394 fail("Bot" + (i+1) + " jar file was not found at '"+ botJarFiles[i].getAbsolutePath() + "', path resolved from configuration read as '" + botJarsSeparated[i] + "'.");
395 }
396 System.out.println("-- Bot" + (i+1) + " jar file found at '" + botJarFiles[i].getAbsolutePath() + "'");
397 }
398 System.out.println("-- Bot jars ok");
399
400 for (int i = 0; i < botNamesSeparated.length; ++i) {
401 if (botNamesSeparated[i] == null || botNamesSeparated[i].isEmpty()) {
402 fail("Bot " + (i+1) + " invalid name '" + botNamesSeparated[i] +"' specified.");
403 }
404 System.out.println("-- Bot" + (i+1) + " name set as '" + botNamesSeparated[i] + "'");
405 }
406 System.out.println("-- Bot names ok");
407
408 mapsDirFile = new File(ut2004HomeDirFile, "Maps");
409 if (!mapsDirFile.exists() || !mapsDirFile.isDirectory()) {
410 fail("UT2004/Maps directory was not found at '" + mapsDirFile.getAbsolutePath() + "', invalid UT2004 installation.");
411 }
412 System.out.println("-- UT2004/Maps directory found at '" + mapsDirFile.getAbsolutePath() + "'");
413
414 mapFile = new File(mapsDirFile, map + ".ut2");
415 if (!mapFile.exists() || !mapFile.isFile()) {
416 fail("Specified map '" + map + "' was not found within UT2004/Maps dir at '" + mapFile.getAbsoluteFile() + "', could not execute the match.");
417 }
418 System.out.println("-- Map '" + map + "' found at '" + mapFile.getAbsolutePath() + "'");
419
420 if (serverName == null || serverName.isEmpty()) {
421 fail("Invalid server name '" + serverName + "' specified.");
422 }
423 System.out.println("-- Server name set as '" + serverName + "'");
424
425 if (fragLimit < 1) {
426 fail("Invalid frag limit '" + fragLimit +"' specified, must be >= 1.");
427 }
428 System.out.println("-- Frag limit set as '" + fragLimit + "'");
429
430 if (timeoutMinutes < 1) {
431 fail("Invalid time limit '" + timeoutMinutes +"' specified, must be >= 1.");
432 }
433 System.out.println("-- Timeout set as '" + timeoutMinutes + "' minutes.");
434
435 if (ut2004Port < 1 || ut2004Port > 32000) {
436 fail("Invalid UT2004 port specified '" + ut2004Port + "', must be 1 <= port <= 32000.");
437 }
438 System.out.println("-- UT2004 port set as '" + ut2004Port + "'");
439
440 if (0 > threadCount || threadCount > 20) {
441 fail("Invalid parallel thread count " + threadCount + ", must be 20 >= N > 0.");
442 }
443
444 System.out.println("Sanity checks OK!");
445 }
446
447 private static void setUT2004Ini() {
448 Date date = new Date(System.currentTimeMillis());
449 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
450 File ut2004IniBackup = new File(ut2004SystemDirFile, "UT2004.ini." + sdf.format(date) + ".bak");
451
452 System.out.println("Backing up '" + ut2004IniFile.getAbsolutePath() + "' into '" + ut2004IniBackup.getAbsolutePath() + "' ...");
453 try {
454 FileUtils.copyFile(ut2004IniFile, ut2004IniBackup);
455 } catch (IOException e) {
456 throw new RuntimeException("Failed to backup UT2004.ini", e);
457 }
458
459 System.out.println("Reading '" + ut2004IniFile.getAbsolutePath() + "' ...");
460 List<String> lines;
461 try {
462 lines = FileUtils.readLines(ut2004IniFile);
463 } catch (IOException e) {
464 throw new RuntimeException("Failed to read UT2004.ini", e);
465 }
466 System.out.println("-- " + lines.size() + " lines read.");
467
468 System.out.println("Searching for UT2004 Port and ServerName ...");
469
470
471
472
473 int state = 0;
474
475 Pattern patternSection = Pattern.compile("^\\s*\\[\\s*[^]]*\\s*\\]\\s*$");
476 Pattern patternSectionURL = Pattern.compile("^\\s*\\[\\s*" + UT2004Ini .Section_URL + "\\s*\\]\\s*$");
477 Pattern patternSectionEngineGameReplicationInfo = Pattern.compile("^\\s*\\[\\s*" + UT2004Ini.Section_Engine_GameReplicationInfo + "\\s*\\]\\s*$");
478 Pattern patternPort = Pattern.compile("^\\s*" + UT2004Ini.Key_Port + "\\s*=\\s*.*$");
479 Pattern patternServerName = Pattern.compile("^\\s*" + UT2004Ini.Key_ServerName + "\\s*=\\s*.*$");
480 Pattern patternShortName = Pattern.compile("^\\s*" + UT2004Ini.Key_ShortName + "\\s*=\\s*.*$");
481
482 boolean portFound = false;
483 boolean nameFound = false;
484 boolean shortNameFound = false;
485
486 for (int lineNum = 0;
487
488
489 lineNum <= lines.size()
490
491
492 && (!(portFound && nameFound && shortNameFound));
493 ++lineNum
494 ) {
495
496
497
498 String line = lineNum < lines.size() ? lines.get(lineNum).trim() : null;
499
500
501 if (lineNum < lines.size() && line == null) {
502 continue;
503 }
504
505 if (lineNum == lines.size() || patternSection.matcher(line).matches()) {
506 switch (state) {
507 case 0:
508 break;
509 case 1:
510 if (!portFound) {
511 lines.add(lineNum, UT2004Ini.Key_Port + "=" + ut2004Port);
512 portFound = true;
513 ++lineNum;
514 }
515 break;
516 case 2:
517 if (!nameFound) {
518 lines.add(lineNum, UT2004Ini.Key_ServerName + "=" + serverName);
519 nameFound = true;
520 ++lineNum;
521 }
522 if (!shortNameFound) {
523 lines.add(lineNum, UT2004Ini.Key_ShortName + "=" + serverName);
524 shortNameFound = true;
525 ++lineNum;
526 }
527 break;
528 }
529 if (lineNum == lines.size()) {
530 break;
531 }
532 if (line == null) {
533 continue;
534 }
535 if (patternSectionURL.matcher(line).matches()) {
536 state = 1;
537 } else
538 if (patternSectionEngineGameReplicationInfo.matcher(line).matches()) {
539 state = 2;
540 } else {
541 state = 0;
542 }
543 continue;
544 }
545
546 switch (state) {
547 case 0:
548 continue;
549 case 1:
550 if (!portFound && patternPort.matcher(line).matches()) {
551 lines.set(lineNum, UT2004Ini.Key_Port + "=" + ut2004Port);
552 portFound = true;
553 }
554 continue;
555 case 2:
556 if (!nameFound && patternServerName.matcher(line).matches()) {
557 lines.set(lineNum, UT2004Ini.Key_ServerName + "=" + serverName);
558 nameFound = true;
559 } else
560 if (!shortNameFound && patternShortName.matcher(line).matches()) {
561 lines.set(lineNum, UT2004Ini.Key_ShortName + "=" + serverName);
562 shortNameFound = true;
563 }
564 continue;
565 default:
566 continue;
567 }
568 }
569
570 if (!portFound) {
571 throw new RuntimeException("Failed to set UT2004 port!");
572 }
573 if (!nameFound) {
574 throw new RuntimeException("Failed to set UT2004 ServerName!");
575 }
576
577 System.out.println("Rewriting '" + ut2004IniFile.getAbsolutePath() + "' ...");
578 try {
579 FileUtils.writeLines(ut2004IniFile, lines);
580 } catch (IOException e) {
581 throw new RuntimeException("Failed to write UT2004.ini", e);
582 }
583
584 System.out.println("UT2004 Port and ServerName set.");
585 }
586
587 private static void generateBatchFile(String matchName, String bot1Jar, String bot1Id, String bot2Jar, String bot2Id) {
588 String fileName = "match-" + bot1Id + "-vs-" + bot2Id + ".bat";
589 File file = new File(fileName);
590 if (file.exists()) {
591 if (file.isFile()) {
592 warning("Batch file already exists, skipping: " + file.getAbsolutePath());
593 } else {
594 severe("Batch file already exists, but it is not file, skipping: " + file.getAbsolutePath());
595 }
596 return;
597 }
598 try {
599 FileWriter writer = new FileWriter(file);
600 PrintWriter print = new PrintWriter(writer);
601
602 print.println("java -jar ut2004-tournament.jar ^");
603 print.println(" -u " + ut2004HomeDir + " ^");
604 print.println(" -a " + bot1Jar + ";" + bot2Jar + " ^");
605 print.println(" -b " + bot1Id + ";" + bot2Id + " ^");
606 print.println(" -m " + map + " ^");
607 print.println(" -r " + resultDir + " ^");
608 print.println(" -n " + matchName + " ^");
609 print.println(" -s DMServer-" + matchName + " ^");
610 print.println(" -f " + fragLimit + " ^");
611 print.println(" -t " + timeoutMinutes);
612
613 try {
614 print.close();
615 } catch (Exception e) {
616 }
617 try {
618 writer.close();
619 } catch (Exception e) {
620 }
621 } catch (Exception e) {
622
623 }
624 }
625
626 private static String getMatchName(String bot1Id, String bot2Id) {
627 return "Match-" + bot1Id + "-vs-" + bot2Id;
628 }
629
630 private static void generateBatchFiles() {
631 for (int i = 0; i < botJarsSeparated.length; ++i) {
632 String bot1Id = botNamesSeparated[i];
633 String bot1Jar = botJarsSeparated[i];
634 for (int j = i+1; j < botJarsSeparated.length; ++j) {
635 String bot2Id = botNamesSeparated[j];
636 String bot2Jar = botJarsSeparated[j];
637 String matchName = getMatchName(bot1Id, bot2Id);
638 generateBatchFile(matchName, bot1Jar, bot1Id, bot2Jar, bot2Id);
639 }
640 }
641 }
642
643 private static UT2004DeathMatch executeMatch(Match match) {
644 UT2004DeathMatchConfig config = new UT2004DeathMatchConfig();
645
646 UT2004BotConfig bot1Config = new UT2004BotConfig();
647 bot1Config.setBotId(match.bot1Id);
648 bot1Config.setPathToBotJar(match.bot1Jar);
649 bot1Config.setRedirectStdErr(debug);
650 bot1Config.setRedirectStdOut(debug);
651
652 UT2004BotConfig bot2Config = new UT2004BotConfig();
653 bot2Config.setBotId(match.bot2Id);
654 bot2Config.setPathToBotJar(match.bot2Jar);
655 bot2Config.setRedirectStdErr(debug);
656 bot2Config.setRedirectStdOut(debug);
657
658 config.setBot(bot1Config, bot2Config);
659 config.setOutputDirectory(new File(resultDir));
660 config.setMatchId(match.matchName);
661 config.setFragLimit(fragLimit);
662 config.setTimeLimit(timeoutMinutes);
663 config.setHumanLikeLogEnabled(humanLikeLog);
664
665 config.getUccConf().setStartOnUnusedPort(true);
666 config.getUccConf().setUnrealHome(ut2004HomeDir);
667 config.getUccConf().setGameType("BotDeathMatch");
668 config.getUccConf().setMapName(map);
669
670 System.out.println("EXECUTING MATCH!");
671
672 LogCategory log = new LogCategory(match.matchName);
673 UT2004DeathMatch ut2004Match = new UT2004DeathMatch(config, log);
674
675 ut2004Match.getLog().setLevel(Level.INFO);
676 ut2004Match.getLog().addConsoleHandler();
677
678 ut2004Match.run();
679
680 return ut2004Match;
681 }
682
683 private static class Match {
684
685 String matchName;
686 String bot1Jar;
687 String bot1Id;
688 String bot2Jar;
689 String bot2Id;
690
691 public Match(String matchName, String bot1Jar, String bot1Id, String bot2Jar, String bot2Id) {
692 super();
693 this.matchName = matchName;
694 this.bot1Jar = bot1Jar;
695 this.bot1Id = bot1Id;
696 this.bot2Jar = bot2Jar;
697 this.bot2Id = bot2Id;
698 }
699 }
700
701 private static class ExecuteOne extends Thread {
702
703 private Match match;
704
705 private UT2004DeathMatch result;
706
707 public boolean done = false;
708
709 public boolean success = true;
710
711 public ExecuteOne(Match match) {
712 super(match.matchName);
713 this.match = match;
714 }
715
716 @Override
717 public void run() {
718 try {
719 info("STARTING: " + match.matchName);
720 result = executeMatch(match);
721 } catch (Exception e) {
722 success = false;
723 } finally {
724 done = true;
725 if (success) {
726 info("ENDED-SUCCESS: " + match.matchName);
727 } else {
728 info("ENDED-FAILURE: " + match.matchName);
729 }
730 }
731 }
732
733 }
734
735 private static void executeInParallel(Match[] matches, int threadCount) throws InterruptedException {
736 int currentMatch = 0;
737 int liveThreads = 0;
738
739 int successes = 0;
740 int skipped = 0;
741
742 ExecuteOne[] threads = new ExecuteOne[threadCount];
743
744 while (currentMatch < matches.length || liveThreads > 0) {
745
746 for (int i = 0; i < threads.length; ++i) {
747 if (threads[i] != null) {
748 if (!threads[i].done) {
749
750 continue;
751 }
752
753 --liveThreads;
754
755 if (threads[i].success) {
756 ++successes;
757
758 }
759
760 threads[i] = null;
761 }
762
763 assert(threads[i] == null);
764
765 if (shouldContinue) {
766 while (currentMatch < matches.length) {
767 Match match = matches[currentMatch];
768 File resultFolder = new File(new File(resultDir), match.matchName);
769 if (resultFolder.exists() && resultFolder.isDirectory()) {
770 warning("Result folder for the match " + match.matchName + " already exists, skipping this match.");
771 ++skipped;
772 } else {
773 break;
774 }
775 ++currentMatch;
776 }
777 }
778
779 if (currentMatch < matches.length) {
780 threads[i] = new ExecuteOne(matches[currentMatch]);
781 ++currentMatch;
782 ++liveThreads;
783 threads[i].start();
784 Thread.sleep(MATCH_START_INTERLEAVE_MILLIS);
785 }
786 }
787
788 Thread.sleep(1000);
789 }
790
791 if (successes == matches.length - skipped) {
792 info("ALL SUCCEEDED!");
793 if (shouldContinue) {
794 info("Succeeded: " + successes + ", Skipped: " + skipped);
795 }
796 } else {
797 severe("Some matches failed, only " + successes + "/" + (matches.length - skipped) + " has succeed.");
798 if (shouldContinue) {
799 severe("Skipped: " + skipped);
800 }
801 }
802 }
803
804 private static void executeMatches() throws InterruptedException {
805
806 int matchCount = botJarsSeparated.length * (botJarsSeparated.length-1) / 2;
807 Match[] matches = new Match[matchCount];
808
809 int k = 0;
810 for (int i = 0; i < botJarsSeparated.length-1; ++i) {
811 String bot1Id = botNamesSeparated[i];
812 String bot1Jar = botJarsSeparated[i];
813
814 for (int j = i+1; j < botJarsSeparated.length; ++j) {
815 String bot2Id = botNamesSeparated[j];
816 String bot2Jar = botJarsSeparated[j];
817
818 String matchName = getMatchName(bot1Id, bot2Id);
819
820 matches[k] = new Match(matchName, bot1Jar, bot1Id, bot2Jar, bot2Id);
821
822 ++k;
823 }
824 }
825
826 executeInParallel(matches, threadCount);
827 }
828
829 public static void main(String[] args) throws JSAPException {
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856 initJSAP();
857
858 header();
859
860 readConfig(args);
861
862 sanityChecks();
863
864 if (generateBatchFiles) {
865 generateBatchFiles();
866 }
867
868 setUT2004Ini();
869
870 try {
871 executeMatches();
872 } catch (Exception e) {
873 fail("ERROR", e);
874 }
875 }
876
877 }