Skip to content

Commit

Permalink
Clean up match pairing logic, fix segfault with extraPairs, remove bl…
Browse files Browse the repository at this point in the history
…ackPriority, add extraPairsAreOneSidedBW
  • Loading branch information
lightvector committed May 19, 2023
1 parent 86da046 commit 462df45
Show file tree
Hide file tree
Showing 9 changed files with 530 additions and 174 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -79,3 +79,5 @@ models/
python/startposesupload.txt
for_release/

tests/results/matchsgfs/
tests/results/matchsgfs2/
13 changes: 9 additions & 4 deletions cpp/command/gatekeeper.cpp
Expand Up @@ -100,11 +100,16 @@ namespace {
noResultUtilityForWhite = baseParams.noResultUtilityForWhite;

//Initialize object for randomly pairing bots. Actually since this is only selfplay, this only
//ever gives is the trivial self-pairing, but we use it also for keeping the game count and some logging.
bool forSelfPlay = false;
bool forGateKeeper = true;
//ever gives is the trivial base-vs-candidate pairing, but we use it also for keeping the game count and some logging.
int64_t numGamesTotal = cfg.getInt64("numGamesPerGating",0,((int64_t)1) << 24);
matchPairer = new MatchPairer(
cfg, 2, {modelNameBaseline,modelNameCandidate}, {nnEvalBaseline,nnEvalCandidate}, {baseParams, baseParams}, forSelfPlay, forGateKeeper
cfg,
2,
{modelNameBaseline,modelNameCandidate},
{nnEvalBaseline,nnEvalCandidate},
{baseParams, baseParams},
{{0,1},{1,0}},
numGamesTotal
);
}

Expand Down
89 changes: 78 additions & 11 deletions cpp/command/match.cpp
Expand Up @@ -70,13 +70,75 @@ int MainCmds::match(const vector<string>& args) {
assert(paramss.size() > 0);
int numBots = (int)paramss.size();

//Load a filter on what bots we actually want to run
vector<bool> excludeBot(numBots);
if(cfg.contains("includeBots")) {
vector<int> includeBots = cfg.getInts("includeBots",0,Setup::MAX_BOT_PARAMS_FROM_CFG);
//Figure out all pairs of bots that will be playing.
std::vector<std::pair<int,int>> matchupsPerRound;
{
//Load a filter on what bots we actually want to run. By default, include everything.
vector<bool> includeBot(numBots);
if(cfg.contains("includeBots")) {
vector<int> includeBotIdxs = cfg.getInts("includeBots",0,Setup::MAX_BOT_PARAMS_FROM_CFG);
for(int i = 0; i<numBots; i++) {
if(contains(includeBotIdxs,i))
includeBot[i] = true;
}
}
else {
for(int i = 0; i<numBots; i++) {
includeBot[i] = true;
}
}

std::vector<int> secondaryBotIdxs;
if(cfg.contains("secondaryBots"))
secondaryBotIdxs = cfg.getInts("secondaryBots",0,Setup::MAX_BOT_PARAMS_FROM_CFG);
for(int i = 0; i<secondaryBotIdxs.size(); i++)
assert(secondaryBotIdxs[i] >= 0 && secondaryBotIdxs[i] < numBots);

for(int i = 0; i<numBots; i++) {
if(!contains(includeBots,i))
excludeBot[i] = true;
if(!includeBot[i])
continue;
for(int j = 0; j<numBots; j++) {
if(!includeBot[j])
continue;
if(i < j && !(contains(secondaryBotIdxs,i) && contains(secondaryBotIdxs,j))) {
matchupsPerRound.push_back(make_pair(i,j));
matchupsPerRound.push_back(make_pair(j,i));
}
}
}

if(cfg.contains("extraPairs")) {
string pairsStr = cfg.getString("extraPairs");
std::vector<string> pairStrs = Global::split(pairsStr,',');
for(const string& pairStr: pairStrs) {
if(Global::trim(pairStr).size() <= 0)
continue;
std::vector<string> pieces = Global::split(Global::trim(pairStr),'-');
if(pieces.size() != 2) {
throw IOError("Could not parse pair: " + pairStr);
}
bool suc;
int p0;
int p1;
suc = Global::tryStringToInt(pieces[0],p0);
if(!suc)
throw IOError("Could not parse pair: " + pairStr);
suc = Global::tryStringToInt(pieces[1],p1);
if(!suc)
throw IOError("Could not parse pair: " + pairStr);
if(p0 < 0 || p0 >= numBots)
throw IOError("Invalid player index in pair: " + pairStr);
if(p1 < 0 || p1 >= numBots)
throw IOError("Invalid player index in pair: " + pairStr);

if(cfg.contains("extraPairsAreOneSidedBW") && cfg.getBool("extraPairsAreOneSidedBW")) {
matchupsPerRound.push_back(std::make_pair(p0,p1));
}
else {
matchupsPerRound.push_back(std::make_pair(p0,p1));
matchupsPerRound.push_back(std::make_pair(p1,p0));
}
}
}
}

Expand All @@ -99,11 +161,17 @@ int MainCmds::match(const vector<string>& args) {
nnModelFilesByBot[i] = cfg.getString("nnModelFile");
}

vector<bool> botIsUsed(numBots);
for(const std::pair<int,int> pair: matchupsPerRound) {
botIsUsed[pair.first] = true;
botIsUsed[pair.second] = true;
}

//Dedup and load each necessary model exactly once
vector<string> nnModelFiles;
vector<int> whichNNModel(numBots);
for(int i = 0; i<numBots; i++) {
if(excludeBot[i])
if(!botIsUsed[i])
continue;

const string& desiredFile = nnModelFilesByBot[i];
Expand Down Expand Up @@ -165,7 +233,7 @@ int MainCmds::match(const vector<string>& args) {

vector<NNEvaluator*> nnEvalsByBot(numBots);
for(int i = 0; i<numBots; i++) {
if(excludeBot[i])
if(!botIsUsed[i])
continue;
nnEvalsByBot[i] = nnEvals[whichNNModel[i]];
}
Expand All @@ -174,9 +242,8 @@ int MainCmds::match(const vector<string>& args) {
assert(patternBonusTables.size() == numBots);

//Initialize object for randomly pairing bots
bool forSelfPlay = false;
bool forGateKeeper = false;
MatchPairer* matchPairer = new MatchPairer(cfg,numBots,botNames,nnEvalsByBot,paramss,forSelfPlay,forGateKeeper,excludeBot);
int64_t numGamesTotal = cfg.getInt64("numGamesTotal",1,((int64_t)1) << 62);
MatchPairer* matchPairer = new MatchPairer(cfg,numBots,botNames,nnEvalsByBot,paramss,matchupsPerRound,numGamesTotal);

//Check for unused config keys
cfg.warnUnusedKeys(cerr,&logger);
Expand Down
149 changes: 16 additions & 133 deletions cpp/program/play.cpp
Expand Up @@ -601,96 +601,29 @@ MatchPairer::MatchPairer(
const vector<string>& bNames,
const vector<NNEvaluator*>& nEvals,
const vector<SearchParams>& bParamss,
bool forSelfPlay,
bool forGateKeeper
): MatchPairer(cfg,nBots,bNames,nEvals,bParamss,forSelfPlay,forGateKeeper,vector<bool>(nBots))
{}


MatchPairer::MatchPairer(
ConfigParser& cfg,
int nBots,
const vector<string>& bNames,
const vector<NNEvaluator*>& nEvals,
const vector<SearchParams>& bParamss,
bool forSelfPlay,
bool forGateKeeper,
const vector<bool>& exclude
const std::vector<std::pair<int,int>>& matchups,
int64_t numGames
)
:numBots(nBots),
botNames(bNames),
nnEvals(nEvals),
baseParamss(bParamss),
excludeBot(exclude),
secondaryBots(),
blackPriority(),
matchupsPerRound(matchups),
nextMatchups(),
nextMatchupsBuf(),
rand(),
matchRepFactor(1),
repsOfLastMatchup(0),
numGamesStartedSoFar(0),
numGamesTotal(),
numGamesTotal(numGames),
logGamesEvery(),
getMatchupMutex()
{
assert(!(forSelfPlay && forGateKeeper));
assert(botNames.size() == numBots);
assert(nnEvals.size() == numBots);
assert(baseParamss.size() == numBots);
assert(exclude.size() == numBots);
if(forSelfPlay) {
assert(numBots == 1);
numGamesTotal = cfg.getInt64("numGamesTotal",1,((int64_t)1) << 62);
}
else if(forGateKeeper) {
assert(numBots == 2);
numGamesTotal = cfg.getInt64("numGamesPerGating",0,((int64_t)1) << 24);
}
else {
if(cfg.contains("secondaryBots"))
secondaryBots = cfg.getInts("secondaryBots",0,Setup::MAX_BOT_PARAMS_FROM_CFG);
for(int i = 0; i<secondaryBots.size(); i++)
assert(secondaryBots[i] >= 0 && secondaryBots[i] < numBots);
for(int i = 0; i<numBots; i++) {
string idxStr = Global::intToString(i);
if(cfg.contains("blackPriority" + idxStr))
blackPriority.push_back(cfg.getInt("blackPriority" + idxStr));
else
blackPriority.push_back(0);
}
numGamesTotal = cfg.getInt64("numGamesTotal",1,((int64_t)1) << 62);
}

if(cfg.contains("matchRepFactor"))
matchRepFactor = cfg.getInt("matchRepFactor",1,100000);

if(cfg.contains("extraPairs")) {
string pairsStr = cfg.getString("extraPairs");
std::vector<string> pairStrs = Global::split(pairsStr,',');
for(const string& pairStr: pairStrs) {
if(Global::trim(pairStr).size() <= 0)
continue;
std::vector<string> pieces = Global::split(Global::trim(pairStr),'-');
if(pieces.size() != 2) {
throw IOError("Could not parse pair: " + pairStr);
}
bool suc;
int p0;
int p1;
suc = Global::tryStringToInt(pieces[0],p0);
if(!suc)
throw IOError("Could not parse pair: " + pairStr);
suc = Global::tryStringToInt(pieces[1],p1);
if(!suc)
throw IOError("Could not parse pair: " + pairStr);
if(p0 < 0 || p0 >= nBots)
throw IOError("Invalid player index in pair: " + pairStr);
if(p1 < 0 || p1 >= nBots)
throw IOError("Invalid player index in pair: " + pairStr);
extraPairs.push_back(std::make_pair(p0,p1));
}
}
if(matchupsPerRound.size() <= 0)
throw StringError("MatchPairer: no matchups specified");
if(matchupsPerRound.size() > 0xFFFFFF)
throw StringError("MatchPairer: too many matchups");

logGamesEvery = cfg.getInt64("logGamesEvery",1,1000000);
}
Expand Down Expand Up @@ -728,9 +661,6 @@ bool MatchPairer::getMatchup(
}

pair<int,int> matchup = getMatchupPairUnsynchronized();
if(blackPriority.size() > 0 && blackPriority.size() == numBots && blackPriority[matchup.first] < blackPriority[matchup.second]) {
matchup = make_pair(matchup.second,matchup.first);
}

botSpecB.botIdx = matchup.first;
botSpecB.botName = botNames[matchup.first];
Expand All @@ -749,69 +679,22 @@ pair<int,int> MatchPairer::getMatchupPairUnsynchronized() {
if(nextMatchups.size() <= 0) {
if(numBots == 0)
throw StringError("MatchPairer::getMatchupPairUnsynchronized: no bots to match up");
if(numBots == 1)
return make_pair(0,0);

nextMatchupsBuf.clear();
//First generate the pairs only in a one-sided manner
for(int i = 0; i<numBots; i++) {
if(excludeBot[i])
continue;
for(int j = 0; j<numBots; j++) {
if(excludeBot[j])
continue;
if(i < j && !(contains(secondaryBots,i) && contains(secondaryBots,j))) {
nextMatchupsBuf.push_back(make_pair(i,j));
}
}
}
for(const std::pair<int,int>& extraPair: extraPairs) {
nextMatchupsBuf.push_back(extraPair);
}

if(nextMatchupsBuf.size() <= 0)
throw StringError("MatchPairer::getMatchupPairUnsynchronized: no matchups generated");
if(nextMatchupsBuf.size() > 0xFFFFFF)
throw StringError("MatchPairer::getMatchupPairUnsynchronized: too many matchups");
//Append all matches for the next round
nextMatchups.clear();
nextMatchups.insert(nextMatchups.begin(), matchupsPerRound.begin(), matchupsPerRound.end());

//Shuffle
for(int i = (int)nextMatchupsBuf.size()-1; i >= 1; i--) {
for(int i = (int)nextMatchups.size()-1; i >= 1; i--) {
int j = (int)rand.nextUInt(i+1);
pair<int,int> tmp = nextMatchupsBuf[i];
nextMatchupsBuf[i] = nextMatchupsBuf[j];
nextMatchupsBuf[j] = tmp;
}

//Then expand each pair into each player starting first
for(int i = 0; i<nextMatchupsBuf.size(); i++) {
pair<int,int> p = nextMatchupsBuf[i];
pair<int,int> swapped = make_pair(p.second,p.first);
if(rand.nextBool(0.5)) {
nextMatchups.push_back(p);
nextMatchups.push_back(swapped);
}
else {
nextMatchups.push_back(swapped);
nextMatchups.push_back(p);
}
pair<int,int> tmp = nextMatchups[i];
nextMatchups[i] = nextMatchups[j];
nextMatchups[j] = tmp;
}
}

pair<int,int> matchup = nextMatchups.back();

//Swap pair every other matchup if doing more than one rep
if(repsOfLastMatchup % 2 == 1) {
pair<int,int> tmp = make_pair(matchup.second,matchup.first);
matchup = tmp;
}

if(repsOfLastMatchup >= matchRepFactor-1) {
nextMatchups.pop_back();
repsOfLastMatchup = 0;
}
else {
repsOfLastMatchup++;
}
nextMatchups.pop_back();

return matchup;
}
Expand Down
35 changes: 9 additions & 26 deletions cpp/program/play.h
Expand Up @@ -191,18 +191,8 @@ class MatchPairer {
const std::vector<std::string>& botNames,
const std::vector<NNEvaluator*>& nnEvals,
const std::vector<SearchParams>& baseParamss,
bool forSelfPlay,
bool forGateKeeper
);
MatchPairer(
ConfigParser& cfg,
int numBots,
const std::vector<std::string>& botNames,
const std::vector<NNEvaluator*>& nnEvals,
const std::vector<SearchParams>& baseParamss,
bool forSelfPlay,
bool forGateKeeper,
const std::vector<bool>& excludeBot
const std::vector<std::pair<int,int>>& matchupsPerRound,
int64_t numGamesTotal
);

~MatchPairer();
Expand All @@ -226,24 +216,17 @@ class MatchPairer {
);

private:
int numBots;
std::vector<std::string> botNames;
std::vector<NNEvaluator*> nnEvals;
std::vector<SearchParams> baseParamss;

std::vector<bool> excludeBot;
std::vector<int> secondaryBots;
std::vector<int> blackPriority;
std::vector<std::pair<int,int>> extraPairs;
const int numBots;
const std::vector<std::string> botNames;
const std::vector<NNEvaluator*> nnEvals;
const std::vector<SearchParams> baseParamss;
const std::vector<std::pair<int,int>> matchupsPerRound;

std::vector<std::pair<int,int>> nextMatchups;
std::vector<std::pair<int,int>> nextMatchupsBuf;
Rand rand;

int matchRepFactor;
int repsOfLastMatchup;

int64_t numGamesStartedSoFar;
int64_t numGamesTotal;
const int64_t numGamesTotal;
int64_t logGamesEvery;

std::mutex getMatchupMutex;
Expand Down

0 comments on commit 462df45

Please sign in to comment.