#include #include #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. #define FEATURE_TIMEOUT 200 Referee::Referee(const string_view execname, const vector &players) : proc(execname), refereeExecname(execname) { proc.run(); if (referee_verbose) { cout << "REF(" << proc.getPid() << ") starting for:"; for (const string &p : players) cout << " " << p; cout << endl; } queryFeatures(); 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; } proc.wait(); proc.terminate(); } bool Referee::moveValid(int player, const string_view line) { string s = to_string(player) + " "; s.append(line); if (referee_verbose) { cout << "REF(" << proc.getPid() << ") write <" << s << ">" << endl; } proc.writeLine(s); optional response = proc.readLine(); if (response) { if (referee_verbose) { cout << "REF(" << proc.getPid() << ") read <" << *response << ">" << endl; } 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; exit(1); } readWriteLines(); if (isEnd) readScores(); return result; } else { cout << "REF(" << proc.getPid() << ") EOF!" << endl; return false; } } vector> Referee::playerWriteLines() { return writeLinesList; } bool Referee::gameEnded() { return isEnd; } void Referee::queryFeatures() { bool first = true; while (true) { optional line; if (first) { line = proc.readLineTimeout(FEATURE_TIMEOUT); if (!line) { cout << "Referee '" << refereeExecname << "' seems to follow the old protocol!" << endl; return; } first = false; } else { line = proc.readLine(); } if (!line) { cout << "REF(" << proc.getPid() << ") EOF!" << endl; exit(1); } if (referee_verbose) { cout << "REF(" << proc.getPid() << ") 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; else { cerr << "REF(" << proc.getPid() << ") 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 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; exit(1); } int player = stoi(line->substr(0, idx)); writeLinesList.emplace_back(player, line->substr(idx + 1)); } } void Referee::readScores() { optional 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> Referee::getScores() { if (isEnd) return scores; else return nullopt; } void Referee::terminate() { proc.terminate(); } const Features& Referee::getFeatures() const { return features; }