summaryrefslogtreecommitdiff
path: root/referee.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'referee.cpp')
-rw-r--r--referee.cpp217
1 files changed, 116 insertions, 101 deletions
diff --git a/referee.cpp b/referee.cpp
index ffcbd14..2701e78 100644
--- a/referee.cpp
+++ b/referee.cpp
@@ -1,90 +1,160 @@
#include <iostream>
+#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
-// the referee isn't capable of sending feature lines and will be treated as
-// such for the duration of the competition.
+// the referee isn't capable of sending feature lines.
#define FEATURE_TIMEOUT 200
Referee::Referee(const string_view execname, const vector<string> &players)
- : proc(execname), refereeExecname(execname) {
+ : proc(execname), refereeExecname(execname), numPlayers(players.size()) {
proc.run();
+ reftag = "REF(" + to_string(proc.getPid()) + ") ";
+
if (referee_verbose) {
- cout << "REF(" << proc.getPid() << ") starting for:";
+ cout << reftag << "starting for:";
for (const string &p : players) cout << " " << p;
cout << endl;
}
queryFeatures();
+ if (!features.version2) {
+ cout << reftag << "does not support protocol version 2!" << endl;
+ exit(1);
+ }
+
proc.writeLine(to_string(players.size()));
for (const string &p : players) {
proc.writeLine(p);
}
-
- readWriteLines();
}
Referee::~Referee() {
if (referee_verbose) {
- cout << "REF(" << proc.getPid() << ") stopping" << endl;
+ cout << reftag << "stopping" << endl;
}
proc.wait();
proc.terminate();
}
-bool Referee::moveValid(int player, const string_view line) {
- string s = to_string(player) + " ";
- s.append(line);
+Referee::Event Referee::nextEvent() {
+ optional<string> ocommand = proc.readLine();
+ if (!ocommand) {
+ cout << reftag << "Unexpected EOF before 'end' command!" << endl;
+ exit(1);
+ }
+
+ string &command = *ocommand;
if (referee_verbose) {
- cout << "REF(" << proc.getPid() << ") write <" << s << ">" << endl;
+ cout << reftag << "read <" << command << ">" << endl;
}
- proc.writeLine(s);
+ auto getPlayer = [this](const string &command, const char *commandName, int prefixLen) {
+ int player;
+ try {
+ player = stoi(command.substr(prefixLen));
+ } catch (...) {
+ cout << reftag << "Protocol violation in '" << commandName << "' command!" << endl;
+ exit(1);
+ }
- optional<string> response = proc.readLine();
- if (response) {
- if (referee_verbose) {
- cout << "REF(" << proc.getPid() << ") read <" << *response << ">" << endl;
+ if (player < 0 || player >= numPlayers) {
+ cout << reftag << "Invalid player index " << player << " mentioned in '" << commandName << "' command!" << endl;
+ exit(1);
}
- bool result;
- if (*response == "valid end") {
- isEnd = true;
- result = true;
- } else if (*response == "valid") {
- result = true;
- } else if (*response == "invalid") {
- result = false;
- } else {
- cout << "REF(" << proc.getPid() << ") Invalid validity judgement '" << *response << "'!" << endl;
+ size_t afterIndex = command.find(' ', prefixLen);
+ if (afterIndex != string::npos) afterIndex++;
+
+ return make_pair(player, afterIndex);
+ };
+
+ if (command.substr(0, 5) == "read ") {
+ int player = getPlayer(command, "read", 5).first;
+ return ReadEvent{
+ player,
+ [this](const string &line) {
+ if (referee_verbose) {
+ cout << reftag << "write <" << line << ">" << endl;
+ }
+
+ if (!proc.writeLine(line)) {
+ cout << reftag << "Referee exited unexpectedly after 'read' command!" << endl;
+ exit(1);
+ }
+ }
+ };
+ } else if (command.substr(0, 6) == "write ") {
+ int player;
+ size_t afterIndex;
+ tie(player, afterIndex) = getPlayer(command, "write", 6);
+
+ if (afterIndex == string::npos) {
+ cout << reftag << "Protocol violation in 'write' command: no line!" << endl;
exit(1);
}
- readWriteLines();
+ return WriteEvent{player, command.substr(afterIndex)};
+ } else if (command.substr(0, 8) == "gamelog ") {
+ int player;
+ size_t afterIndex;
+ tie(player, afterIndex) = getPlayer(command, "gamelog", 8);
- if (isEnd) readScores();
+ if (afterIndex == string::npos) {
+ cout << reftag << "Protocol violation in 'gamelog' command: no line!" << endl;
+ exit(1);
+ }
- return result;
- } else {
- cout << "REF(" << proc.getPid() << ") EOF!" << endl;
- return false;
- }
-}
+ return GamelogEvent{player, command.substr(afterIndex)};
+ } else if (command.substr(0, 4) == "end ") {
+ size_t cursor = 4;
-vector<pair<int, string>> Referee::playerWriteLines() {
- return writeLinesList;
-}
+ vector<int> scores;
+ while (cursor < command.size()) {
+ size_t idx = command.find(' ', cursor);
+ if (idx == string::npos) idx = command.size();
+
+ string substr = command.substr(cursor, idx - cursor);
+
+ int score;
+ try {
+ score = stoi(substr);
+ } catch (...) {
+ cout << reftag << "Protocol violation in 'end' command: invalid scores!" << endl;
+ exit(1);
+ }
+
+ scores.push_back(score);
+
+ cursor = idx + 1;
+ }
+
+ proc.terminate();
+ return EndEvent{move(scores)};
+ } else if (command.substr(0, 6) == "error ") {
+ int player;
+ size_t afterIndex;
+ tie(player, afterIndex) = getPlayer(command, "error", 6);
+
+ if (afterIndex == string::npos) {
+ cout << reftag << "Protocol violation in 'error' command: no message!" << endl;
+ exit(1);
+ }
-bool Referee::gameEnded() {
- return isEnd;
+ proc.terminate();
+ return ErrorEvent{player, command.substr(6)};
+ } else {
+ cout << reftag << "Protocol violation: unknown command '" << command << "'!" << endl;
+ exit(1);
+ }
}
void Referee::queryFeatures() {
@@ -95,8 +165,8 @@ void Referee::queryFeatures() {
if (first) {
line = proc.readLineTimeout(FEATURE_TIMEOUT);
if (!line) {
- cout << "Referee '" << refereeExecname << "' seems to follow the old protocol!" << endl;
- return;
+ cout << "Referee '" << refereeExecname << "' does not support feature detection!" << endl;
+ exit(1);
}
first = false;
@@ -105,85 +175,30 @@ void Referee::queryFeatures() {
}
if (!line) {
- cout << "REF(" << proc.getPid() << ") EOF!" << endl;
+ cout << reftag << "EOF!" << endl;
exit(1);
}
if (referee_verbose) {
- cout << "REF(" << proc.getPid() << ") read <" << *line << ">" << endl;
+ cout << reftag << "read <" << *line << ">" << endl;
}
if (*line == "feature_end") {
return;
} else if (line->substr(0, 8) == "feature ") {
string featname = line->substr(8);
- if (featname == "write_lines") features.writeLines = true;
- else if (featname == "no_last_move") features.noLastMove = true;
+ if (featname == "version_2") features.version2 = true;
else {
- cerr << "REF(" << proc.getPid() << ") Referee requested unsupported feature '" << featname << "'" << endl;
+ cerr << reftag << "Referee requested unsupported feature '" << featname << "'" << endl;
exit(1);
}
} else {
- cerr << "REF(" << proc.getPid() << ") Referee protocol violation in feature negotiation" << endl;
- exit(1);
- }
- }
-}
-
-void Referee::readWriteLines() {
- writeLinesList.clear();
-
- if (!features.writeLines) return;
-
- while (true) {
- optional<string> line = proc.readLine();
- if (!line) {
- cout << "REF(" << proc.getPid() << ") EOF!" << endl;
- break;
- }
-
- if (referee_verbose) {
- cout << "REF(" << proc.getPid() << ") read <" << *line << ">" << endl;
- }
-
- if (line == "write_end") break;
-
- size_t idx = line->find(' ');
- if (idx == string::npos) {
- cerr << "REF(" << proc.getPid() << ") Referee feature_write_lines protocol violation!" << endl;
+ cerr << reftag << "Referee protocol violation in feature negotiation" << endl;
exit(1);
}
-
- int player = stoi(line->substr(0, idx));
- writeLinesList.emplace_back(player, line->substr(idx + 1));
}
}
-void Referee::readScores() {
- optional<string> line = proc.readLine();
- if (!line) {
- cerr << "Referee stopped before providing scores" << endl;
- exit(1);
- }
-
- size_t cursor = 0;
- while (cursor < line->size()) {
- size_t idx = line->find(' ', cursor);
- if (idx == string::npos) idx = line->size();
- scores.push_back(stoi(line->substr(cursor, idx - cursor)));
- cursor = idx + 1;
- }
-}
-
-optional<vector<int>> Referee::getScores() {
- if (isEnd) return scores;
- else return nullopt;
-}
-
void Referee::terminate() {
proc.terminate();
}
-
-const Features& Referee::getFeatures() const {
- return features;
-}