mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 22:02:58 -06:00
494 lines
17 KiB
C++
494 lines
17 KiB
C++
#include "VegaMain.h"
|
|
#include "Tournament.h"
|
|
|
|
extern int CompareScoreName(const void *pid1, const void *pid2);
|
|
|
|
void RoundData::PrepareRRPairing(int nplayer)
|
|
//
|
|
// fill the pairingRR[][] array
|
|
//
|
|
{ int i, round, npairs, nr, code;
|
|
int idwhite, idblack, result;
|
|
|
|
// GenerateRR( nplayer, pairingRR);
|
|
if (PLAY_SYSTEM == ROUND_ROBIN) {
|
|
GenerateRRCalendar( nplayer, pairingRR);
|
|
}
|
|
else if (PLAY_SYSTEM == ROUND_ROBIN2) {
|
|
nr = GenerateRRCalendar( nplayer, pairingRR);
|
|
for (round=nr+1; round<=2*nr; round++) // adesso ripete scambiando i colori
|
|
{ npairs = pairingRR[0][round] = pairingRR[0][round-nr] ;
|
|
for (i=1; i<=npairs; i++)
|
|
{ code = pairingRR[i][round - nr];
|
|
DecodeResult(code, &idwhite, &idblack, &result);
|
|
pairingRR[i][round] = CodifyResult( idblack, idwhite, result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int RoundData::GenerateRRCalendar( int nplayer, int pairs[][N_ROUND_MAX_RR+1])
|
|
//
|
|
// purpose: generate the round robin calendar
|
|
//
|
|
// nplayer = number of players
|
|
// pairs[round][] = array containing the calendar
|
|
//
|
|
{ int arr[N_ROUND_MAX_RR+1]; // contains 0 (busy) and 1 (free) relative to each player
|
|
int ID_last_black=1; // who played with black in the previous round
|
|
int i, j, np2, rounds;
|
|
int count, color;
|
|
int pairing[2][N_ROUND_MAX_RR/2 + 2 ];
|
|
int odd=0, bye;
|
|
|
|
if (nplayer&1) {
|
|
odd = 1;
|
|
nplayer++;
|
|
np2=nplayer/2;
|
|
rounds=nplayer-1;
|
|
}
|
|
else {
|
|
np2=nplayer/2;
|
|
rounds=nplayer-1;
|
|
}
|
|
arr[nplayer] = 0; // player "nplayer" will be always busy
|
|
for (i=1; i<=rounds; i++)
|
|
{ for (j=1; j<=nplayer-1; j++) arr[j] = 1; //free all the player except "nplayer"
|
|
color = i & 1;
|
|
pairing[color][1] = nplayer; //set the first couple with their color
|
|
pairing[ !color ][1] = ID_last_black;
|
|
arr[ID_last_black] = 0; // ID_last_black is set to busy
|
|
|
|
j = ID_last_black;
|
|
count = 1;
|
|
|
|
while (count<np2) { // look for white free player: from left to right
|
|
if (j>=nplayer) j=1; // restart the cycle
|
|
if (arr[j]) {
|
|
count++;
|
|
pairing[0][count] = j; // set the white
|
|
arr[j] = 0; // set to busy
|
|
j++;
|
|
} else j++;
|
|
}
|
|
count = np2+1;
|
|
while (count>2) { // look for black free player: from right to left
|
|
if (j>=nplayer) j=1; // restart the cycle
|
|
if (arr[j]) {
|
|
count--;
|
|
pairing[1][count] = j; // set the white
|
|
arr[j] = 0; // set to busy
|
|
j++;
|
|
} else j++;
|
|
}
|
|
ID_last_black = pairing[1][np2];
|
|
|
|
pairs[0][i] = np2;
|
|
count = 1;
|
|
for (j=1; j<=np2; j++) {
|
|
if ( !odd || (odd && pairing[0][j]!=nplayer && pairing[1][j]!=nplayer) ) {
|
|
pairs[count][i] = pairing[0][j]*10000 + pairing[1][j]*10 + 9;
|
|
count++;
|
|
}
|
|
else if ( odd && pairing[0][j]==nplayer )
|
|
//bye = pairing[1][j]*10000 + 0 + 2; // set the BYE //NEW
|
|
bye = pairing[1][j]*10000 + 0 + 8; // set the BYE
|
|
else if ( odd && pairing[1][j]==nplayer )
|
|
//bye = pairing[0][j]*10000 + 0 + 2; // set the BYE
|
|
bye = pairing[0][j]*10000 + 0 + 8; // set the BYE //NEW
|
|
}
|
|
if ( odd ) pairs[np2][i] = bye;
|
|
}
|
|
return rounds;
|
|
}
|
|
|
|
|
|
|
|
void RoundData::SaveRoundRR(int flag)
|
|
// flag = 0, save the file .rnd
|
|
// flag > 0, save the backup file at round "round"
|
|
{ int i, j, npair, nr;
|
|
String filename;
|
|
if (flag==0) filename = PATHDIR + FILETORNEO + ".rnd";
|
|
else filename = TD.PATHDIR + FILETORNEO + AsString(TD.currentRound) + ".rnd";
|
|
FileOut out(filename);
|
|
String s;
|
|
int round = currentRound;
|
|
|
|
out.PutLine( NFormat("%2d # current round", currentRound ) );
|
|
out.PutLine( " 1 # not used" );
|
|
out.PutLine( " 1 # not used" );
|
|
out.PutLine( " 1 # not used" );
|
|
// out.PutLine( NFormat("%2d # IS_GET_RESULT_DONE", IS_GET_RESULT_DONE) );
|
|
out.PutLine( NFormat("%2d # STATUS_ROUND", STATUS_ROUND) );
|
|
out.PutLine( " 1 # not used" );
|
|
out.PutLine( " 1 # not used" );
|
|
|
|
if (currentRound>0) {
|
|
out.PutLine( "# players color" );
|
|
for (i=1; i<=NPlayer; i++)
|
|
{ String s = NFormat("%3d ", i);
|
|
for (j=1; j<=round; j++)
|
|
{ s << NFormat("%3d ", playerColorRR[i][j]);
|
|
}
|
|
out.PutLine( s );
|
|
}
|
|
|
|
out.PutLine("# players opponent" );
|
|
for (i=1; i<=NPlayer; i++)
|
|
{ String s = NFormat("%3d ", i);
|
|
for (j=1; j<=round; j++) s << NFormat("%3d ", playerOpponentRR[i][j]);
|
|
out.PutLine( s );
|
|
}
|
|
|
|
out.PutLine( "# round results");
|
|
for (i=1; i<=NPlayer; i++)
|
|
{ String s = NFormat("%3d ", i);
|
|
for (j=1; j<=round; j++) s << NFormat("%d ", roundResultRR[i][j]);
|
|
out.PutLine( s );
|
|
}
|
|
}
|
|
out.PutLine( "# code pairing");
|
|
if (NPlayer & 1) { npair = (NPlayer-1)/2 + 1; nr = NPlayer;}
|
|
else { npair = NPlayer/2; nr = NPlayer-1;}
|
|
if (PLAY_SYSTEM == ROUND_ROBIN2) nr = 2*nr;
|
|
for (i=0; i<=npair; i++)
|
|
{ String s = "";
|
|
for (j=1; j<=nr; j++) s << NFormat("%7d ", pairingRR[i][j]);
|
|
out.PutLine( s );
|
|
}
|
|
out.Close();
|
|
}
|
|
|
|
void RoundData::LoadRoundRR(String filename)
|
|
{ FileIn in(filename);
|
|
int i, j, nr, npair, numPlayers = TD.NPlayer;// NPlayer is known from the *.veg file
|
|
/* if ((in=fopen(filename, "r"))==NULL)
|
|
{ WarningMessage(sss[S_NOREADR], sss[S_CLOSE]);
|
|
return;
|
|
}
|
|
*/
|
|
TD.currentRound = StrIntValue( in.GetLine() );
|
|
in.GetLine(); //skip this line
|
|
in.GetLine(); //skip this line
|
|
in.GetLine(); //skip this line
|
|
STATUS_ROUND = StrIntValue( in.GetLine() );
|
|
in.GetLine(); //skip this line
|
|
in.GetLine(); //skip this line
|
|
|
|
if (currentRound>0) {
|
|
in.GetLine(); //skip this line // "# players color\n"
|
|
for (i=1; i<=numPlayers; i++)
|
|
{ Vector<String> field = Split(in.GetLine(), ' ');
|
|
//field.GetCount() coincide with currentRound
|
|
for (j=1; j<field.GetCount(); j++) playerColorRR[i][j] = StrIntValue( field[j] );
|
|
}
|
|
|
|
in.GetLine(); //skip this line // "# players opponent\n"
|
|
for (i=1; i<=numPlayers; i++)
|
|
{ Vector<String> field = Split(in.GetLine(), ' ');
|
|
//field.GetCount() coincide with currentRound
|
|
for (j=1; j<field.GetCount(); j++) playerOpponentRR[i][j] = StrIntValue( field[j] );
|
|
}
|
|
|
|
in.GetLine(); //skip this line // "# players result\n"
|
|
for (i=1; i<=numPlayers; i++)
|
|
{ Vector<String> field = Split(in.GetLine(), ' ');
|
|
//field.GetCount() coincide with currentRound
|
|
for (j=1; j<field.GetCount(); j++) roundResultRR[i][j] = StrIntValue( field[j] );
|
|
}
|
|
}
|
|
// now reads the pairing
|
|
in.GetLine(); //skip this line // "# code pairing\n"
|
|
if (numPlayers & 1) { npair = (numPlayers-1)/2 + 1; nr = numPlayers;}
|
|
else { npair = numPlayers/2; nr = numPlayers-1;}
|
|
if (PLAY_SYSTEM == ROUND_ROBIN2) nr = 2*nr;
|
|
for (i=0; i<=npair; i++)
|
|
{ Vector<String> field = Split(in.GetLine(), ' ');
|
|
for (j=1; j<=field.GetCount(); j++) pairingRR[i][j] = StrIntValue( field[j-1] );
|
|
}
|
|
in.Close();
|
|
}
|
|
|
|
|
|
void RoundData::AdjournCrossTableRR(int round)
|
|
//
|
|
// NOTE: it is called just after the iserting of the result of the round,
|
|
// so at round N, the state refer to the previous N-1 rounds
|
|
//
|
|
{ int id, j, np;
|
|
int jopp;
|
|
double score, aro, buc, min, max;
|
|
double sb, modifiedscore[N_PLAYER_MAX+1];
|
|
|
|
for (id=1; id<=NPlayer; id++)
|
|
{ //set ARO
|
|
aro = modifiedscore[id] = 0.;
|
|
np = 0;
|
|
for (j=1; j<=round; j++)
|
|
{ jopp = TD.playerOpponentRR[id][j];
|
|
if (jopp != 0 && (TD.roundResultRR[id][j]<3)) // no BYE o game won for forfait
|
|
{ aro += (float) player[jopp].RATING;
|
|
np++;
|
|
}
|
|
}
|
|
if (np != 0) player[id].tiebreak[ARO] = (float) (aro/np);
|
|
else player[id].tiebreak[ARO] = 0.;
|
|
|
|
//set score
|
|
score = 0.;
|
|
for (j=1; j<=round; j++) {
|
|
if (TD.roundResultRR[id][j]==1)
|
|
{ score += WON;
|
|
modifiedscore[id] += WON;
|
|
}
|
|
else if (TD.roundResultRR[id][j]==3) // win forfeit
|
|
{ score += WON;
|
|
modifiedscore[id] += DRAW;
|
|
}
|
|
else if (TD.roundResultRR[id][j]==4) // lost forfeit
|
|
{
|
|
modifiedscore[id] += DRAW;
|
|
}
|
|
else if (TD.roundResultRR[id][j]==5) // null game
|
|
{
|
|
modifiedscore[id] += 0.; //do nothing
|
|
}
|
|
else if (TD.roundResultRR[id][j]==2 || TD.roundResultRR[id][j]==6)
|
|
{ score += DRAW;
|
|
modifiedscore[id] += DRAW;
|
|
}
|
|
}
|
|
player[id].tiebreak[SCORE] = (float) score;
|
|
|
|
//Exclamation(AsString(id) + " " + AsString(player[id].tiebreak[SCORE]) + " "+ AsString(modifiedscore[id]) );
|
|
}
|
|
|
|
for (id=1; id<=NPlayer; id++)
|
|
{ //set Buccholz
|
|
buc = 0.;
|
|
min = 10000.;
|
|
max = 0.;
|
|
for (j=1; j<=round; j++)
|
|
{ jopp = TD.playerOpponentRR[id][j]; //BUG! was TD.playerOpponent
|
|
if ( (jopp != 0) && (TD.roundResultRR[id][j]<3) ) // no BYE, no forfait
|
|
{ buc += modifiedscore[jopp];
|
|
|
|
if (modifiedscore[jopp]<min) min = modifiedscore[jopp];
|
|
if (modifiedscore[jopp]>max) max = modifiedscore[jopp];
|
|
}
|
|
else if ( (jopp == 0) && (TD.roundResultRR[id][j]==5) ) // odd number
|
|
{ buc += 0.; //do nothing
|
|
}
|
|
else // BYE or forfait
|
|
{ buc += modifiedscore[id]; // sum his score to the bucholz
|
|
|
|
if (modifiedscore[id]<min) min = modifiedscore[id];
|
|
if (modifiedscore[id]>max) max = modifiedscore[id];
|
|
}
|
|
}
|
|
if (buc > min) player[id].tiebreak[BUC_CUT1] = (float) (buc-min);
|
|
else player[id].tiebreak[BUC_CUT1] = 0.;
|
|
|
|
if (buc - min > max ) player[id].tiebreak[BUC_MED] = (float) (buc - min - max);
|
|
else player[id].tiebreak[BUC_MED] = 0.;
|
|
|
|
player[id].tiebreak[BUC_TOT] = (float) buc;
|
|
}
|
|
|
|
for (id=1; id<=NPlayer; id++)
|
|
{ //set Sonneborg-Berger
|
|
sb = 0.;
|
|
for (j=1; j<=round; j++)
|
|
{ jopp = TD.playerOpponentRR[id][j];
|
|
if ( jopp != 0 && TD.roundResultRR[id][j]==1 ) // win, no forfait
|
|
sb += modifiedscore[jopp];
|
|
else if ( (jopp != 0) && (TD.roundResultRR[id][j]==2) ) // draw, no forfait
|
|
sb += modifiedscore[jopp]/2.;
|
|
else if ( (jopp == 0) || (TD.roundResultRR[id][j]==5) )
|
|
sb += 0.; // do nothing
|
|
else if ( ((jopp == 0) || (TD.roundResultRR[id][j]>2)) && (TD.roundResult[id][j] != 8) )
|
|
sb += modifiedscore[id]/2.; // sum his half score to the bucholz
|
|
}
|
|
player[id].tiebreak[SONN_BERG] = (float) sb;
|
|
}
|
|
// adjourn the rest of cross table
|
|
CalcVarElo(NPlayer);
|
|
// PrepareHTMLindex();
|
|
NationalPlayerCard(NPlayer); // here is calculated the performance rating
|
|
setAPRO(round, NPlayer);
|
|
ShowTableScore(NPlayer);
|
|
CrossTableRoundRobin(NPlayer);
|
|
ShowTableCategoryScore(NPlayer);
|
|
}
|
|
|
|
|
|
void RoundData::CrossTableRoundRobin(int nplayers)
|
|
//
|
|
// PURPOSE: sort the cross table according the ranking
|
|
//
|
|
// INPUT VARIABLE
|
|
// nplayers = number of players to be sorted
|
|
//
|
|
{ int i, k, j, nr, iw, ib, code, res, npairs, id1, id2, jround;
|
|
int arr[N_PLAYER_MAX_RR+2];
|
|
String stringa, mm[N_PLAYER_MAX_RR+1][N_PLAYER_MAX_RR+1];
|
|
String str1, str2, str3, remark = "\nRemark:\n";
|
|
|
|
FileOut save(PATHDIR + "crosstbl.txt");
|
|
nr = currentRound;
|
|
if (PLAY_SYSTEM == ROUND_ROBIN2)
|
|
{ str1 = "-- ";
|
|
str2 = "- ";
|
|
str3 = " ";
|
|
}
|
|
else { str1 = "- ";
|
|
str2 = " ";
|
|
str3 = " ";
|
|
}
|
|
|
|
for (i=0; i<nplayers; i++)
|
|
{ arr[i] = i+1; // set the id of each player in the array to be sort
|
|
}
|
|
qsort(arr, nplayers, sizeof(int), CompareScoreName);
|
|
|
|
for (i=0; i<nplayers; i++)
|
|
for (j=0; j<nplayers; j++) mm[i][j] = " ";
|
|
|
|
for (k=0; k<nplayers; k++)
|
|
{ id1=arr[k];
|
|
for(j=0; j<nplayers; j++)
|
|
{ id2=arr[j];
|
|
for(jround=1; jround<=nr; jround++)
|
|
{ if (id2 == playerOpponentRR[id1][jround])
|
|
{ if (roundResultRR[id1][jround]==0 || roundResultRR[id1][jround]==4)
|
|
{ mm[k][j] << "0";
|
|
// mm[j][k] << "1";
|
|
}
|
|
else if (roundResultRR[id1][jround]==1 || roundResultRR[id1][jround]==3)
|
|
{ mm[k][j] << "1";
|
|
//mm[j][k] << "0";
|
|
}
|
|
else if (roundResultRR[id1][jround]==2 || roundResultRR[id1][jround]==6 || roundResultRR[id1][jround]==8)
|
|
{ mm[k][j] << "½";
|
|
//mm[j][k] << "½";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
save<< NAMETORNEO << "\n";
|
|
save<< PLACETORNEO << " - " << DATATORNEO_B <<", "<<DATATORNEO_E << "\n\n";
|
|
save<< t_(" Cross Table");
|
|
save<< "\n\n N NAME Elo Tit Fed score |";
|
|
for(j=1; j<=nplayers; j++) save << NFormat("%3d ", j);
|
|
save << "\n----------------------------------------------";
|
|
for(j=1; j<=nplayers; j++) save << "----";
|
|
save << "\n";
|
|
|
|
for (k=0; k<nplayers; k++)
|
|
{ i=arr[k];
|
|
save << NFormat( "%3d %-20.20s %4d %3.3s %3.3s %5.1f | ",
|
|
k+1, // k is the ranking position now
|
|
player[i].name,
|
|
player[i].RATING,
|
|
player[i].codetitle,
|
|
player[i].country,
|
|
player[i].tiebreak[SCORE]);
|
|
for(j=0; j<nplayers; j++)
|
|
{ if (j==k) save<< " * ";
|
|
else if (strlen(mm[k][j]) == 1) save<<mm[k][j]<<str1;
|
|
else if (strlen(mm[k][j]) == 2) save<<mm[k][j]<<str2;
|
|
else save<<mm[k][j]<<str3;
|
|
}
|
|
save<<"\n";
|
|
}
|
|
|
|
//add notes to the cross table
|
|
for(jround=1; jround<=currentRound; jround++) {
|
|
npairs = pairingRR[0][jround];
|
|
for (i=1; i<=npairs; i++)
|
|
{ code = pairingRR[i][jround];
|
|
DecodeResult(code, &iw, &ib, &res);
|
|
if ( (res!=0 && res!=1 && res!=5) && iw!=0 && ib!=0 )
|
|
remark << NFormat("round %2d: %-20.20s - %-20.20s = %s\n",
|
|
jround, player[iw].name, player[ib].name, LabelResult(res, code, jround) );
|
|
}
|
|
}
|
|
save << remark;
|
|
|
|
save<<"\n Generated by Vega - www.vegachess.com\n";
|
|
save.Close();
|
|
|
|
|
|
// produce HTML files
|
|
FileOut savehtml(PATHDIRWWW + "crosstbl.html");
|
|
savehtml << TD.HTMLCommonHeader();
|
|
savehtml << HTMLNavBar();
|
|
savehtml << "<h2>" << NAMETORNEO << "</h2>\n";
|
|
savehtml << "<h2>" << PLACETORNEO << " - " << DATATORNEO_B <<", "<<DATATORNEO_E << "</h2>\n";
|
|
savehtml << "<h3> " << t_(" Cross Table at round ")<< currentRound<< "</h3>\n";
|
|
savehtml << "<table bgcolor=#efefef cellpadding=4 cellspacing=0 border=0> \n";
|
|
savehtml << "<tbody>\n";
|
|
savehtml << "<tr>";
|
|
savehtml << "<th> N </th> <th width=\"180\">NAME</th> <th>Rat</th> <th>PRat</th> <th>Fed</th> <th>Scr</th>\n";
|
|
|
|
for(j=1; j<=nplayers; j++) savehtml << "<th> " << j << " </th>";
|
|
savehtml << "</tr> \n";
|
|
|
|
for (k=0; k<nplayers; k++)
|
|
{ i=arr[k];
|
|
stringa = NFormat("<td>%3d</td> <td> <a href=\"playercard.html#%d\"> %s %s </a> </td> <td>%4d</td> <td bgcolor=\"#EEEEEE\">%4d</td> <td>%3.3s</td> <td>%4.1f</td>",
|
|
k+1, // k is the ranking position now
|
|
i, player[i].codetitle, player[i].name,
|
|
player[i].RATING,
|
|
(int) player[i].tiebreak[PER_RAT],
|
|
player[i].country,
|
|
player[i].tiebreak[SCORE]);
|
|
if ((k+1)&1) savehtml << "<tr bgcolor=#e2e2f8> ";
|
|
else savehtml << "<tr bgcolor=#ffffff> ";
|
|
savehtml << stringa;
|
|
for(j=0; j<nplayers; j++)
|
|
{ if (j==k) savehtml<< "<td bgcolor=\"#000000\" width=\"27\"> </td>";
|
|
else if (strlen(mm[k][j]) == 1) savehtml<< "<td align=\"center\">" << mm[k][j]<<str1<< "</td>";
|
|
else if (strlen(mm[k][j]) == 2) savehtml<<"<td align=\"center\">" << mm[k][j]<<str2<< "</td>";
|
|
else savehtml<<"<td align=\"center\">" << mm[k][j]<<str3<< "</td>";
|
|
}
|
|
savehtml<<"\n";
|
|
savehtml << "</tr>\n";
|
|
}
|
|
savehtml << "</tbody>\n";
|
|
savehtml << "</table>\n";
|
|
savehtml << "<br> <p align=\"center\"><a href=\"http://www.vegachess.com\"> Generated by Vega </a></p> \n";
|
|
savehtml << "</DIV></body>\n</html>\n";
|
|
savehtml.Close();
|
|
}
|
|
|
|
void RoundData::RoundRobinCalendar()
|
|
//
|
|
// PURPOSE: show all the tournament calendar
|
|
//
|
|
{ int i, j, code, iw, ib, res, npairs, nround;
|
|
String name, stringa;
|
|
FileOut savepair(PATHDIR + "pairsRR.txt");
|
|
if (NPlayer & 1) nround = NPlayer;
|
|
else nround = NPlayer-1;
|
|
|
|
if (PLAY_SYSTEM==ROUND_ROBIN2) nround = 2*nround;
|
|
for (j=1; j<=nround; j++)
|
|
{ savepair << NAMETORNEO << t_(" : Round ") << j << "\n";
|
|
savepair << "======================================================================\n";
|
|
npairs = pairingRR[0][j];
|
|
for (i=1; i<=npairs; i++)
|
|
{ code = pairingRR[i][j];
|
|
DecodeResult(code, &iw, &ib, &res);
|
|
savepair << NFormat("%3d = %3d %-20.20s - %3d %-20.20s = ...-...\n",
|
|
i, iw, player[iw].name, ib, player[ib].name);
|
|
savepair << "----------------------------------------------------------------------\n";
|
|
}
|
|
savepair << "\n\n";
|
|
}
|
|
savepair<<"\n Generated by Vega - www.vegachess.com\n";
|
|
savepair.Close();
|
|
}
|