View Single Post
  Spotlight this post!  
Unread 07-04-2008, 20:02
Bongle's Avatar
Bongle Bongle is offline
Registered User
FRC #2702 (REBotics)
Team Role: Mentor
 
Join Date: Feb 2004
Rookie Year: 2002
Location: Waterloo
Posts: 1,069
Bongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond reputeBongle has a reputation beyond repute
Send a message via MSN to Bongle
Re: Offensive Power Rankings for 2008

Someone (don't know if he wants to be revealed) via PM requested the algorithm I've been using, which meant I had to clean my code, which means I'm not as embarrassed to post it.

So here it is:
Code:
// opr.cpp : Defines the entry point for the console application.
//

#include "stdafx.h" // if you're not using visual studio, you'll probably have to toast this line

#include <fstream>
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include "jama/jama_lu.h"

using namespace std;

struct Match
{
	Match()
	{
		memset(this,0,sizeof(Match));
	}
	int blue[3];
	int red[3];
	int blueScore;
	int redScore;
};

// reads all the match data from files copy/pasted from usfirst.org into a list of Match structures
// note that week 1, 2008 files can't be read unmodified by this function
// what I've been doing is pre-processing them doing a find-replace on AM and PM and adding " X " so that they have the right number of tab-seperated values
void ReadTeamData(string strFile,vector<Match>& lstMatches,set<int>& setTeams)
{
	ifstream inFile;
	inFile.open(strFile.c_str());
	
	if(inFile.fail())
	{
		cout<<"Error opening file"<<endl;
		return;
	}

	//int iTheoryMatchNum = 0;
	while(!inFile.eof())
	{
		char buf[512];
		Match aNewMatch;

		inFile>>buf; // "9:20"
		inFile>>buf; // "AM/PM"
		inFile>>buf; // match number

		
		inFile>>buf; // red 1		
		aNewMatch.red[0] = atoi(buf);
		inFile>>buf; // red 2
		aNewMatch.red[1] = atoi(buf);
		inFile>>buf; // red 3
		aNewMatch.red[2] = atoi(buf);

		inFile>>buf; // blue 1
		aNewMatch.blue[0] = atoi(buf);
		inFile>>buf; // blue 2
		aNewMatch.blue[1] = atoi(buf);
		inFile>>buf; // blue 3
		aNewMatch.blue[2] = atoi(buf);

		

		inFile>>buf;
		aNewMatch.redScore = atoi(buf);
		inFile>>buf;
		aNewMatch.blueScore = atoi(buf);

		if(aNewMatch.redScore < 0 || aNewMatch.redScore > 168 || aNewMatch.blueScore < 0 || aNewMatch.blueScore > 168)
		{
			cout<<"Warning.  Score of "<<aNewMatch.redScore<<" or of "<<aNewMatch.blueScore<<" is unexpectedly large or small.  The input file may be badly formatted"<<endl;
		}
		if(!setTeams.count(aNewMatch.red[0])) setTeams.insert(aNewMatch.red[0]);
		if(!setTeams.count(aNewMatch.red[1])) setTeams.insert(aNewMatch.red[1]);
		if(!setTeams.count(aNewMatch.red[2])) setTeams.insert(aNewMatch.red[2]);
		if(!setTeams.count(aNewMatch.blue[0])) setTeams.insert(aNewMatch.blue[0]);
		if(!setTeams.count(aNewMatch.blue[1])) setTeams.insert(aNewMatch.blue[1]);
		if(!setTeams.count(aNewMatch.blue[2])) setTeams.insert(aNewMatch.blue[2]);
		
		lstMatches.push_back(aNewMatch);
		
	}
}
// generates a mapping from team #s to row#s and the reverse
void GenerateTeamToRow(const set<int> setTeams,map<int,int>& mapTeamToRow,map<int,int>& mapRowToTeam)
{
	int iCounter = 0;
	for(set<int>::const_iterator i = setTeams.begin();i != setTeams.end();i++)
	{
		mapTeamToRow[*i] = iCounter;
		mapRowToTeam[iCounter] = *i;
		iCounter++;
	}
}
// increments the symmetric points in a matrix to indicate that a pair of teams played with each other
void IncMatrix(TNT::Array2D<double>& M,map<int,int> mapTeamToRow,int iTeam1,int iTeam2)
{
	int iRow1 = mapTeamToRow[iTeam1];
	int iRow2 = mapTeamToRow[iTeam2];

	M[iRow1][iRow2]++;
	if(iRow1 != iRow2)
	{
		M[iRow2][iRow1]++;
	}

}
// increments each team's score for all members of an alliance
void IncScore(TNT::Array1D<double>& S,map<int,int> mapTeamToRow,int aTeams[3],int iScore)
{
	for(int x = 0;x < 3;x++)
	{		
		int iRow = mapTeamToRow[aTeams[x]];
		S[iRow] += iScore;
	}
}
// generates our M and S data.  M is an NxN matrix where each cell(i,j) represents how many times team i played with team j
void GenerateBasicMatrix(const vector<Match>& lstMatches,const map<int,int> mapTeamToRow,TNT::Array2D<double>& M,TNT::Array1D<double>& S)
{
	// assertion: M should be fully zeroed at this point
	for(int x = 0;x < lstMatches.size();x++)
	{
		Match m = lstMatches[x];
		
		IncMatrix(M,mapTeamToRow,m.red[0],m.red[0]);
		IncMatrix(M,mapTeamToRow,m.red[1],m.red[1]);
		IncMatrix(M,mapTeamToRow,m.red[2],m.red[2]);

		IncMatrix(M,mapTeamToRow,m.red[0],m.red[1]);
		IncMatrix(M,mapTeamToRow,m.red[0],m.red[2]);
		IncMatrix(M,mapTeamToRow,m.red[1],m.red[2]);


		IncMatrix(M,mapTeamToRow,m.blue[0],m.blue[0]);
		IncMatrix(M,mapTeamToRow,m.blue[1],m.blue[1]); // teams play with themselves too!
		IncMatrix(M,mapTeamToRow,m.blue[2],m.blue[2]);

		IncMatrix(M,mapTeamToRow,m.blue[0],m.blue[1]);
		IncMatrix(M,mapTeamToRow,m.blue[0],m.blue[2]);
		IncMatrix(M,mapTeamToRow,m.blue[1],m.blue[2]);

		IncScore(S,mapTeamToRow,m.red,m.redScore);
		IncScore(S,mapTeamToRow,m.blue,m.blueScore);
	}
	
}
// zeroes out M
void InitMatrix(TNT::Array2D<double>& M)
{
	for(int x = 0;x < M.dim1();x++)
	{
		for(int y = 0;y < M.dim2();y++)
		{
			M[x][y] = 0;
		}
	}
}
// zeroes out S
void InitS(TNT::Array1D<double>& S)
{
	for(int x = 0;x < S.dim1();x++)
	{
		S[x] = 0;
	}
}
// loads all the teams going to championships
void LoadCMPTeams(string strFile,set<int>& lstTeams)
{
	ifstream inF;
	inF.open(strFile.c_str());

	if(inF.fail())
	{
		cerr<<"Failure at opening "<<strFile<<endl;
	}
	while(!inF.eof())
	{
		int iTeam;
		inF>>iTeam;		
		lstTeams.insert(iTeam);
	}
	
}
// reads in the list of regional files that we need to process
void ReadFileList(string strInput,vector <string>& lstFiles)
{
	ifstream inF;
	inF.open(strInput.c_str());
	while(!inF.eof())
	{
		string str;
		inF>>str;
		lstFiles.push_back(str);
	}
}
// writes the power ratings to <strFile>.out
void WritePowerRatings(string strFile,map<int,int> mapRowToTeam,Array1D<double> PowerRatings)
{
	ofstream outF;
	string strOutFile = strFile + ".out";
	outF.open(strOutFile.c_str());

	for(int x = 0;x < PowerRatings.dim();x++)
	{
		int iTeam = mapRowToTeam[x];
		outF<<iTeam<<"\t"<<PowerRatings[x]<<endl;
	}
	outF.close();
}
int main(int argc, char* argv[])
{
	if(argc != 2)
	{
		cout<<"Wrong number of arguments for scripted running, defaulting to using reglist.txt"<<endl;
		argv[1] = "reglist.txt";
	}
	
	vector <string> lstFiles; // all the regional text files to process
	map<int,double> mapTeamToTopScore;
	map<int,double> mapTeamToLastScore;
	map<int,string> mapTeamToBestReg;
	map<int,string> mapTeamToLastReg;
	map<int,bool> mapTeamToBestSet;
	map<int,int> mapTeamToRegCount;
	
	set<int> setAllTeams; // all teams
	set <int> lstChamps; // teams going to champs 
	ReadFileList(argv[1],lstFiles);

	LoadCMPTeams("c:\\cmpTeams.txt",lstChamps); // list of teams going to championships.  If this vector has more than zero entries, then we only print out data for teams going to champs

	for(int x = 0;x < lstFiles.size();x++)
	{		
		set<int> setTeams;		
		map<int,int> mapTeamToRow;
		map<int,int> mapRowToTeam;
		
		vector<Match > lstMatches;
		
		string strFile;		
		strFile = lstFiles[x];
		
		ReadTeamData(strFile,lstMatches,setTeams);					// reads match data
		
		GenerateTeamToRow(setTeams,mapTeamToRow,mapRowToTeam);		// maps each team to a number between 0 and N-1
				
		TNT::Array2D<double> M(setTeams.size(),setTeams.size());	// M
		TNT::Array1D<double> S(setTeams.size());					// S

		InitMatrix(M);
		InitS(S);

		GenerateBasicMatrix(lstMatches,mapTeamToRow,M,S); // makes M from the match list data		

		JAMA::LU<double> aMatrixSolver(M);
		Array1D<double> PowerRatings = aMatrixSolver.solve(S); // solves MO = S.  PowerRatings = solved O

	    
		WritePowerRatings(strFile,mapRowToTeam,PowerRatings); // writes the output for this regional to <strFile>.out

		// records a team's last and best power ratings
		for(int x = 0;x < PowerRatings.dim();x++)
		{
			if(lstChamps.size() == 0 || lstChamps.count(mapRowToTeam[x]))
			{
				
				double dPwr = PowerRatings[x];
				int iTeam = mapRowToTeam[x];
				if(setAllTeams.count(iTeam) == 0) // if this team hasn't made it into our list of all teams, add it
				{					
					setAllTeams.insert(iTeam);
				}
				mapTeamToRegCount[iTeam]++;
				if(dPwr > mapTeamToTopScore[iTeam] || mapTeamToBestSet[iTeam] == false)
				{
					mapTeamToTopScore[iTeam] = dPwr;
					mapTeamToBestReg[iTeam] = strFile;
					mapTeamToBestSet[iTeam]  =true;
				}
				mapTeamToLastScore[iTeam] = dPwr;
				mapTeamToLastReg[iTeam] = strFile;
			}
		}
	} // end of file loop
	
	// this prints out a team's last OPR and where it occurred.  If a team's best OPR occurred elsewhere, it prints that out
	cout<<"Team\tRegional Count\tLast OPR\tLast Regional\tBest OPR\tBest Regional"<<endl;
	for(set<int>::iterator i = setAllTeams.begin();i != setAllTeams.end();i++)
	{
		int iTeam = *i;

		cout<<iTeam<<"\t"<<mapTeamToRegCount[iTeam]<<"\t"<<mapTeamToLastScore[iTeam]<<"\t"<<mapTeamToLastReg[iTeam];
		
		cout<<"\t"<<mapTeamToTopScore[iTeam]<<"\t"<<mapTeamToBestReg[iTeam];			
		
		cout<<endl;
	}

	return 0;
}
Required files:
-Reglist.txt - a list of regional data to be processed. One file per line. Note that it will output a file for each file in this list, and that will be the OPR for that regional
-cmpteams.txt - A list of teams that you're interested in (for example, a list of teams going to championships). One team per line.
-the JAMA math library (see attached file). This is from the NIST, it should be ok to redistribute. You can get the actual file here: http://math.nist.gov/tnt/download.html

------------
Licensing: None, do whatever you want with it. Feel free to give me credit or something though (but you don't have to, if it'd cramp your style).
Attached Files
File Type: zip jama.zip (69.7 KB, 79 views)
File Type: txt sampleRegInput.txt (3.8 KB, 78 views)
File Type: txt sample_CMPTeams.txt (1.7 KB, 77 views)
File Type: txt sample_reglist.txt (10 Bytes, 73 views)
Reply With Quote