diff options
Diffstat (limited to 'referee.cpp')
-rw-r--r-- | referee.cpp | 217 |
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; -} |