1 package cz.cuni.amis.pogamut.ut2004.tournament.deathmatch;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.Comparator;
8 import java.util.Formatter;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.concurrent.Callable;
13 import java.util.logging.Level;
14 import java.util.logging.LogRecord;
15
16 import cz.cuni.amis.pogamut.base.utils.logging.ILogPublisher;
17 import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
18 import cz.cuni.amis.pogamut.ut2004.tournament.match.UT2004Match;
19 import cz.cuni.amis.pogamut.ut2004.tournament.match.UT2004MatchConfig;
20 import cz.cuni.amis.utils.FilePath;
21 import cz.cuni.amis.utils.NullCheck;
22 import cz.cuni.amis.utils.exception.PogamutException;
23 import cz.cuni.amis.utils.exception.PogamutIOException;
24 import cz.cuni.amis.utils.maps.HashMapMap;
25 import cz.cuni.amis.utils.maps.LazyMap;
26 import cz.cuni.amis.utils.token.IToken;
27 import cz.cuni.amis.utils.token.Token;
28 import cz.cuni.amis.utils.token.Tokens;
29
30
31
32
33
34
35
36
37
38 public class UT2004DeathMatchRepeater implements Callable<List<UT2004DeathMatchResult>>, Runnable {
39
40 protected UT2004DeathMatchConfig matchConfig;
41 protected int repeats;
42
43 protected List<UT2004DeathMatchResult> results = new ArrayList<UT2004DeathMatchResult>();
44 protected List<Throwable> exceptions = new ArrayList<Throwable>();
45 private LogCategory log;
46
47
48
49
50
51 public UT2004DeathMatchRepeater() {
52 }
53
54
55
56
57
58 public UT2004DeathMatchRepeater(LogCategory log) {
59 this.log = log;
60 }
61
62 public UT2004DeathMatchRepeater(UT2004DeathMatchConfig match, int repeats, LogCategory log) {
63 NullCheck.check(match, "match");
64 this.matchConfig = match;
65 this.repeats = repeats;
66 if (this.repeats < 0) {
67 throw new IllegalArgumentException("repeats = " + repeats + " < 0, can't be!");
68 }
69 this.log = log;
70 }
71
72 protected String getNum(int i, int max) {
73 String result = String.valueOf(i);
74 String maxStr = String.valueOf(max);
75 while (result.length() < maxStr.length()) {
76 result = "0" + result;
77 }
78 return result;
79 }
80
81 protected Token getToken(IToken orig, int i, int max) {
82 return Tokens.get(orig.getToken() + "-" + getNum(i+1, max) + "_of_" + max);
83 }
84
85
86
87
88
89 public LogCategory getLog() {
90 return log;
91 }
92
93 public void setLog(LogCategory log) {
94 this.log = log;
95 }
96
97
98
99
100
101 public UT2004DeathMatchConfig getMatchConfig() {
102 return matchConfig;
103 }
104
105 public void setMatchConfig(UT2004DeathMatchConfig matchConfig) {
106 this.matchConfig = matchConfig;
107 }
108
109
110
111
112
113 public int getRepeats() {
114 return repeats;
115 }
116
117 public void setRepeats(int repeats) {
118 this.repeats = repeats;
119 }
120
121
122
123
124
125
126
127
128
129
130 public List<Throwable> getExceptions() {
131 return Collections.unmodifiableList(exceptions);
132 }
133
134
135
136
137
138
139
140
141
142
143 public List<UT2004DeathMatchResult> getResults() {
144 return Collections.unmodifiableList(results);
145 }
146
147 @Override
148 public List<UT2004DeathMatchResult> call() throws Exception {
149 call();
150 return getResults();
151 }
152
153 @Override
154 public void run() {
155 if (this.matchConfig == null) {
156 throw new PogamutException("No match set into the repeater!", this);
157 }
158 if (this.repeats < 0) {
159 throw new PogamutException("repeats = " + repeats + " < 0, can't be!", this);
160 }
161 IToken token = matchConfig.getMatchId();
162 for (int i = 0; i < repeats; ++i) {
163 if (log != null && log.isLoggable(Level.INFO)) {
164 log.info("Running " + token.getToken() + " match " + (i+1) + " / " + repeats + " ...");
165 }
166 matchConfig.setMatchId(getToken(token, i, repeats));
167 UT2004DeathMatch match = new UT2004DeathMatch(matchConfig, new LogCategory(matchConfig.getMatchId().getToken()));
168 match.getLog().addHandler(new ILogPublisher() {
169 @Override
170 public void close() throws SecurityException {
171 }
172 @Override
173 public void flush() {
174 }
175 @Override
176 public void publish(LogRecord record) {
177 if (UT2004DeathMatchRepeater.this.log != null) {
178 UT2004DeathMatchRepeater.this.log.log(record);
179 }
180 }
181 });
182 match.cleanUp();
183 boolean exception = false;
184 UT2004DeathMatchResult result = null;
185 try {
186 result = (UT2004DeathMatchResult) match.call();
187 } catch (Exception e) {
188 exception = true;
189 results.add(null);
190 exceptions.add(e);
191 }
192 if (!exception) {
193 results.add(result);
194 exceptions.add(null);
195 }
196 }
197 matchConfig.setMatchId(token);
198 outputAggregatedResults();
199 }
200
201 protected void outputAggregatedResults() {
202 if (log != null && log.isLoggable(Level.FINE)) {
203 log.fine(matchConfig.getMatchId().getToken() + ": Outputting aggregated match results into CSV files...");
204 }
205 IToken token = matchConfig.getMatchId();
206 File outputDirectory = new File(matchConfig.getOutputDirectory().getAbsolutePath() + File.separator + matchConfig.getMatchId().getToken() + "-results");
207 outputDirectory.mkdirs();
208 outputAggregatedResults(outputDirectory);
209 if (log != null && log.isLoggable(Level.INFO)) {
210 UT2004MatchConfig config = matchConfig;
211 log.info(config.getMatchId().getToken() + ": Aggregated match results output into CSV files.");
212 }
213 }
214
215 protected void outputAggregatedResults(File outputDirectory) {
216 UT2004MatchConfig config = matchConfig;
217
218
219
220 List<IToken> botIds = config.getAllBotIds();
221
222
223
224 Map<IToken, Integer> wins = new LazyMap<IToken, Integer>() {
225 @Override
226 protected Integer create(IToken key) { return 0; }
227 };
228 Map<IToken, Integer> draws = new LazyMap<IToken, Integer>() {
229 @Override
230 protected Integer create(IToken key) { return 0; }
231 };
232
233 int matchFinished = 0;
234 for (UT2004DeathMatchResult result : results) {
235 if (result == null) continue;
236 ++matchFinished;
237 if (result.isDraw()) {
238 for (IToken botId : botIds) {
239 draws.put(botId, 1+draws.get(botId));
240 }
241 } else {
242 wins.put(result.getWinnerBot(), 1+wins.get(result.getWinnerBot()));
243 }
244 }
245
246
247
248 HashMapMap<IToken, IToken, Integer> kills = new HashMapMap<IToken, IToken, Integer>();
249 HashMap<IToken, Integer> scores = new HashMap<IToken, Integer>();
250 HashMap<IToken, Integer> deaths = new HashMap<IToken, Integer>();
251 HashMap<IToken, Integer> totalKills = new HashMap<IToken, Integer>();
252 HashMap<IToken, Integer> totalKilled = new HashMap<IToken, Integer>();
253 for (IToken botId1 : botIds) {
254 scores.put(botId1, 0);
255 deaths.put(botId1, 0);
256 totalKills.put(botId1, 0);
257 for (IToken botId2 : botIds) {
258 kills.put(botId1, botId2, 0);
259 }
260 }
261 for (UT2004DeathMatchResult result : results) {
262 if (result == null) continue;
263 for (IToken botId1 : botIds) {
264 scores.put(botId1, scores.get(botId1) + result.getFinalScores().get(botId1).getScore());
265 deaths.put(botId1, deaths.get(botId1) + result.getFinalScores().get(botId1).getDeaths());
266 totalKills.put(botId1, totalKills.get(botId1) + result.getTotalKills().get(botId1));
267 for (IToken botId2 : botIds) {
268 kills.put(botId1, botId2, kills.get(botId1, botId2) + result.getKillCounts().get(botId1, botId2));
269 }
270 }
271 }
272
273
274
275
276
277 File resultFile = new File(outputDirectory.getAbsolutePath() + File.separator + "match-" + repeats + "x-" + config.getMatchId().getToken() + "-scores-aggregated.csv");
278 FilePath.makeDirsToFile(resultFile);
279 try {
280 Formatter writer = new Formatter(resultFile);
281 writer.format("botId;matches;matchFinished;win;winRatio;draw;drawRatio;lose;loseRatio;score;scoreAvg;kills;killsAvg;killedByOthers;killedByOthersAvg;deaths;deathsAvg;suicides;suicidesAvg");
282 if (matchFinished > 0) {
283 for (IToken botId : botIds) {
284 writer.format(";");
285 writer.format(botId.getToken());
286 writer.format(";");
287 writer.format(botId.getToken() + "Avg");
288 }
289 for (IToken botId : botIds) {
290 writer.format
291 (
292 "\n%s;%d;%d;%d;%.3f;%d;%.3f;%d;%.3f;%d;%.3f;%d;%.3f;%d;%.3f;%d;%.3f;%d;%.3f",
293
294 botId.getToken(),
295
296 repeats,
297
298 matchFinished,
299
300 wins.get(botId),
301 ((double)wins.get(botId)) / ((double)matchFinished),
302
303 draws.get(botId),
304 ((double)draws.get(botId)) / ((double)matchFinished),
305
306 matchFinished - wins.get(botId) - draws.get(botId),
307 ((double)(matchFinished - wins.get(botId) - draws.get(botId))) / ((double)matchFinished),
308
309 scores.get(botId),
310 ((double)scores.get(botId))/((double)matchFinished),
311
312 totalKills.get(botId),
313 ((double)totalKills.get(botId))/matchFinished,
314
315 (deaths.get(botId) - kills.get(botId, botId)),
316 ((double)(deaths.get(botId) - kills.get(botId, botId))) / ((double)matchFinished),
317
318 deaths.get(botId),
319 ((double)deaths.get(botId)) / ((double)matchFinished),
320
321 kills.get(botId, botId),
322 ((double)kills.get(botId, botId)) /((double)matchFinished)
323 );
324 for (IToken botId2 : botIds) {
325 writer.format(";%d", kills.get(botId).get(botId2));
326 writer.format(";%.3f", ((double)kills.get(botId).get(botId2))/((double)matchFinished));
327 }
328 }
329 } else {
330 writer.format("NO MATCH FINISHED, ALL HAVE ENDED WITH AN EXCEPTION!");
331 }
332 try {
333 writer.close();
334 } catch (Exception e) {
335 }
336 } catch (IOException e) {
337 throw new PogamutIOException("Failed to write results!", e, log, this);
338 }
339
340
341
342
343
344 for (IToken botId1 : botIds) {
345 File botFile = new File(outputDirectory.getAbsolutePath() + File.separator + "match-" + repeats + "x-" + config.getMatchId().getToken() + "-scores-" + botId1.getToken() + ".csv");
346 FilePath.makeDirsToFile(botFile);
347 try {
348 Formatter writer = new Formatter(botFile);
349 writer.format("match;matches;matchFinished;score;kills;killedByOthers;deaths;suicides");
350 for (IToken botId2 : botIds) {
351 writer.format(";");
352 writer.format(botId2.getToken());
353 }
354 int i = 0;
355 for (UT2004DeathMatchResult result : results) {
356 ++i;
357 if (result == null) continue;
358 writer.format
359 (
360 "\n%d;%d;%d;%d;%d;%d;%d;%d",
361
362 i,
363
364 repeats,
365
366 matchFinished,
367
368 result.getFinalScores().get(botId1).getScore(),
369
370 result.getTotalKills().get(botId1),
371
372 result.getWasKilled().get(botId1),
373
374 result.getFinalScores().get(botId1).getDeaths(),
375
376 result.getSuicides().get(botId1)
377 );
378 for (IToken botId2 : botIds) {
379 writer.format(";%d", result.getKillCounts().get(botId1).get(botId2));
380 }
381 }
382 try {
383 writer.close();
384 } catch (Exception e) {
385 }
386 } catch (IOException e) {
387 throw new PogamutIOException("Failed to write results!", e, log, this);
388 }
389 }
390
391
392
393
394
395 List<IToken> customBots = new ArrayList<IToken>(config.getBots().keySet());
396 Collections.sort(customBots, new Comparator<IToken>() {
397 @Override
398 public int compare(IToken o1, IToken o2) {
399 return o1.getToken().compareTo(o2.getToken());
400 }
401 });
402
403 if (results.size() > 0) {
404 for (IToken botId1 : customBots) {
405 File botFile = new File(outputDirectory.getAbsolutePath() + File.separator + "match-" + repeats + "x-" + config.getMatchId().getToken() + "-stats-" + botId1.getToken() + ".csv");
406 FilePath.makeDirsToFile(botFile);
407 try {
408 Formatter writer = new Formatter(botFile);
409 writer.format("match;");
410 results.get(0).getBotObservers().get(botId1).getStats().outputHeader(writer);
411 int i = 0;
412 for (UT2004DeathMatchResult result : results) {
413 ++i;
414 if (result == null) continue;
415 writer.format("%d;", i);
416 result.getBotObservers().get(botId1).getStats().outputStatLine(writer, result.getMatchTimeEnd());
417 }
418 try {
419 writer.close();
420 } catch (Exception e) {
421 }
422 } catch (IOException e) {
423 throw new PogamutIOException("Failed to write results!", e, log, this);
424 }
425 }
426 }
427 }
428
429
430
431 }