23 0, 7, 14, 21, 29, 36, 43, 50, 57, 65,
24 72, 80, 87, 95, 102, 110, 117, 125, 133, 141,
25 149, 158, 166, 175, 184, 193, 202, 211, 220, 230,
26 240, 251, 262, 273, 284, 296, 309, 322, 336, 351,
27 366, 383, 401, 422, 444, 470, 501, 538, 589, 677,
38 if (percentage > 100) { percentage = 100; }
39 uint performance = oppAvg;
40 if (percentage < 50) {
56 uint diff = (player > oppAvg) ? player - oppAvg : oppAvg - player;
65 expected = 50 - expected;
66 int cutoff = (percentage > expected) ? 5 : -5;
67 return (((
int)percentage - (int)expected) * (int)games + cutoff) / 10;
79 if (rating <= 2250) {
return 0; }
80 return 1 + ((rating - 2251) / 25);
89 const eloT Margin = 350;
92 else if (player - opponent > Margin)
93 return player - Margin;
94 else if (opponent - player > Margin)
95 return player + Margin;
120 result = p2->
elo - p1->
elo;
135 PlayerData[pcount] = NULL;
143 ShowTitles = ShowElos = ShowCountries = ShowTallies = SwissColors = ShowAges =
true;
144 ShowTiebreaks =
false;
147 DecimalPointChar =
'.';
148 APAColumnNums =
false;
152 Crosstable::Destroy ()
154 for (
uint player=0; player < PlayerCount; player++) {
158 my_Tcl_Free((
char*)pdata->
name);
160 delete[] pdata->
name;
162 for (
uint opp = 0; opp < PlayerCount; opp++) {
164 while (clash != NULL) {
167 my_Tcl_Free((
char*)clash);
176 my_Tcl_Free((
char*)pdata);
190 for (
uint i = 0; i < PlayerCount; i++) {
191 if (PlayerData[i]->
id ==
id) {
194 if (elo > PlayerData[i]->elo) { PlayerData[i]->
elo = elo; }
201 PlayerData[PlayerCount] = pdata;
223 if (SpellCheck != NULL ) {
250 for (i=0; i < PlayerCount; i++) {
251 if (PlayerData[i]->
id == white) { whiteIdx = i;
break; }
253 for (i=0; i < PlayerCount; i++) {
254 if (PlayerData[i]->
id == black) { blackIdx = i;
break; }
275 whiteClash->
next = NULL;
276 pwhite->
lastClash[blackIdx] = whiteClash;
284 blackClash->
next = NULL;
285 pblack->
lastClash[whiteIdx] = blackClash;
287 whiteClash->
result = result;
289 whiteClash->
gameNum = gameNumber;
290 blackClash->
gameNum = gameNumber;
300 if (round > MaxRound) { MaxRound =
round; }
304 if (pwhite->
clashCount[blackIdx] > MaxClashes) {
311 if (result && pblack->
elo > 0) {
315 if (result && pwhite->
elo > 0) {
321 if (date !=
ZERO_DATE && date < FirstDate) {
327 pwhite->
score += (ThreeWin ? 6 : 2);
328 if (pblack->
elo > 0) {
333 pblack->
score += (ThreeWin ? 6 : 2);
334 if (pwhite->
elo > 0) {
339 pwhite->
score += (ThreeWin ? 2 : 1);
340 pblack->
score += (ThreeWin ? 2 : 1);
341 if (pblack->
elo > 0) {
344 if (pwhite->
elo > 0) {
351 ResultCount[result]++;
363 for (player = 0; player < PlayerCount; player++) {
370 for (
uint opp = 0; opp < PlayerCount; opp++) {
371 if (opp == player) {
continue; }
373 while (clash != NULL) {
377 tb += PlayerData[opp]->
score;
381 tb += oppScore + oppScore;
409 uint completeAllPlayAll = (PlayerCount * (PlayerCount - 1)) / 2;
425 for (
uint i=0; i < PlayerCount; i++) {
426 if (PlayerData[i]->elo > 0) { total += PlayerData[i]->
elo; count++; }
428 if (count == 0) {
return 0; }
429 return (
eloT) (total / count);
440 CurrentGame = currentGame;
441 if (playerLimit == 0 || playerLimit > PlayerCount) {
442 playerLimit = PlayerCount;
444 if (playerLimit < 2) {
return; }
451 for (player=0; player < PlayerCount; player++) {
452 SortedIndex[player] = player;
453 InvertedIndex[player] = player;
455 for (
uint first=0; first < PlayerCount-1; first++) {
456 for (
uint second = first+1; second < PlayerCount; second++) {
458 PlayerData[SortedIndex[second]],
460 uint temp = SortedIndex[first];
461 SortedIndex[first] = SortedIndex[second];
462 SortedIndex[second] = temp;
466 for (player=0; player < PlayerCount; player++) {
467 InvertedIndex[SortedIndex[player]] = player;
471 if (playerLimit > 99) { PlayerNumWidth = 3; }
475 for (player = 0; player < PlayerCount; player++) {
477 if (len > LongestNameLen) { LongestNameLen = len; }
481 PrintRatings =
false;
483 PrintCountries =
false;
485 PrintTiebreaks =
true;
487 for (player = 0; player < PlayerCount; player++) {
489 if (pd->
elo > 0) { PrintRatings =
true; }
490 if (pd->
title[0] != 0) { PrintTitles =
true; }
491 if (pd->
country[0] != 0) { PrintCountries =
true; }
502 if (! ShowElos) { PrintRatings =
false; }
503 if (! ShowTitles) { PrintTitles =
false; }
504 if (! ShowCountries) { PrintCountries =
false; }
505 if (! ShowTallies) { PrintTallies =
false; }
506 if (! ShowAges) { PrintAges =
false; }
507 if (! ShowTiebreaks) { PrintTiebreaks =
false; }
525 NewLine = BlankRowLine =
"<br>";
528 StartTable =
"\n<p><table border=1 cellspacing=0 cellpadding=4>\n";
529 EndTable =
"</table></p>\n";
533 BlankRowLine =
"<tr></tr>\n";
536 StartRightCol =
"<td align=right>";
537 EndRightCol =
"</td>";
538 StartBoldCol =
"<th>";
539 EndBoldCol =
"</th>";
543 EndTable =
"\\end{tabular}\n\n";
547 BlankRowLine =
"\\\\\n";
552 StartBoldCol =
"\\bf ";
557 LineWidth = LongestNameLen + 6;
558 if (PrintRatings) { LineWidth += 16; }
559 if (PrintTitles) { LineWidth += 4; }
560 if (PrintCountries) { LineWidth += 4; }
561 if (PrintAges) { LineWidth += 4; }
564 LineWidth += 16 + PlayerNumWidth;
565 LineWidth += (PlayerNumWidth + (SwissColors ? 3 : 2)) * MaxRound;
566 if (PrintTiebreaks) { LineWidth += 5; }
568 LineWidth += 16 + PlayerNumWidth;
569 if (playerLimit == 2) {
570 LineWidth += (MaxClashes + 1);
572 LineWidth += (playerLimit * (MaxClashes + 1));
574 if (PrintTiebreaks) { LineWidth += 7; }
576 LineWidth = (LongestNameLen * 2) + 17 + MaxClashes;
577 if (PrintRatings) { LineWidth += 10; }
578 if (PrintTitles) { LineWidth += 8; }
579 if (PrintCountries) { LineWidth += 8; }
580 if (PrintAges) { LineWidth += 8; }
585 PrintAllPlayAll (dstr, playerLimit);
588 PrintKnockout (dstr, playerLimit);
592 PrintSwiss (dstr, playerLimit);
598 sprintf (stemp,
"%u game%s: %s%u %s%u %s%u",
608 sprintf (stemp,
" %s%u",
619 Crosstable::PrintDashesLine (
DString * dstr)
623 dstr->
Append (
"\\hline\n");
629 for (
uint i=0; i < LineWidth; i++) {
640 sprintf (stemp,
"<pi %s>", pdata->
name);
643 sprintf (stemp,
"%-*s ", LongestNameLen, pdata->
name);
644 dstr->
Append (StartCol, stemp, EndCol);
648 sprintf (stemp,
"%4u ", pdata->
elo);
651 strcpy (stemp,
" - ");
656 dstr->
Append (StartRightCol, stemp, EndRightCol);
664 sprintf (stemp,
" - ");
666 sprintf (stemp,
"%3s ", pdata->
title);
668 dstr->
Append (StartCol, stemp, EndCol);
673 sprintf (stemp,
" - ");
680 dstr->
Append (StartCol, stemp, EndCol);
682 if (PrintCountries) {
684 sprintf (stemp,
" - ");
686 sprintf (stemp,
"%-3s ", pdata->
country);
688 dstr->
Append (StartCol, stemp, EndCol);
696 if (!PrintRatings) {
return; }
702 int performance =
Performance(oppAvgRating, percentage);
703 if (performance > 0 && performance < 5000) {
708 sprintf (stemp,
"%4d %+3d", performance, change);
710 sprintf (stemp,
"%4d ", performance);
712 dstr->
Append (
" ", StartRightCol, stemp, EndRightCol);
724 sprintf (stemp,
"%3d%c%1d%%", per_score / 10 , DecimalPointChar, per_score % 10);
725 dstr->
Append (
" ", StartRightCol, stemp, EndRightCol);
729 Crosstable::PrintAllPlayAll (
DString * dstr,
uint playerLimit)
734 dstr->
Append (StartTable);
736 dstr->
Append (
"\\begin{tabular}{rl");
737 if (PrintRatings) { dstr->
Append (
"r"); }
738 if (PrintTitles) { dstr->
Append (
"r"); }
739 if (PrintAges) { dstr->
Append (
"r"); }
740 if (PrintCountries) { dstr->
Append (
"l"); }
741 dstr->
Append (
"r@{ / }r");
742 if (PrintTiebreaks) { dstr->
Append (
"r"); }
743 for (
uint i=0; i < playerLimit; i++) {
745 if (i < playerLimit-1) { dstr->
Append (
"@{ }"); }
747 if (PrintRatings) { dstr->
Append (
"r"); }
752 dstr->
Append (
"<th></th> <th>Player</th> ");
754 dstr->
Append (
" & \\bf Player & ");
756 strPad (stemp,
"", LongestNameLen + 2 + PlayerNumWidth,
' ');
760 dstr->
Append (StartBoldCol,
" Rtng", EndBoldCol);
763 dstr->
Append (StartBoldCol,
" Ti", EndBoldCol);
766 dstr->
Append (StartBoldCol,
" Age", EndBoldCol);
768 if (PrintCountries) {
769 dstr->
Append (StartBoldCol,
" Nat", EndBoldCol);
773 dstr->
Append (
" \\multicolumn{2}{c}{\\bf Score} & ");
776 dstr->
Append (
" ", StartBoldCol,
"Score", EndBoldCol,
" ");
778 dstr->
Append (
" ", StartBoldCol,
" Score ", EndBoldCol,
" ");
780 if (PrintTiebreaks) {
781 dstr->
Append (StartBoldCol,
" (Tie) ", EndBoldCol);
784 for (player = 0; player < playerLimit; player++) {
785 strPad (stemp, PlayerData[SortedIndex[player]]->
name, MaxClashes,
' ');
788 strPad (stemp,
"", MaxClashes,
' ');
789 uint pnum = player + 1;
790 stemp[MaxClashes-1] = (pnum % 10) +
'0';
791 if (MaxClashes >= 2 && pnum >= 10) {
792 stemp[MaxClashes-2] = ((pnum / 10) % 10) +
'0';
794 if (MaxClashes >= 3 && pnum >= 100) {
795 stemp[MaxClashes-3] = ((pnum / 10) % 10) +
'0';
798 if (playerLimit == 2) {
802 "12345678901234567890123456789012345678901234567890",
806 dstr->
Append (
" \\tt ", stemp,
" &");
808 dstr->
Append (
" ", StartBoldCol, stemp, EndBoldCol);
812 dstr->
Append (
" ", StartBoldCol,
"Perf Chg Percnt", EndBoldCol);
814 dstr->
Append (
" ", StartBoldCol,
"Percnt", EndBoldCol);
816 dstr->
Append (
" ", StartBoldCol,
"+/-/=", EndBoldCol);
818 dstr->
Append (EndRow, NewLine);
820 PrintDashesLine (dstr);
823 uint previousScore = 0;
824 for (player = 0; player < playerLimit; player++) {
825 uint index = SortedIndex[player];
830 if (player > 0 && pdata->
score != previousScore) {
831 dstr->
Append (BlankRowLine);
834 previousScore = pdata->
score;
836 sprintf (stemp,
"%*u: ", PlayerNumWidth, player+1);
837 dstr->
Append (StartRow, StartRightCol, stemp, EndRightCol);
839 PrintPlayer (dstr, pdata);
842 sprintf (stemp,
" %2u%c%c ", pdata->
score / 2, DecimalPointChar,
843 (pdata->
score & 1 ?
'5' :
'0'));
844 dstr->
Append (StartRightCol, stemp, EndRightCol);
845 sprintf (stemp,
" %2u ", pdata->
gameCount);
846 dstr->
Append (StartRightCol, stemp, EndRightCol);
849 sprintf (stemp,
" %2u%c%c ",
850 pdata->
score / 2, DecimalPointChar,
851 (pdata->
score & 1 ?
'5' :
'0'));
853 sprintf (stemp,
" %2u%c%c / %2u ",
854 pdata->
score / 2, DecimalPointChar,
857 dstr->
Append (StartRightCol, stemp, EndRightCol);
859 if (PrintTiebreaks) {
860 sprintf (stemp,
"%3u%c%02u ", pdata->
tiebreak / 4,
861 DecimalPointChar, (pdata->
tiebreak % 4) * 25);
862 dstr->
Append (StartRightCol, stemp, EndRightCol);
865 uint r_won = 0, r_loss = 0, r_draw = 0;
866 for (
uint oppCount = 0; oppCount < playerLimit; oppCount++) {
867 if (playerLimit == 2 && oppCount == player) {
continue; }
868 uint opp = SortedIndex[oppCount];
870 dstr->
Append (StartRightCol);
873 for (
uint count = 0; count < MaxClashes; count++) {
876 if (CurrentGame == clash->
gameNum)
877 sprintf(stemp,
"<green><g_%u>%c</g></green>",
880 sprintf(stemp,
"<blue><g_%u>%c</g></blue>",
899 dstr->
AddChar (index == opp ?
'X' :
'.');
905 PrintPerformance (dstr, pdata);
906 PrintScorePercentage (dstr, pdata);
910 sprintf (stemp,
" (+%u -%u =%u)", r_won, r_loss, r_draw);
915 dstr->
Append (EndRow, NewLine);
917 PrintDashesLine (dstr);
922 Crosstable::PrintSwiss (
DString * dstr,
uint playerLimit)
940 dstr->
Append (StartTable);
942 dstr->
Append (
"\\begin{tabular}{rl");
943 if (PrintRatings) { dstr->
Append (
"r"); }
944 if (PrintTitles) { dstr->
Append (
"r"); }
945 if (PrintAges) { dstr->
Append (
"r"); }
946 if (PrintCountries) { dstr->
Append (
"l"); }
947 dstr->
Append (
"r@{ / }r");
948 if (PrintTiebreaks) { dstr->
Append (
"r"); }
949 for (
uint i=0; i < MaxRound; i++) {
951 if (i < MaxRound-1) { dstr->
Append (
"@{ }"); }
953 if (PrintRatings) { dstr->
Append (
"r"); }
958 dstr->
Append (
"<th></th> <th>Player</th> ");
960 dstr->
Append (
" & \\bf Player & ");
962 strPad (stemp,
"", LongestNameLen + 2 + PlayerNumWidth,
' ');
966 dstr->
Append (StartBoldCol,
" Rtng", EndBoldCol);
969 dstr->
Append (StartBoldCol,
" Ti", EndBoldCol);
972 dstr->
Append (StartBoldCol,
" Age", EndBoldCol);
974 if (PrintCountries) {
975 dstr->
Append (StartBoldCol,
" Nat", EndBoldCol);
978 dstr->
Append (
" \\multicolumn{2}{c}{\\bf Score} & ");
981 dstr->
Append (
" ", StartBoldCol,
"Score", EndBoldCol,
" ");
983 dstr->
Append (
" ", StartBoldCol,
" Score ", EndBoldCol,
" ");
985 if (PrintTiebreaks) {
986 dstr->
Append (StartBoldCol,
"(Tie)", EndBoldCol);
991 dstr->
Append (
" \\multicolumn{1}{c}{\\bf ",
round,
"} & ");
993 sprintf (stemp,
" %s%*d ", SwissColors ?
" " :
"",
994 PlayerNumWidth,
round);
995 dstr->
Append (StartBoldCol, stemp, EndBoldCol);
999 dstr->
Append (
" ", StartBoldCol,
"Perf Chg Percnt", EndBoldCol);
1001 dstr->
Append (
" ", StartBoldCol,
"Percnt", EndBoldCol);
1003 dstr->
Append (
" ", StartBoldCol,
"+/-/=", EndBoldCol);
1005 dstr->
Append (EndRow, NewLine);
1007 PrintDashesLine (dstr);
1010 uint previousScore = 0;
1011 for (player = 0; player < playerLimit; player++) {
1012 uint index = SortedIndex[player];
1017 if (player > 0 && pdata->
score != previousScore) {
1018 dstr->
Append (BlankRowLine);
1021 previousScore = pdata->
score;
1023 sprintf (stemp,
"%*u: ", PlayerNumWidth, player+1);
1024 dstr->
Append (StartRow, StartRightCol, stemp, EndRightCol);
1026 PrintPlayer (dstr, pdata);
1029 sprintf (stemp,
" %2u%c%c ", pdata->
score / 2, DecimalPointChar,
1030 (pdata->
score & 1 ?
'5' :
'0'));
1031 dstr->
Append (StartRightCol, stemp, EndRightCol);
1032 sprintf (stemp,
" %2u ", pdata->
gameCount);
1033 dstr->
Append (StartRightCol, stemp, EndRightCol);
1036 sprintf (stemp,
" %2u%c%c ",
1037 pdata->
score / 2, DecimalPointChar,
1038 (pdata->
score & 1 ?
'5' :
'0'));
1040 sprintf (stemp,
" %2u%c%c / %2u ",
1041 pdata->
score / 2, DecimalPointChar,
1044 dstr->
Append (StartRightCol, stemp, EndRightCol);
1046 if (PrintTiebreaks) {
1047 sprintf (stemp,
"%2u%c%c ", pdata->
tiebreak / 2,
1048 DecimalPointChar, (pdata->
tiebreak & 1 ?
'5' :
'0'));
1049 dstr->
Append (StartRightCol, stemp, EndRightCol);
1052 uint r_won = 0, r_loss = 0, r_draw = 0;
1056 dstr->
Append (StartRightCol);
1057 if (clash == NULL) {
1059 if (PlayerNumWidth == 3) { dstr->
AddChar (
'.'); }
1060 if (SwissColors) { dstr->
AddChar (
'.'); }
1063 if (CurrentGame == clash->
gameNum)
1064 sprintf (stemp,
"<green><g_%u>", clash->
gameNum);
1066 sprintf (stemp,
"<blue><g_%u>", clash->
gameNum);
1070 sprintf (stemp,
"%*d%c%s", PlayerNumWidth,
1071 InvertedIndex[clash->
opponent] + 1,
1073 resultStr[clash->
result]);
1075 sprintf (stemp,
"%*d%s", PlayerNumWidth,
1076 InvertedIndex[clash->
opponent] + 1,
1077 resultStr[clash->
result]);
1081 if (CurrentGame == clash->
gameNum)
1082 dstr->
Append (
"</g></green>");
1084 dstr->
Append (
"</g></blue>");
1097 PrintPerformance (dstr, pdata);
1098 PrintScorePercentage (dstr, pdata);
1102 sprintf (stemp,
" (+%u -%u =%u)", r_won, r_loss, r_draw);
1107 dstr->
Append (EndRow, NewLine);
1110 PrintDashesLine (dstr);
1116 Crosstable::PrintKnockout (
DString * dstr,
uint playerLimit)
1127 dstr->
Append (StartTable);
1129 dstr->
Append (
"\\begin{tabular}{l");
1130 if (PrintRatings) { dstr->
Append (
"r"); }
1131 if (PrintTitles) { dstr->
Append (
"r"); }
1132 if (PrintAges) { dstr->
Append (
"r"); }
1133 if (PrintCountries) { dstr->
Append (
"l"); }
1134 dstr->
Append (
"@{\\hspace{4ex}}lr@{ / }r@{\\hspace{4ex}}l");
1135 if (PrintRatings) { dstr->
Append (
"r"); }
1136 if (PrintTitles) { dstr->
Append (
"r"); }
1137 if (PrintAges) { dstr->
Append (
"r"); }
1138 if (PrintCountries) { dstr->
Append (
"l"); }
1141 PrintDashesLine (dstr);
1142 for (player = 0; player < playerLimit; player++) {
1143 PlayerData[player]->
printed =
false;
1145 for (player = 0; player < playerLimit; player++) {
1146 uint index = SortedIndex[player];
1149 if (clash == NULL) {
continue; }
1150 if (pdata->
printed) {
continue; }
1157 while (clash != 0) {
1162 clash = clash->
next;
1167 PrintPlayer (dstr, pdata);
1169 dstr->
Append (StartCol,
" ");
1172 for (
uint count = 0; count < MaxClashes; count++) {
1173 while (clash != NULL && clash->
round !=
round) {
1174 clash = clash->
next;
1176 if (clash != NULL) {
1178 if (CurrentGame == clash->
gameNum)
1179 sprintf (stemp,
"<green><g_%u>%c</g></green>",
1183 sprintf (stemp,
"<blue><g_%u>%c</g></blue>",
1190 clash = clash->
next;
1196 dstr->
Append (
" ", EndCol);
1199 sprintf (stemp,
" %2u%c%c ", score / 2, DecimalPointChar,
1200 (score & 1 ?
'5' :
'0'));
1201 dstr->
Append (StartRightCol, stemp, EndRightCol);
1202 sprintf (stemp,
" %2u ", nGames);
1203 dstr->
Append (StartRightCol, stemp, EndRightCol);
1205 sprintf (stemp,
"%2u%c%c / %2u ",
1206 score / 2, DecimalPointChar,
1207 (score & 1 ?
'5' :
'0'), nGames);
1208 dstr->
Append (StartRightCol, stemp, EndRightCol);
1210 PrintPlayer (dstr, PlayerData[opponent]);
1211 dstr->
Append (EndRow, NewLine);
1213 PlayerData[opponent]->
printed =
true;
1215 PrintDashesLine (dstr);
1216 dstr->
Append (EndTable, NewLine);
uint date_GetYear(dateT date)
const uint CROSSTABLE_MaxPlayers
clashT * lastClash[CROSSTABLE_MaxPlayers]
crosstableModeT BestMode(void)
uint strLength(const char *str)
const char * getTitle() const
bool strEqual(const char *str1, const char *str2)
const uint CROSSTABLE_MaxRounds
const uint NUM_RESULT_TYPES
char * strDuplicate(const char *original)
clashT * roundClash[CROSSTABLE_MaxRounds]
const resultT RESULT_Black
const resultT RESULT_OPPOSITE[4]
class SpellChecker - name spelling
const resultT RESULT_Draw
uint clashCount[CROSSTABLE_MaxPlayers]
uint strPad(char *target, const char *original, int width, char padding)
const resultT RESULT_White
static uint Performance(uint oppAvg, uint percentage)
static eloT OpponentElo(eloT player, eloT opponent)
const char RESULT_CHAR[4]
int comparePlayerData(playerDataT *p1, playerDataT *p2, crosstableSortT option)
const uint perf_elodiff[51]
static int RatingChange(eloT player, uint oppAvg, uint percentage, uint count)
const uint RESULT_SCORE[4]
clashT * firstClash[CROSSTABLE_MaxPlayers]
dateT getBirthdate() const
const PlayerInfo * getPlayerInfo(const char *name, std::vector< const char *> *bio=0) const
void strCopy(char *target, const char *original)
void PrintTable(DString *dstr, crosstableModeT mode, uint playerLimit, int currentGame)
const errorT ERROR_Corrupt
errorT AddPlayer(idNumberT id, const char *name, eloT elo, const SpellChecker *)
const char * strPlural(uint x)
const resultT RESULT_None
static uint FideCategory(eloT rating)
const errorT ERROR_NotFound
int strCompare(const char *s1, const char *s2)
const char * getLastCountry() const
uint date_GetMonth(dateT date)
class PlayerInfo - player informations
errorT AddResult(uint gameNumber, idNumberT white, idNumberT black, resultT result, uint round, dateT date)