summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom@tomsmeding.com>2021-10-17 20:43:58 +0200
committerTom Smeding <tom@tomsmeding.com>2021-10-17 20:43:58 +0200
commit838b28a526e3c588f75cc00675003c16438518f1 (patch)
treefc496a26507d70146bf71026f73b755284edac46
parentc7e21669027d12c418f3b0f4fd0e2acd92d65806 (diff)
Allow running only a single gameHEADmaster
-rw-r--r--main.cpp260
1 files changed, 198 insertions, 62 deletions
diff --git a/main.cpp b/main.cpp
index 4adc6bc..4f688aa 100644
--- a/main.cpp
+++ b/main.cpp
@@ -166,10 +166,15 @@ MatchResult MatchResult::inverted() const {
return r;
}
+enum class Mode {
+ competition,
+ single,
+};
+
struct Params {
+ Mode mode;
bool referee_verbose = false;
string refereePath;
- int num_matches = 5;
size_t timeout_msec = 10000;
};
@@ -244,33 +249,17 @@ static void recordResult(Player &p1, Player &p2, const MatchResult &result) {
}
}
-static void playMatch(MultiLog &multiLog, Player &p1, Player &p2, int index, const Params &params) {
- int logId = multiLog.add(p1.fname + " - " + p2.fname + ": ");
-
- if (optional<MatchResult> optres = readMatchCache(p1, p2, index)) {
- multiLog.append(logId, optres->describe(p1, p2) + " (cached)");
- multiLog.complete(logId);
- recordResult(p1, p2, *optres);
- return;
- }
-
+static MatchResult playMatch(const Player &p1, const Player &p2, const Params &params, const string &gamecode, ostream &gamelog, const string &p1logpath, const string &p2logpath) {
MatchResult mres;
- string gamelog_path = gameLogPath(p1, p2, index);
- ofstream gamelog(gamelog_path);
- if (!gamelog) {
- cout << "ERROR opening game log file " << gamelog_path << endl;
- throw StopCompetitionError();
- }
-
Referee referee(params.referee_verbose, params.refereePath, {p1.fname, p2.fname});
gamelog << "Player 1: " << p1.fname << "\n"
<< "Player 2: " << p2.fname << "\n\n" << flush;
Process procs[2] = {Process(p1.fname), Process(p2.fname)};
- procs[0].redirectStderr(playerLogPath(p1, p2, index, 1));
- procs[1].redirectStderr(playerLogPath(p1, p2, index, 2));
+ procs[0].redirectStderr(p1logpath);
+ procs[1].redirectStderr(p2logpath);
procs[0].run();
procs[1].run();
@@ -286,7 +275,7 @@ static void playMatch(MultiLog &multiLog, Player &p1, Player &p2, int index, con
optional<string> oline = proc.readLine();
if (!oline) {
cout << "ERROR reading move from player " << readEvent->player + 1
- << " (game " << gameCodeName(p1, p2, index) << ")" << endl;
+ << " (game " << gamecode << ")" << endl;
cout << "(process exit code: " << proc.waitPoll() << ")" << endl;
throw StopCompetitionError();
}
@@ -306,7 +295,7 @@ static void playMatch(MultiLog &multiLog, Player &p1, Player &p2, int index, con
} else if (auto writeEvent = get_if<Referee::WriteEvent>(&event)) {
if (!procs[writeEvent->player].writeLine(writeEvent->line, writeEvent->allowBrokenPipe)) {
cout << "ERROR writing move to player " << writeEvent->player + 1
- << " (game " << gameCodeName(p1, p2, index) << ")" << endl;
+ << " (game " << gamecode << ")" << endl;
throw StopCompetitionError();
}
@@ -324,7 +313,7 @@ static void playMatch(MultiLog &multiLog, Player &p1, Player &p2, int index, con
} else if (auto errorEvent = get_if<Referee::ErrorEvent>(&event)) {
cout << "ERROR in player " << errorEvent->player + 1 << ": " << errorEvent->message
- << " (game " << gameCodeName(p1, p2, index) << ")" << endl;
+ << " (game " << gamecode << ")" << endl;
gamelog << endl
<< "ERROR in P" << errorEvent->player + 1 << ": " << errorEvent->message << endl;
throw StopCompetitionError();
@@ -343,64 +332,154 @@ static void playMatch(MultiLog &multiLog, Player &p1, Player &p2, int index, con
}
}
- multiLog.append(logId, mres.describe(p1, p2));
- multiLog.complete(logId);
-
gamelog << "\nResult: " << mres.describe(p1, p2) << "\n"
<< "P1 took " << mres.ms1 / 1000000.0 << " seconds\n"
<< "P2 took " << mres.ms2 / 1000000.0 << " seconds\n" << flush;
+ return mres;
+}
+
+static void playMatchInCompetition(MultiLog &multiLog, Player &p1, Player &p2, int index, const Params &params) {
+ const int logId = multiLog.add(p1.fname + " - " + p2.fname + ": ");
+
+ if (optional<MatchResult> optres = readMatchCache(p1, p2, index)) {
+ multiLog.append(logId, optres->describe(p1, p2) + " (cached)");
+ multiLog.complete(logId);
+ recordResult(p1, p2, *optres);
+ return;
+ }
+
+ const string gamelog_path = gameLogPath(p1, p2, index);
+ ofstream gamelog(gamelog_path);
+ if (!gamelog) {
+ cout << "ERROR opening game log file " << gamelog_path << endl;
+ throw StopCompetitionError();
+ }
+
+ const MatchResult mres = playMatch(
+ p1, p2, params,
+ gameCodeName(p1, p2, index),
+ gamelog,
+ playerLogPath(p1, p2, index, 1),
+ playerLogPath(p1, p2, index, 2)
+ );
+
+ multiLog.append(logId, mres.describe(p1, p2));
+ multiLog.complete(logId);
+
writeMatchCache(p1, p2, index, mres);
recordResult(p1, p2, mres);
}
-static void playerPit(Scheduler &scheduler, MultiLog &multiLog, Player &p1, Player &p2, const Params &params) {
- for (int i = 0; i < params.num_matches; i++) {
- scheduler.submit([i, &p1, &p2, &params, &multiLog]() {
- playMatch(multiLog, p1, p2, i + 1, params);
+struct CompParams {
+ Scheduler *scheduler;
+ MultiLog *multiLog;
+ int num_matches = 5;
+};
+
+static void playerPit(CompParams &compParams, Player &p1, Player &p2, const Params &params) {
+ for (int i = 0; i < compParams.num_matches; i++) {
+ compParams.scheduler->submit([i, &p1, &p2, &params, &compParams]() {
+ playMatchInCompetition(*compParams.multiLog, p1, p2, i + 1, params);
});
}
}
-static void fullCompetition(Scheduler &scheduler, MultiLog &multiLog, vector<Player> &players, const Params &params) {
+static void fullCompetition(CompParams compParams, vector<Player> &players, const Params &params) {
for (size_t p1i = 0; p1i < players.size(); p1i++) {
for (size_t p2i = p1i + 1; p2i < players.size(); p2i++) {
- playerPit(scheduler, multiLog, players[p1i], players[p2i], params);
- playerPit(scheduler, multiLog, players[p2i], players[p1i], params);
+ playerPit(compParams, players[p1i], players[p2i], params);
+ playerPit(compParams, players[p2i], players[p1i], params);
}
}
}
static void usage(const char *argv0) {
- cerr << "Usage: " << argv0 << " [OPTIONS] <referee> <players...>\n"
+ cerr <<
+ "Usage: " << argv0 << " comp [OPTIONS] <referee> <players...>\n"
+ "Run a full competition. Gamelogs in " << gamelogdir << ", playerlogs in\n"
+ << playerlogdir << ". Caches in " << matchcachedir << "; already-played games\n"
+ "as determined by caches will not be run again.\n"
" --noansi Don't use fancy back-updating log output\n"
" --vs Verbose scheduler output (use with --noansi)\n"
- " --vr Verbose referee-handling output (use with --noansi)\n"
" -j <NUM> Set number of parallel games (default ncores/2)\n"
" -n <NUM> Set number of matches per player pairing (default "
- << Params{}.num_matches << ")\n"
+ << CompParams{}.num_matches << ")\n"
+ "\n"
+ "Usage: " << argv0 << " single [OPTIONS] <referee> <player1> <player2>\n"
+ "Run only a single game. Gamelog from referee is sent to stdout.\n"
+ " --log1 <FILE> Playerlog file for player 1\n"
+ " --log2 <FILE> Playerlog file for player 2\n"
+ "\n"
+ "Options that always apply:\n"
+ " --vr Verbose referee-handling output (use with --noansi if comp mode)\n"
" -T <MSEC> Set player timeout in milliseconds (default "
- << Params{}.timeout_msec << ")\n";
+ << Params{}.timeout_msec << ")\n"
+ ;
}
-int main(int argc, char **argv) {
+static Params parseCommonOptions(vector<const char*> &argv) {
Params params;
+
+ if (argv.size() <= 1) {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ if (strcmp(argv[1], "comp") == 0) params.mode = Mode::competition;
+ else if (strcmp(argv[1], "single") == 0) params.mode = Mode::single;
+ else {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ argv.erase(argv.begin() + 1);
+
+ for (size_t i = 1; i < argv.size(); i++) {
+ if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
+ usage(argv[0]);
+ exit(0);
+ } else if (strcmp(argv[i], "--vr") == 0) {
+ params.referee_verbose = true;
+ argv.erase(argv.begin() + i);
+ i--;
+ } else if (strcmp(argv[i], "-T") == 0 && i + 1 < argv.size()) {
+ char *endp;
+ params.timeout_msec = strtoull(argv[i+1], &endp, 10);
+ if (!argv[i][0] || *endp || params.timeout_msec <= 0) {
+ cerr << "Invalid number to -T" << endl;
+ exit(1);
+ }
+ argv.erase(argv.begin() + i, argv.begin() + i + 2);
+ i--;
+ } else if (strcmp(argv[i], "-j") == 0 ||
+ strcmp(argv[i], "-n") == 0 ||
+ (strlen(argv[i]) >= strlen("--log1") &&
+ memcmp(argv[i], "--log", strlen("--log")) == 0)) {
+ i++; // skip argument of option
+ } else if (params.refereePath.empty()) {
+ params.refereePath = argv[i];
+ argv.erase(argv.begin() + i);
+ i--;
+ }
+ }
+
+ return params;
+}
+
+static int mainCompetition(const vector<const char*> &argv, const Params &params) {
vector<Player> players;
bool multilog_fancy = true;
bool scheduler_verbose = false;
int num_threads = std::thread::hardware_concurrency() / 2;
+ CompParams compParams;
- for (int i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
- usage(argv[0]);
- return 0;
- } else if (strcmp(argv[i], "--noansi") == 0) {
+ for (size_t i = 1; i < argv.size(); i++) {
+ if (strcmp(argv[i], "--noansi") == 0) {
multilog_fancy = false;
} else if (strcmp(argv[i], "--vs") == 0) {
scheduler_verbose = true;
- } else if (strcmp(argv[i], "--vr") == 0) {
- params.referee_verbose = true;
- } else if (strcmp(argv[i], "-j") == 0 && i + 1 < argc) {
+ } else if (strcmp(argv[i], "-j") == 0 && i + 1 < argv.size()) {
char *endp;
num_threads = strtol(argv[i+1], &endp, 10);
if (!argv[i][0] || *endp || num_threads <= 0) {
@@ -408,27 +487,17 @@ int main(int argc, char **argv) {
return 1;
}
i++;
- } else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
+ } else if (strcmp(argv[i], "-n") == 0 && i + 1 < argv.size()) {
char *endp;
- params.num_matches = strtol(argv[i+1], &endp, 10);
- if (!argv[i][0] || *endp || params.num_matches <= 0) {
+ compParams.num_matches = strtol(argv[i+1], &endp, 10);
+ if (!argv[i][0] || *endp || compParams.num_matches <= 0) {
cerr << "Invalid number to -n" << endl;
return 1;
}
i++;
- } else if (strcmp(argv[i], "-T") == 0 && i + 1 < argc) {
- char *endp;
- params.timeout_msec = strtoull(argv[i+1], &endp, 10);
- if (!argv[i][0] || *endp || params.timeout_msec <= 0) {
- cerr << "Invalid number to -T" << endl;
- return 1;
- }
- i++;
} else if (argv[i][0] == '-') {
cerr << "Unknown option/flag '" << argv[i] << "'" << endl;
return 1;
- } else if (params.refereePath.empty()) {
- params.refereePath = argv[i];
} else {
players.emplace_back(argv[i]);
}
@@ -439,16 +508,19 @@ int main(int argc, char **argv) {
return 1;
}
- MultiLog multiLog{multilog_fancy};
-
mkdirp(matchcachedir);
mkdirp(playerlogdir);
mkdirp(gamelogdir);
signal(SIGPIPE, [](int){});
+ MultiLog multiLog{multilog_fancy};
+ compParams.multiLog = &multiLog;
+
Scheduler scheduler(scheduler_verbose, num_threads);
- fullCompetition(scheduler, multiLog, players, params);
+ compParams.scheduler = &scheduler;
+
+ fullCompetition(compParams, players, params);
scheduler.finish();
vector<pair<string, int>> scores;
@@ -474,4 +546,68 @@ int main(int argc, char **argv) {
for (const auto &p : scores) {
cout << setw(maxlen) << p.first << " | " << p.second << endl;
}
+
+ return 0;
+}
+
+static int mainSingle(const vector<const char*> &argv, const Params &params) {
+ vector<Player> players;
+ unordered_map<int, string> logfnames;
+
+ for (size_t i = 1; i < argv.size(); i++) {
+ if (strlen(argv[i]) >= strlen("--log1") &&
+ memcmp(argv[i], "--log", strlen("--log")) == 0 &&
+ i + 1 < argv.size()) {
+ const char *idxstring = argv[i] + strlen("--log");
+ char *endp;
+ int playeridx = strtol(idxstring, &endp, 10);
+ if (!idxstring[0] || *endp || playeridx <= 0) {
+ cerr << "--logN index invalid" << endl;
+ return 1;
+ }
+ logfnames[playeridx] = argv[i+1];
+ i++;
+ } else if (argv[i][0] == '-') {
+ cerr << "Unknown option/flag '" << argv[i] << "'" << endl;
+ return 1;
+ } else {
+ players.emplace_back(argv[i]);
+ }
+ }
+
+ if (players.size() != 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ vector<string> playerlogs(players.size());
+ for (size_t i = 0; i < players.size(); i++) {
+ auto it = logfnames.find(i + 1);
+ if (it == logfnames.end()) playerlogs[i] = "/dev/null";
+ else playerlogs[i] = it->second;
+ }
+
+ signal(SIGPIPE, [](int){});
+
+ playMatch(players[0], players[1], params, "single", cout, playerlogs[0], playerlogs[1]);
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ if (argc == 0) {
+ usage("competition");
+ return 1;
+ }
+
+ vector<const char*> arguments{argv, argv + argc};
+ const Params params = parseCommonOptions(arguments);
+
+ switch (params.mode) {
+ case Mode::competition:
+ return mainCompetition(arguments, params);
+
+ case Mode::single:
+ return mainSingle(arguments, params);
+ }
}