summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.cpp117
-rw-r--r--multilog.cpp10
-rw-r--r--multilog.h3
-rw-r--r--params.h16
-rw-r--r--referee.cpp18
-rw-r--r--referee.h4
-rw-r--r--scheduler.cpp26
-rw-r--r--scheduler.h6
8 files changed, 127 insertions, 73 deletions
diff --git a/main.cpp b/main.cpp
index 306c5cc..41ce02a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -23,13 +23,13 @@
#include "error.h"
#include "multilog.h"
#include "referee.h"
-#include "params.h"
using namespace std;
-// TODO no globals
-static MultiLog gMultiLog;
+const char *const matchcachedir = "comp_cache";
+const char *const playerlogdir = "comp_playerlogs";
+const char *const gamelogdir = "comp_gamelogs";
static char hexchar(int n) {
@@ -158,7 +158,10 @@ MatchResult MatchResult::inverted() const {
}
struct Params {
+ bool referee_verbose = false;
string refereePath;
+ int num_matches = 5;
+ size_t timeout_msec = 10000;
};
static string gameCodeName(const Player &p1, const Player &p2, int index) {
@@ -226,19 +229,18 @@ static void recordResult(Player &p1, Player &p2, const MatchResult &result) {
p2.results.push_back(result.inverted());
}
-static void playMatch(Player &p1, Player &p2, int index, const Params &params) {
- int logId = gMultiLog.add(p1.fname + " - " + p2.fname + ": ");
+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)) {
- gMultiLog.append(logId, optres->describe(p1, p2) + " (cached)");
- gMultiLog.complete(logId);
+ multiLog.append(logId, optres->describe(p1, p2) + " (cached)");
+ multiLog.complete(logId);
recordResult(p1, p2, *optres);
return;
}
MatchResult mres;
-
string gamelog_path = gameLogPath(p1, p2, index);
ofstream gamelog(gamelog_path);
if (!gamelog) {
@@ -246,7 +248,7 @@ static void playMatch(Player &p1, Player &p2, int index, const Params &params) {
throw StopCompetitionError();
}
- Referee referee(params.refereePath, {p1.fname, p2.fname});
+ Referee referee(params.referee_verbose, params.refereePath, {p1.fname, p2.fname});
gamelog << "Player 1: " << p1.fname << "\n"
<< "Player 2: " << p2.fname << "\n\n" << flush;
@@ -279,7 +281,7 @@ static void playMatch(Player &p1, Player &p2, int index, const Params &params) {
readEvent->callback(*oline);
- if (mres.ms1 / 1000 > timeout_msec || mres.ms2 / 1000 > timeout_msec) {
+ if (mres.ms1 / 1000 > params.timeout_msec || mres.ms2 / 1000 > params.timeout_msec) {
mres.status = MatchResult::Status::timeout;
mres.sc1 = mres.sc2 = 0;
referee.terminate();
@@ -326,8 +328,8 @@ static void playMatch(Player &p1, Player &p2, int index, const Params &params) {
}
}
- gMultiLog.append(logId, mres.describe(p1, p2));
- gMultiLog.complete(logId);
+ 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"
@@ -337,35 +339,92 @@ static void playMatch(Player &p1, Player &p2, int index, const Params &params) {
recordResult(p1, p2, mres);
}
-static void playerPit(Scheduler &scheduler, Player &p1, Player &p2, const Params &params) {
- for (int i = 0; i < num_matches; i++) {
- scheduler.submit([&p1, &p2, i, params]() { playMatch(p1, p2, i + 1, params); });
+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);
+ });
}
}
-static void fullCompetition(Scheduler &scheduler, vector<Player> &players, const Params &params) {
+static void fullCompetition(Scheduler &scheduler, MultiLog &multiLog, 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, players[p1i], players[p2i], params);
- playerPit(scheduler, players[p2i], players[p1i], params);
+ playerPit(scheduler, multiLog, players[p1i], players[p2i], params);
+ playerPit(scheduler, multiLog, players[p2i], players[p1i], params);
}
}
}
-int main(int argc, char **argv) {
- if (argc < 3) {
- cerr << "Usage: " << argv[0] << " <referee> <players...>" << endl;
- return 1;
- }
+static void usage(const char *argv0) {
+ cerr << "Usage: " << argv0 << " [OPTIONS] <referee> <players...>\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"
+ " -T <MSEC> Set player timeout in milliseconds (default "
+ << Params{}.timeout_msec << ")\n";
+}
+int main(int argc, char **argv) {
Params params;
- params.refereePath = argv[1];
-
vector<Player> players;
- for (int i = 2; i < argc; i++) {
- players.emplace_back(argv[i]);
+ bool multilog_fancy = true;
+ bool scheduler_verbose = false;
+ int num_threads = std::thread::hardware_concurrency() / 2;
+
+ 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) {
+ 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) {
+ char *endp;
+ num_threads = strtol(argv[i+1], &endp, 10);
+ if (!argv[i][0] || *endp || num_threads <= 0) {
+ cerr << "Invalid number to -j" << endl;
+ return 1;
+ }
+ i++;
+ } else if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) {
+ char *endp;
+ params.num_matches = strtol(argv[i+1], &endp, 10);
+ if (!argv[i][0] || *endp || params.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]);
+ }
+ }
+
+ if (players.empty()) {
+ usage(argv[0]);
+ return 1;
}
+ MultiLog multiLog{multilog_fancy};
mkdirp(matchcachedir);
mkdirp(playerlogdir);
@@ -373,8 +432,8 @@ int main(int argc, char **argv) {
signal(SIGPIPE, [](int){});
- Scheduler scheduler(num_threads);
- fullCompetition(scheduler, players, params);
+ Scheduler scheduler(scheduler_verbose, num_threads);
+ fullCompetition(scheduler, multiLog, players, params);
scheduler.finish();
vector<pair<string, int>> scores;
diff --git a/multilog.cpp b/multilog.cpp
index d2dce10..2d9f3d6 100644
--- a/multilog.cpp
+++ b/multilog.cpp
@@ -2,7 +2,6 @@
#include <string>
#include <cassert>
#include "multilog.h"
-#include "params.h"
static int uniqid() {
@@ -12,7 +11,10 @@ static int uniqid() {
MultiLog::Item::Item()
- : id(uniqid()) {}
+ : id{uniqid()} {}
+
+MultiLog::MultiLog(bool fancy)
+ : fancy{fancy} {}
int MultiLog::add(const string_view prefix) {
lock_guard guard(mut);
@@ -21,7 +23,7 @@ int MultiLog::add(const string_view prefix) {
items.emplace_back();
items.back().line = prefix;
- if (multilog_fancy) {
+ if (fancy) {
cout << prefix << endl;
} else {
cout << "[" << items.back().id << "] " << prefix << endl;
@@ -36,7 +38,7 @@ void MultiLog::append(int id, const string_view text) {
size_t idx = findId(id);
items[idx].line += text;
- if (multilog_fancy) {
+ if (fancy) {
redrawLine(idx);
} else {
cout << "[" << items[idx].id << "]... " << text << endl;
diff --git a/multilog.h b/multilog.h
index ba0ff4d..3c411f2 100644
--- a/multilog.h
+++ b/multilog.h
@@ -19,12 +19,15 @@ class MultiLog {
mutex mut;
vector<Item> items;
+ const bool fancy;
// Methods assume mutex is taken.
size_t findId(int id);
void redrawLine(size_t idx);
public:
+ MultiLog(bool fancy);
+
// Returns id of new item
int add(const string_view prefix);
void append(int id, const string_view text);
diff --git a/params.h b/params.h
deleted file mode 100644
index 3caff86..0000000
--- a/params.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-
-// Actual variable needs to be const as well to enable C++ default-internal linkage
-const char *const matchcachedir = "comp_cache";
-const char *const playerlogdir = "comp_playerlogs";
-const char *const gamelogdir = "comp_gamelogs";
-
-const int num_threads = 2;
-
-const int num_matches = 5;
-const int timeout_msec = 5000;
-
-const bool multilog_fancy = true;
-const bool referee_verbose = false;
-const bool scheduler_verbose = false;
diff --git a/referee.cpp b/referee.cpp
index d215c3d..08770a7 100644
--- a/referee.cpp
+++ b/referee.cpp
@@ -2,7 +2,6 @@
#include <tuple>
#include <cstdlib>
#include "referee.h"
-#include "params.h"
// Timeout in milliseconds for the feature negotiation at the start of the
// protocol. If the referee hasn't sent anything after this timeout, we assume
@@ -10,14 +9,17 @@
#define FEATURE_TIMEOUT 200
-Referee::Referee(const string_view execname, const vector<string> &players)
- : proc(execname), refereeExecname(execname), numPlayers(players.size()) {
+Referee::Referee(bool verbose, const string_view execname, const vector<string> &players)
+ : verbose(verbose)
+ , proc(execname)
+ , refereeExecname(execname)
+ , numPlayers(players.size()) {
proc.run();
reftag = "REF(" + to_string(proc.getPid()) + ") ";
- if (referee_verbose) {
+ if (verbose) {
cout << reftag << "starting for:";
for (const string &p : players) cout << " " << p;
cout << endl;
@@ -37,7 +39,7 @@ Referee::Referee(const string_view execname, const vector<string> &players)
}
Referee::~Referee() {
- if (referee_verbose) {
+ if (verbose) {
cout << reftag << "stopping" << endl;
}
proc.wait();
@@ -53,7 +55,7 @@ Referee::Event Referee::nextEvent() {
string &command = *ocommand;
- if (referee_verbose) {
+ if (verbose) {
cout << reftag << "read <" << command << ">" << endl;
}
@@ -82,7 +84,7 @@ Referee::Event Referee::nextEvent() {
return ReadEvent{
player,
[this](const string &line) {
- if (referee_verbose) {
+ if (verbose) {
cout << reftag << "write <" << line << ">" << endl;
}
@@ -190,7 +192,7 @@ void Referee::queryFeatures() {
exit(1);
}
- if (referee_verbose) {
+ if (verbose) {
cout << reftag << "read <" << *line << ">" << endl;
}
diff --git a/referee.h b/referee.h
index 11b10ae..5c60bd2 100644
--- a/referee.h
+++ b/referee.h
@@ -13,6 +13,8 @@ using namespace std;
class Referee {
+ const bool verbose;
+
Process proc;
string refereeExecname;
int numPlayers;
@@ -51,7 +53,7 @@ public:
};
using Event = variant<ReadEvent, WriteEvent, GamelogEvent, EndEvent, ErrorEvent>;
- Referee(const string_view execname, const vector<string> &players);
+ Referee(bool verbose, const string_view execname, const vector<string> &players);
~Referee();
Event nextEvent();
diff --git a/scheduler.cpp b/scheduler.cpp
index 6ed66ac..740eb3a 100644
--- a/scheduler.cpp
+++ b/scheduler.cpp
@@ -2,9 +2,8 @@
#include <chrono>
#include <cassert>
#include "scheduler.h"
-#include "params.h"
-void Scheduler::workerEntry() {
+void Scheduler::workerEntry(bool verbose) {
while (true) {
Job *job = nullptr;
@@ -17,20 +16,21 @@ void Scheduler::workerEntry() {
}
if (job) {
- if (scheduler_verbose) {
+ if (verbose) {
cout << "SCHED(" << this_thread::get_id() << ") running job" << endl;
}
job->callback();
- if (scheduler_verbose) {
+ delete job;
+ if (verbose) {
cout << "SCHED(" << this_thread::get_id() << ") finished job" << endl;
}
} else if (finishFlag) {
- if (scheduler_verbose) {
+ if (verbose) {
cout << "SCHED(" << this_thread::get_id() << ") spotted finishFlag" << endl;
}
break;
} else {
- if (scheduler_verbose) {
+ if (verbose) {
cout << "SCHED(" << this_thread::get_id() << ") no job, waiting" << endl;
}
this_thread::sleep_for(chrono::milliseconds(100));
@@ -38,17 +38,17 @@ void Scheduler::workerEntry() {
}
}
-Scheduler::Scheduler(int nthreads)
- : nthreads(nthreads) {
+Scheduler::Scheduler(bool verbose, int nthreads)
+ : verbose(verbose), nthreads(nthreads) {
assert(nthreads > 0);
workers.reserve(nthreads);
for (int i = 0; i < nthreads; i++) {
- workers.emplace_back([this]() { workerEntry(); });
+ workers.emplace_back([this]() { workerEntry(this->verbose); });
}
- if (scheduler_verbose) {
+ if (verbose) {
cout << "SCHED " << nthreads << " workers spawned" << endl;
}
}
@@ -66,7 +66,7 @@ void Scheduler::submit(const function<void()> &func) {
void Scheduler::finish() {
if (hasJoined) return;
- if (scheduler_verbose) {
+ if (verbose) {
cout << "SCHED finish()" << endl;
}
@@ -76,13 +76,13 @@ void Scheduler::finish() {
}
for (int i = 0; i < nthreads; i++) {
- if (scheduler_verbose) {
+ if (verbose) {
cout << "SCHED Joining worker " << i << "..." << endl;
}
workers[i].join();
}
- if (scheduler_verbose) {
+ if (verbose) {
cout << "SCHED All workers joined" << endl;
}
diff --git a/scheduler.h b/scheduler.h
index 95da7c7..5018a65 100644
--- a/scheduler.h
+++ b/scheduler.h
@@ -17,6 +17,8 @@ class Scheduler {
: callback(callback) {}
};
+ bool verbose;
+
queue<Job*> jobs;
bool finishFlag = false;
mutex commMut;
@@ -25,12 +27,12 @@ class Scheduler {
vector<thread> workers;
- void workerEntry();
+ void workerEntry(bool verbose);
public:
const int nthreads;
- Scheduler(int nthreads);
+ Scheduler(bool verbose, int nthreads);
~Scheduler();
// func is run in child thread