#include #include #include #include "referee.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. #define FEATURE_TIMEOUT 200 Referee::Referee(bool verbose, const string_view execname, const vector &players) : verbose(verbose) , proc(execname) , refereeExecname(execname) , numPlayers(players.size()) { proc.run(); reftag = "REF(" + to_string(proc.getPid()) + ") "; if (verbose) { 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()), false); for (const string &p : players) { proc.writeLine(p, false); } } Referee::~Referee() { if (verbose) { cout << reftag << "stopping" << endl; } proc.wait(); proc.terminate(); } Referee::Event Referee::nextEvent() { optional ocommand = proc.readLine(); if (!ocommand) { cout << reftag << "Unexpected EOF before 'end' command!" << endl; exit(1); } string &command = *ocommand; if (verbose) { cout << reftag << "read <" << command << ">" << endl; } 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); } if (player < 0 || player >= numPlayers) { cout << reftag << "Invalid player index " << player << " mentioned in '" << commandName << "' command!" << endl; exit(1); } 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 (verbose) { cout << reftag << "write <" << line << ">" << endl; } if (!proc.writeLine(line, false)) { 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); } return WriteEvent{player, false, command.substr(afterIndex)}; } else if (command.substr(0, 11) == "writemaybe ") { int player; size_t afterIndex; tie(player, afterIndex) = getPlayer(command, "writemaybe", 11); if (afterIndex == string::npos) { cout << reftag << "Protocol violation in 'writemaybe' command: no line!" << endl; exit(1); } return WriteEvent{player, true, command.substr(afterIndex)}; } else if (command.substr(0, 8) == "gamelog ") { int player; size_t afterIndex; tie(player, afterIndex) = getPlayer(command, "gamelog", 8); if (afterIndex == string::npos) { cout << reftag << "Protocol violation in 'gamelog' command: no line!" << endl; exit(1); } return GamelogEvent{player, command.substr(afterIndex)}; } else if (command.substr(0, 4) == "end ") { size_t cursor = 4; vector 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); } proc.terminate(); return ErrorEvent{player, command.substr(afterIndex)}; } else { cout << reftag << "Protocol violation: unknown command '" << command << "'!" << endl; exit(1); } } void Referee::queryFeatures() { bool first = true; while (true) { optional line; if (first) { line = proc.readLineTimeout(FEATURE_TIMEOUT); if (!line) { cout << "Referee '" << refereeExecname << "' does not support feature detection!" << endl; exit(1); } first = false; } else { line = proc.readLine(); } if (!line) { cout << reftag << "EOF!" << endl; exit(1); } if (verbose) { cout << reftag << "read <" << *line << ">" << endl; } if (*line == "feature_end") { return; } else if (line->substr(0, 8) == "feature ") { string featname = line->substr(8); if (featname == "version_2") features.version2 = true; else { cerr << reftag << "Referee requested unsupported feature '" << featname << "'" << endl; exit(1); } } else { cerr << reftag << "Referee protocol violation in feature negotiation" << endl; exit(1); } } } void Referee::terminate() { proc.terminate(); }