2 Copyright (c) 1993 Richard V. Nash.
3 Copyright (c) 2000 Dan Papasian
4 Copyright (C) Andrew Tridgell 2002
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 static double Ratings_B_Average;
24 static double Ratings_B_StdDev;
26 static double Ratings_S_Average;
27 static double Ratings_S_StdDev;
29 static double Ratings_L_Average;
30 static double Ratings_L_StdDev;
32 static double Ratings_Bug_Average;
33 static double Ratings_Bug_StdDev;
35 static double Ratings_W_Average;
36 static double Ratings_W_StdDev;
39 static double Rb_M = 0.0, Rb_S = 0.0, Rb_total = 0.0;
40 static int Rb_count = 0;
42 static double Rs_M = 0.0, Rs_S = 0.0, Rs_total = 0.0;
43 static int Rs_count = 0;
45 static double Rl_M = 0.0, Rl_S = 0.0, Rl_total = 0.0;
46 static int Rl_count = 0;
48 static double Rbug_M = 0.0, Rbug_S = 0.0, Rbug_total = 0.0;
49 static int Rbug_count = 0;
51 static double Rw_M = 0.0, Rw_S = 0.0, Rw_total = 0.0;
52 static int Rw_count = 0;
55 rateStruct bestS[MAX_BEST];
57 rateStruct bestB[MAX_BEST];
59 rateStruct bestW[MAX_BEST];
64 #define LOWESTHIST 800
65 static int sHist[MAXHIST];
66 static int bHist[MAXHIST];
67 static int wHist[MAXHIST];
68 static int lHist[MAXHIST];
69 static int bugHist[MAXHIST];
71 static int DisplayRank(int p, param_list param, int showComputers);
72 static int DisplayTargetRank(int p, char *target, int show, int showComp);
73 static int ShowFromString(char *s);
74 static int DisplayRankedPlayers(int p, int start, int end,
75 int show, int showComp);
76 static int Best(int p, param_list param, int ShowComp);
78 static int is_active(int Games)
80 return (Games >= PROVISIONAL);
84 static void rating_add(int rating, int type)
88 which = (rating - LOWESTHIST) / 100;
93 if (type == TYPE_BLITZ) {
100 Rb_S = Rb_S + (rating - Rb_M) * (rating - Rb_M);
101 Rb_M = Rb_M + (rating - Rb_M) / (Rb_count);
103 Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
104 Ratings_B_Average = Rb_total / (double) Rb_count;
105 } else if (type == TYPE_WILD) { /* TYPE_WILD */
112 Rw_S = Rw_S + (rating - Rw_M) * (rating - Rw_M);
113 Rw_M = Rw_M + (rating - Rw_M) / (Rw_count);
115 Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
116 Ratings_W_Average = Rw_total / (double) Rw_count;
117 } else if (type == TYPE_LIGHT) { /* TYPE_LIGHT */
124 Rl_S = Rl_S + (rating - Rl_M) * (rating - Rl_M);
125 Rl_M = Rl_M + (rating - Rl_M) / (Rl_count);
127 Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
128 Ratings_L_Average = Rl_total / (double) Rl_count;
129 } else if (type == TYPE_BUGHOUSE) { /* TYPE_BUGHOUSE */
132 Rbug_total += rating;
133 if (Rbug_count == 1) {
136 Rbug_S = Rbug_S + (rating - Rbug_M) * (rating - Rbug_M);
137 Rbug_M = Rbug_M + (rating - Rbug_M) / (Rbug_count);
139 Ratings_Bug_StdDev = sqrt(Rbug_S / Rbug_count);
140 Ratings_Bug_Average = Rbug_total / (double) Rbug_count;
141 } else { /* TYPE_STAND */
148 Rs_S = Rs_S + (rating - Rs_M) * (rating - Rs_M);
149 Rs_M = Rs_M + (rating - Rs_M) / (Rs_count);
151 Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
152 Ratings_S_Average = Rs_total / (double) Rs_count;
156 static void rating_remove(int rating, int type)
160 which = (rating - LOWESTHIST) / 100;
163 if (which >= MAXHIST)
165 if (type == TYPE_BLITZ) {
166 bHist[which] = bHist[which] - 1;
167 if (bHist[which] < 0)
177 Rb_M = Rb_M - (rating - Rb_M) / (Rb_count);
178 Rb_S = Rb_S - (rating - Rb_M) * (rating - Rb_M);
179 /* added this 3.11.95 foxbat */ if (Rb_S < 0)
183 Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
184 Ratings_B_Average = Rb_total / (double) Rb_count;
186 Ratings_B_StdDev = 0;
187 Ratings_B_Average = 0;
189 } else if (type == TYPE_WILD) { /* TYPE_WILD */
190 wHist[which] = wHist[which] - 1;
191 if (wHist[which] < 0)
201 Rw_M = Rw_M - (rating - Rw_M) / (Rw_count);
202 Rw_S = Rw_S - (rating - Rw_M) * (rating - Rw_M);
203 /* added this 3.10.95 foxbat */ if (Rw_S < 0)
207 Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
208 Ratings_W_Average = Rw_total / (double) Rw_count;
210 Ratings_W_StdDev = 0;
211 Ratings_W_Average = 0;
213 } else if (type == TYPE_LIGHT) { /* TYPE_LIGHT */
214 lHist[which] = lHist[which] - 1;
215 if (lHist[which] < 0)
225 Rl_M = Rl_M - (rating - Rl_M) / (Rl_count);
226 Rl_S = Rl_S - (rating - Rl_M) * (rating - Rl_M);
231 Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
232 Ratings_L_Average = Rl_total / (double) Rl_count;
234 Ratings_L_StdDev = 0;
235 Ratings_L_Average = 0;
237 } else if (type == TYPE_BUGHOUSE) { /* TYPE_BUGHOUSE */
238 bugHist[which] = bugHist[which] - 1;
239 if (bugHist[which] < 0)
244 Rbug_total -= rating;
245 if (Rbug_count == 0) {
249 Rbug_M = Rbug_M - (rating - Rbug_M) / (Rbug_count);
250 Rbug_S = Rbug_S - (rating - Rbug_M) * (rating - Rbug_M);
255 Ratings_Bug_StdDev = sqrt(Rbug_S / Rbug_count);
256 Ratings_Bug_Average = Rbug_total / (double) Rbug_count;
258 Ratings_Bug_StdDev = 0;
259 Ratings_Bug_Average = 0;
261 } else { /* TYPE_STAND */
262 sHist[which] = sHist[which] - 1;
263 if (sHist[which] < 0)
273 Rs_M = Rs_M - (rating - Rs_M) / (Rs_count);
274 Rs_S = Rs_S - (rating - Rs_M) * (rating - Rs_M);
275 /* added this 3.10.95 foxbat */ if (Rs_S < 0)
279 Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
280 Ratings_S_Average = Rs_total / (double) Rs_count;
282 Ratings_S_StdDev = 0;
283 Ratings_S_Average = 0;
288 static void load_ratings(void)
293 fp = fopen_p("%s/newratingsV%d_data", "r", STATS_DIR, STATS_VERSION);
295 d_printf( "CHESSD: Can't read ratings data!\n");
299 fscanf(fp, "%lf %lf %lf %d", &Rb_M, &Rb_S, &Rb_total, &Rb_count);
300 fscanf(fp, "%lf %lf %lf %d", &Rs_M, &Rs_S, &Rs_total, &Rs_count);
301 fscanf(fp, "%lf %lf %lf %d", &Rw_M, &Rw_S, &Rw_total, &Rw_count);
302 fscanf(fp, "%lf %lf %lf %d", &Rl_M, &Rl_S, &Rl_total, &Rl_count);
304 for (i = 0; i < MAXHIST; i++) {
305 fscanf(fp, "%d %d %d %d", &sHist[i], &bHist[i], &wHist[i], &lHist[i]);
309 Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
310 Ratings_S_Average = Rs_total / (double) Rs_count;
312 Ratings_S_StdDev = 0;
313 Ratings_S_Average = 0;
316 Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
317 Ratings_B_Average = Rb_total / (double) Rb_count;
319 Ratings_B_StdDev = 0;
320 Ratings_B_Average = 0;
323 Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
324 Ratings_W_Average = Rw_total / (double) Rw_count;
326 Ratings_W_StdDev = 0;
327 Ratings_W_Average = 0;
330 Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
331 Ratings_L_Average = Rl_total / (double) Rl_count;
333 Ratings_L_StdDev = 0;
334 Ratings_L_Average = 0;
338 static void save_ratings(void)
343 fp = fopen_p("%s/newratingsV%d_data", "w", STATS_DIR,STATS_VERSION);
345 d_printf( "CHESSD: Can't write ratings data!\n");
349 fprintf(fp, "%10f %10f %10f %d\n", Rb_M, Rb_S, Rb_total, Rb_count);
350 fprintf(fp, "%10f %10f %10f %d\n", Rs_M, Rs_S, Rs_total, Rs_count);
351 fprintf(fp, "%10f %10f %10f %d\n", Rw_M, Rw_S, Rw_total, Rw_count);
352 fprintf(fp, "%10f %10f %10f %d\n", Rl_M, Rl_S, Rl_total, Rl_count);
354 for (i = 0; i < MAXHIST; i++) {
355 fprintf(fp, "%d %d %d %d\n", sHist[i], bHist[i], wHist[i], lHist[i]);
360 static void zero_stats(void)
363 for (i = 0; i < MAXHIST; i++) {
369 Rb_M = 0.0, Rb_S = 0.0, Rb_total = 0.0;
372 Rs_M = 0.0, Rs_S = 0.0, Rs_total = 0.0;
375 Rw_M = 0.0, Rw_S = 0.0, Rw_total = 0.0;
378 Rl_M = 0.0, Rl_S = 0.0, Rl_total = 0.0;
382 void ratings_init(void)
388 static int Round (double x)
390 return (x < 0 ? (int) (x - 0.5) : (int) (x + 0.5));
393 /* Constants for Glicko system */
395 #define Gv 0.0005828166666667 /* (0.187)^2 / 60 */
398 #define Gq 0.00575646273249 /* ln(10.0)/400.0 */
399 #define Gp 0.000010072398601964 /* (3 * Gq / PI)^2 */
400 #define GBq 0.00287823136624 /* ln(10.0)/800.0 */
401 #define GBp (9 * GBq * GBq / (3.141592653589 * 3.141592653589))
402 /* End of Glicko system variables */
404 static double Gf(double ss)
406 return (1.0 / sqrt(1.0 + Gp * ss * ss));
409 /* Confusing but economical: calculate error and attenuation function together */
410 static double GE(int r, int rr, double ss, double *fss)
413 return (1.0 / (1.0 + pow(10.0, (rr - r) * (*fss) / 400.0)));
416 static double GBf(double sp, double ss, double ssp)
418 return (1.0 / sqrt(1.0 + GBp * (ss*ss + sp*sp + ssp*ssp)));
421 /* Confusing but economical: calculate error and attenuation function together */
422 static double GBE(int r, int rp, int rr, int rrp,
423 double sp, double ss, double ssp, double *fss)
425 *fss = GBf(sp, ss, ssp);
426 return (1.0 / (1.0 + pow(10.0, (rr + rrp - r - rp) * (*fss) / 800.0)));
429 double current_sterr(double s, long t)
434 t = 0; /* this shouldn't happen */
435 new = sqrt(s * s + Gd * Gd * log(1.0 + t / 60.0)); /* log formula */
436 /* new = sqrt(s * s + Gv * t); */ /* linear formula */
442 static void UpdateStats(struct statistics *p_stats, int *r, double *s, int now)
444 /* Calculate effective pre-game sterrs. ltime==0 implies never had sterr. */
446 *s = current_sterr(p_stats->sterr, now - p_stats->ltime);
448 /* pre-game ratings */
449 *r = (p_stats->rating == 0 && p_stats->num == 0) ? Gr0
454 static void GetCurrentStats (int p, int *r, double *s, int *new,
455 int *rPart, double *sPart, int type, int now)
457 struct player *pp = &player_globals.parray[p];
458 struct statistics *p_stats = NULL;
459 struct statistics *part_stats; /* for bughouse. */
463 p_stats = &pp->b_stats;
466 p_stats = &pp->s_stats;
469 p_stats = &pp->w_stats;
472 p_stats = &pp->l_stats;
475 p_stats = &pp->bug_stats;
476 part_stats = &player_globals.parray[pp->partner].bug_stats;
477 UpdateStats(part_stats, rPart, sPart, now);
480 /* Calculate effective pre-game sterrs. ltime==0 implies never had sterr. */
481 UpdateStats(p_stats, r, s, now);
482 if (p_stats->rating == 0 && p_stats->num == 0)
487 /* Calculates new rating and standard error. By vek. The person */
488 /* who invented the ratings system is Mark E. Glickman, Ph.D. */
489 /* His e-mail address is glickman@hustat.harvard.edu as of April '95. */
490 /* Postscript copy of the note I coded this from should be available */
491 /* for ftp from ics.onenet.net, if not elsewhere. */
493 void rating_sterr_delta(int p1, int p2, int type, int gtime, int result,
494 int *deltarating, double *newsterr)
497 double s1, s2, s1part, s2part;
498 int r1, r2, r1part, r2part, new; /* Initial sterrs and ratings */
499 double q, E, fs2, denominator, GK, w; /* Parts of fancy formulas */
500 double delta; /* Result to return */
502 GetCurrentStats (p2, &r2, &s2, &new, &r2part, &s2part, type, gtime);
503 GetCurrentStats (p1, &r1, &s1, &new, &r1part, &s1part, type, gtime);
506 if (result == RESULT_WIN) {
508 } else if (result == RESULT_DRAW) {
513 if (type != TYPE_BUGHOUSE) {
515 E = GE(r1, r2, s2, &fs2); /* side effect: calculate fs2 */
518 E = GBE(r1, r1part, r2, r2part, s1part, s2, s2part, &fs2);
520 denominator = 1.0 / (s1 * s1) + q * q * fs2 * fs2 * E * (1.0 - E);
521 GK = q * fs2 / denominator;
523 delta = GK * (w - E);
525 *deltarating = Round(Gr0 + delta);
527 *deltarating = Round(delta); /* Returned values: deltarating,
529 *newsterr = 1.0 / sqrt(denominator);
532 int rating_update(int g, int link_game)
535 double wSigma, bSigma; /* vek */
538 struct statistics *w_stats;
539 struct statistics *b_stats;
543 int inprogress = (g == player_globals.parray[game_globals.garray[g].black].game);
544 /* if this is adjudication of stored game, be quiet about ratings change */
546 if (game_globals.garray[g].type == TYPE_BLITZ) {
547 w_stats = &player_globals.parray[game_globals.garray[g].white].b_stats;
548 b_stats = &player_globals.parray[game_globals.garray[g].black].b_stats;
549 } else if (game_globals.garray[g].type == TYPE_STAND) {
550 w_stats = &player_globals.parray[game_globals.garray[g].white].s_stats;
551 b_stats = &player_globals.parray[game_globals.garray[g].black].s_stats;
552 } else if (game_globals.garray[g].type == TYPE_WILD) {
553 w_stats = &player_globals.parray[game_globals.garray[g].white].w_stats;
554 b_stats = &player_globals.parray[game_globals.garray[g].black].w_stats;
555 } else if (game_globals.garray[g].type == TYPE_LIGHT) {
556 w_stats = &player_globals.parray[game_globals.garray[g].white].l_stats;
557 b_stats = &player_globals.parray[game_globals.garray[g].black].l_stats;
558 } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
559 w_stats = &player_globals.parray[game_globals.garray[g].white].bug_stats;
560 b_stats = &player_globals.parray[game_globals.garray[g].black].bug_stats;
562 d_printf( "CHESSD: Can't update untimed ratings!\n");
566 switch (game_globals.garray[g].result) {
571 if (game_globals.garray[g].winner == WHITE) {
586 case END_FLAGNOMATERIAL:
587 wRes = bRes = RESULT_DRAW;
590 d_printf( "CHESSD: Update undecided game %d?\n", game_globals.garray[g].result);
593 /* Use end time, not start time; important for adjourned games. */
594 /* gtime = untenths(game_globals.garray[g].timeOfStart); */
596 rating_sterr_delta(game_globals.garray[g].white, game_globals.garray[g].black,
597 game_globals.garray[g].type, gtime, wRes,
600 rating_sterr_delta(game_globals.garray[g].black, game_globals.garray[g].white,
601 game_globals.garray[g].type, gtime, bRes,
604 /* hersco, 1/23/96: We now know rating changes for these players;
605 update partners' game. */
606 if (link_game >= 0) {
607 game_globals.garray[link_game].winner = CToggle(game_globals.garray[g].winner);
608 game_globals.garray[link_game].result = game_globals.garray[g].result;
609 rating_update (link_game, -1); /* but don't recurse any deeper. */
611 /* vek: Update time of last rated game played, for future ratings calcs. */
612 /* Kept independently for blitz and standard. */
613 w_stats->ltime = gtime;
614 b_stats->ltime = gtime;
615 /* end vek add 4/5/95 */
617 if (wRes == RESULT_WIN) {
619 } else if (wRes == RESULT_LOSS) {
625 if (bRes == RESULT_WIN) {
627 } else if (bRes == RESULT_LOSS) {
633 rating_remove(w_stats->rating, game_globals.garray[g].type);
634 rating_remove(b_stats->rating, game_globals.garray[g].type);
637 pprintf(game_globals.garray[g].white, "%s rating adjustment: %d ",
638 TypeStrings[game_globals.garray[g].type], w_stats->rating);
639 pprintf(game_globals.garray[g].black, "%s rating adjustment: %d ",
640 TypeStrings[game_globals.garray[g].type], b_stats->rating);
642 if (wDelta < -1000) {
643 pprintf(game_globals.garray[g].white, "not changed due to bug (way too small)! sorry!\n");
644 d_printf( "CHESSD: Got too small ratings bug for %s (w) vs. %s\n",
645 player_globals.parray[game_globals.garray[g].white].login, player_globals.parray[game_globals.garray[g].black].login);
646 } else if (wDelta > 3000) {
647 pprintf(game_globals.garray[g].white, "not changed due to bug (way too big)! sorry!\n");
648 d_printf( "CHESSD: Got too big ratings bug for %s (w) vs. %s\n",
649 player_globals.parray[game_globals.garray[g].white].login, player_globals.parray[game_globals.garray[g].black].login);
651 w_stats->rating += wDelta;
652 w_stats->sterr = wSigma;
655 if (bDelta < -1000) {
656 pprintf(game_globals.garray[g].black, "not changed due to bug (way too small)! sorry! ");
657 d_printf( "CHESSD: Got too small ratings bug for %s (b) vs. %s\n",
658 player_globals.parray[game_globals.garray[g].black].login, player_globals.parray[game_globals.garray[g].white].login);
659 } else if (bDelta > 3000) {
660 pprintf(game_globals.garray[g].black, "not changed due to bug (way too big)! sorry! ");
661 d_printf( "CHESSD: Got too big ratings bug for %s (b) vs. %s\n",
662 player_globals.parray[game_globals.garray[g].black].login, player_globals.parray[game_globals.garray[g].white].login);
664 b_stats->rating += bDelta;
665 b_stats->sterr = bSigma;
666 } /* error messages down to vek */
668 rating_add(w_stats->rating, game_globals.garray[g].type);
669 rating_add(b_stats->rating, game_globals.garray[g].type);
671 if ((w_stats->rating > w_stats->best) && (is_active(w_stats->num))) {
672 w_stats->best = w_stats->rating;
673 w_stats->whenbest = time(NULL);
675 if ((b_stats->rating > b_stats->best) && (is_active(b_stats->num))) {
676 b_stats->best = b_stats->rating;
677 b_stats->whenbest = time(NULL);
680 /* ratings are saved later - DAV */
683 if (game_globals.garray[g].type == TYPE_BLITZ) {
685 Rb_total += (w_stats->rating + b_stats->rating) / 2.0;
686 } else if (game_globals.garray[g].type == TYPE_STAND) {
688 Rs_total += (w_stats->rating + b_stats->rating) / 2.0;
689 } else if (game_globals.garray[g].type == TYPE_LIGHT) {
691 Rl_total += (w_stats->rating + b_stats->rating) / 2.0;
692 } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
694 Rbug_total += (w_stats->rating + b_stats->rating) / 2.0;
695 } else if (game_globals.garray[g].type == TYPE_WILD) {
697 Rw_total += (w_stats->rating + b_stats->rating) / 2.0;
701 pprintf(game_globals.garray[g].white, "--> %d\n", w_stats->rating);
702 pprintf(game_globals.garray[g].black, "--> %d\n", b_stats->rating);
705 UpdateRank(game_globals.garray[g].type, player_globals.parray[game_globals.garray[g].white].name,
706 w_stats, player_globals.parray[game_globals.garray[g].white].name);
707 UpdateRank(game_globals.garray[g].type, player_globals.parray[game_globals.garray[g].black].name,
708 b_stats, player_globals.parray[game_globals.garray[g].black].name);
712 static void ShowAssess (int p, int p1, int p2, int type1, int type2)
715 int win1, win2, win3, win4;
716 int draw1, draw2, draw3, draw4;
717 int loss1, loss2, loss3, loss4;
718 double newsterr1, newsterr2, newsterr3, newsterr4;
719 int nowtime = time(0);
720 char line[100]; /* should never need any more than 80. */
722 if (type1 == TYPE_BUGHOUSE) {
723 p3 = player_globals.parray[p1].partner;
724 p4 = player_globals.parray[p2].partner;
726 if (!CheckPFlag(p3, PFLAG_REG) || !CheckPFlag(p4, PFLAG_REG)) {
727 pprintf (p, "Bughouse with unregistered partners cannot be rated.\n");
734 rating_sterr_delta(p1, p2, type1, nowtime, RESULT_WIN, &win1, &newsterr1);
735 rating_sterr_delta(p1, p2, type1, nowtime, RESULT_DRAW, &draw1, &newsterr1);
736 rating_sterr_delta(p1, p2, type1, nowtime, RESULT_LOSS, &loss1, &newsterr1);
737 rating_sterr_delta(p2, p1, type1, nowtime, RESULT_WIN, &win2, &newsterr2);
738 rating_sterr_delta(p2, p1, type1, nowtime, RESULT_DRAW, &draw2, &newsterr2);
739 rating_sterr_delta(p2, p1, type1, nowtime, RESULT_LOSS, &loss2, &newsterr2);
742 rating_sterr_delta(p3,p4, type2, nowtime,RESULT_WIN, &win3, &newsterr3);
743 rating_sterr_delta(p3,p4, type2, nowtime,RESULT_DRAW, &draw3, &newsterr3);
744 rating_sterr_delta(p3,p4, type2, nowtime,RESULT_LOSS, &loss3, &newsterr3);
745 rating_sterr_delta(p4,p3, type2, nowtime,RESULT_WIN, &win4, &newsterr4);
746 rating_sterr_delta(p4,p3, type2, nowtime,RESULT_DRAW, &draw4, &newsterr4);
747 rating_sterr_delta(p4,p3, type2, nowtime,RESULT_LOSS, &loss4, &newsterr4);
749 sprintf (line, "%7s", "");
750 if (type1 != TYPE_BUGHOUSE) {
751 CenterText (&line[7], TypeStrings[type1], 35, 1);
754 CenterText (&line[44], TypeStrings[type2], 35, 0);
758 CenterText (&line[7], "Bughouse", 72, 0);
760 pprintf (p, "\n%s\n", line);
762 sprintf (line, "%7s", "");
763 CenterText (&line[7], player_globals.parray[p1].name, 17, 1);
765 CenterText (&line[25], player_globals.parray[p2].name, 17, 1);
768 CenterText (&line[44], player_globals.parray[p3].name, 17, 1);
770 CenterText (&line[62], player_globals.parray[p4].name, 17, 0);
772 pprintf (p, "%s\n", line);
773 pprintf (p, "%7s (%4s RD %5.1f) ",
774 "", ratstrii(GetRating(&player_globals.parray[p1], type1), p1),
775 GetRD(&player_globals.parray[p1], type1));
776 pprintf (p, " (%4s RD %5.1f)",
777 ratstrii(GetRating(&player_globals.parray[p2], type1), p2),
778 GetRD(&player_globals.parray[p2], type1));
780 pprintf (p, " (%4s RD %5.1f)",
781 ratstrii(GetRating(&player_globals.parray[p3], type2), p3),
782 GetRD(&player_globals.parray[p3], type2));
783 pprintf (p, " (%4s RD %5.1f)",
784 ratstrii(GetRating(&player_globals.parray[p4], type2), p4),
785 GetRD(&player_globals.parray[p4], type2));
787 pprintf (p, "\n%7s%9d %17d", "Win:", win1, loss2);
789 pprintf (p, " %17d %17d", win3, loss4);
790 pprintf (p, "\n%7s%9d %17d", "Draw:", draw1, draw2);
792 pprintf (p, " %17d %17d", draw3, draw4);
793 pprintf (p, "\n%7s%9d %17d", "Loss:", loss1, win2);
795 pprintf (p, " %17d %17d", loss3, win4);
796 pprintf (p, "\n%7s%10.1f %17.1f", "New RD:", newsterr1, newsterr2);
798 pprintf (p, " %17.1f %17.1f", newsterr3, newsterr4);
802 int com_assess(int p, param_list param)
804 struct player *pp = &player_globals.parray[p];
805 int p1 = p, p2, g, gametype = -1;
806 int p1_connected = 1, p2_connected = 1;
808 /* Hawk: Now assess can be used with players not */
809 /* logged on -- I wonder if anyone doesn't */
810 /* get just a bit confused here :) */
812 if (param[0].type == TYPE_NULL) {
815 pprintf(p, "You are not playing or examining a game.\n");
817 } else if ((game_globals.garray[g].status == GAME_EXAMINE) || (game_globals.garray[g].status == GAME_SETUP)) {
818 gametype = game_globals.garray[g].type;
819 if (!strcmp(game_globals.garray[g].black_name, pp->name)) {
820 if (game_globals.garray[g].rated) {
821 pcommand(p, "assess %s %s %s\n", game_globals.garray[g].black_name,
822 game_globals.garray[g].white_name, TypeStrings[gametype]);
823 return COM_OK_NOPROMPT;
825 pprintf(p, "You are not involved in a rated game.\n");
828 if (game_globals.garray[g].rated) {
829 pcommand(p, "assess %s %s %s\n", game_globals.garray[g].white_name,
830 game_globals.garray[g].black_name, TypeStrings[gametype]);
831 return COM_OK_NOPROMPT;
833 pprintf(p, "You are not involved in a rated game.\n");
839 gametype = game_globals.garray[g].type;
841 } else if (param[0].type == TYPE_INT) {
842 if (param[1].type != TYPE_NULL) {
843 pprintf(p, "Assess <game number> cannot use other parameters.\n");
846 g = param[0].val.integer - 1;
847 if (g < 0 || g >= game_globals.g_num) {
848 pprintf (p, "There is no game %d.\n", g+1);
851 gametype = game_globals.garray[g].type;
852 pcommand(p, "assess %s %s %s\n", game_globals.garray[g].white_name,
853 game_globals.garray[g].black_name, TypeStrings[gametype]);
854 return COM_OK_NOPROMPT;
856 if (!FindPlayer(p, param[0].val.word, &p2, &p2_connected)) {
857 pprintf(p, "No user named \"%s\" was found.\n", param[0].val.word);
860 if (param[1].type != TYPE_NULL) {
862 p1_connected = p2_connected;
863 if (!FindPlayer(p, param[1].val.word, &p2, &p2_connected)) {
864 pprintf(p, "No user named \"%s\" was found.\n", param[1].val.word);
869 if (param[2].type == TYPE_WORD) {
870 param[2].val.word[0] = toupper(param[2].val.word[0]);
871 for (gametype = 0; gametype < NUM_GAMETYPES; gametype++)
872 if (!strcmp(TypeStrings[gametype], param[2].val.word))
877 if (gametype >= NUM_GAMETYPES) {
878 pprintf(p, "There is no game type %s.\n", param[2].val.word);
879 } else if (p1 == p2) {
880 pprintf(p, "You can't assess the same players.\n");
881 } else if (!CheckPFlag(p1, PFLAG_REG) || !CheckPFlag(p2, PFLAG_REG)) {
882 pprintf(p, "You can only assess games between registered players.\n");
883 } else if (gametype >= 0) {
884 if (gametype == TYPE_BUGHOUSE
885 && (player_globals.parray[p1].partner < 0 || player_globals.parray[p2].partner < 0)) {
886 pprintf(p, "You can only assess bughouse games if both players have partners.\n");
887 } else if (gametype == TYPE_UNTIMED || gametype == TYPE_NONSTANDARD) {
888 pprintf (p, "%s games are never rated.\n", TypeStrings[gametype]);
890 ShowAssess (p, p1, p2, gametype, -1);
893 ShowAssess (p, p1, p2, TYPE_BLITZ, TYPE_STAND);
894 ShowAssess (p, p1, p2, TYPE_LIGHT, TYPE_WILD);
895 if (player_globals.parray[p1].partner >= 0 && player_globals.parray[p2].partner >= 0
896 && player_globals.parray[p1].partner != p2)
897 ShowAssess (p, p1, p2, TYPE_BUGHOUSE, TYPE_BUGHOUSE);
907 int com_best(int p, param_list param)
909 return Best(p, param, 1);
912 int com_hbest(int p, param_list param)
914 return Best(p, param, 0);
918 int com_best(int p, param_list param)
922 pprintf(p, "Standard Blitz Wild\n");
923 for (i = 0; i < MAX_BEST; i++) {
924 if ((i >= numS) && (i >= numB))
927 pprintf(p, "%4d %-17s ", bestS[i].rating, bestS[i].name);
932 pprintf(p, "%4d %-17s ", bestB[i].rating, bestB[i].name);
937 pprintf(p, "%4d %-17s\n", bestW[i].rating, bestW[i].name);
946 int com_statistics(int p, param_list param)
948 pprintf(p, " Standard Blitz Lightning Wild\n");
949 pprintf(p, "average: %7.2f %7.2f %7.2f %7.2f\n", Ratings_S_Average, Ratings_B_Average, Ratings_L_Average, Ratings_W_Average);
950 pprintf(p, "std dev: %7.2f %7.2f %7.2f %7.2f\n", Ratings_S_StdDev, Ratings_B_StdDev, Ratings_L_StdDev, Ratings_W_StdDev);
951 pprintf(p, "number : %7d %7d %7d %7d\n", Rs_count, Rb_count, Rl_count, Rw_count);
955 int com_fixrank(int p, param_list param)
959 if (!FindPlayer(p, param[0].val.word, &p1, &connected))
961 UpdateRank(TYPE_BLITZ, player_globals.parray[p1].name, &player_globals.parray[p1].b_stats,
962 player_globals.parray[p1].name);
963 UpdateRank(TYPE_STAND, player_globals.parray[p1].name, &player_globals.parray[p1].s_stats,
964 player_globals.parray[p1].name);
965 UpdateRank(TYPE_WILD, player_globals.parray[p1].name, &player_globals.parray[p1].w_stats,
966 player_globals.parray[p1].name);
972 int com_rank(int p, param_list param)
974 return DisplayRank(p, param, 1);
977 int com_hrank(int p, param_list param)
979 return DisplayRank(p, param, 0);
982 static int DisplayRank(int p, param_list param, int showComputers)
984 struct player *pp = &player_globals.parray[p];
985 int start, end, target, connected;
986 int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
988 if (param[0].type == TYPE_NULL) {
989 DisplayTargetRank(p, pp->name, show, showComputers);
991 } else if (isdigit(param[0].val.word[0])) {
993 sscanf(param[0].val.word, "%d-%d", &start, &end);
994 if (end > 0 && (param[1].type != TYPE_NULL))
995 show = ShowFromString(param[1].val.word);
996 DisplayRankedPlayers(p, start, end, show, showComputers);
999 target = player_search(p, param[0].val.word);
1001 pprintf(p, "Target %s not found.\n", param[0].val.word);
1004 connected = (target > 0);
1006 target = -target - 1;
1010 if (param[1].type != TYPE_NULL)
1011 show = ShowFromString(param[1].val.word);
1012 DisplayTargetRank(p, player_globals.parray[target].name, show, showComputers);
1014 player_remove(target);
1019 /* CompareStats returns 1 if s1 comes first, -1 if s2 comes first, and 0
1020 if neither takes precedence. */
1022 static int CompareStats(char *name1, statistics *s1,
1023 char *name2, statistics *s2)
1032 else if (s2 == NULL)
1035 if (s1->rating > s2->rating)
1037 if (s1->rating < s2->rating)
1040 for (i = 0; i < l1; i++) {
1041 if (name2[i] == '\0')
1043 if (tolower(name1[i]) < tolower(name2[i]))
1045 if (tolower(name1[i]) > tolower(name2[i]))
1048 if (name2[i] != '\0')
1050 /* if (s1->sterr < s2->sterr) return 1;
1051 if (s1->sterr > s2->sterr) return -1;
1052 if (s1->num > s2->num) return 1;
1053 if (s1->num < s2->num) return -1;
1055 d_printf( "Duplicate entries found: %s.\n", name1);
1059 static int GetRankFileName(char *out, int type)
1063 sprintf(out, "%s/rank.blitz", STATS_DIR);
1066 sprintf(out, "%s/rank.std", STATS_DIR);
1069 sprintf(out, "%s/rank.wild", STATS_DIR);
1076 /* loon: Turning this off 28 Oct 1995 (temporary:)) since we're lagged
1078 void UpdateRank(int type, char *addName,
1079 struct statistics *sNew, char *delName)
1083 void UpdateRank(int type, char *addName,
1084 struct statistics *sNew, char *delName)
1086 char RankFile[MAX_FILENAME_SIZE];
1087 char TmpRankFile[MAX_FILENAME_SIZE];
1088 char line[MAX_RANK_LINE];
1089 char login[MAX_LOGIN_NAME];
1096 if (GetRankFileName(RankFile, type) < 0)
1098 fp = fopen_s(RankFile, "r");
1100 d_printf( "Can't open rank file to update.\n");
1103 sprintf(TmpRankFile, "%s/tmpRank", sdir);
1104 fptemp = fopen_s(TmpRankFile, "w");
1105 if (fptemp == NULL) {
1106 d_printf("Unable to open rank file for updating.\n");
1110 while (fgets(line, MAX_RANK_LINE - 1, fp)) {
1111 sscanf(line, "%s %d %d %d", login, &sCur.rating,
1113 if (delName != NULL && !strcasecmp(delName, login)) { /* Kill name. */
1117 if (addName != NULL && CompareStats(addName, sNew, login, &sCur) > 0) {
1118 int computer = in_list(-1, L_COMPUTER, addName);
1119 fprintf(fptemp, "%s %d %d %d\n", addName, sNew->rating,
1120 sNew->num, computer);
1123 fprintf(fptemp, "%s %d %d %d\n", login, sCur.rating, sCur.num, comp);
1128 rename(TmpRankFile, RankFile);
1132 static void DisplayRankHead(int p, int show)
1134 char Line[MAX_STRING_LENGTH];
1137 if (CheckFlag(show, SHOW_BLITZ))
1138 strcat(Line, " Blitz ");
1139 if (CheckFlag(show, SHOW_STANDARD))
1140 strcat(Line, " Standard ");
1141 if (CheckFlag(show, SHOW_WILD))
1142 strcat(Line, " Wild");
1143 pprintf(p, "%s\n\n", Line);
1146 static int CountRankLine(int countComp, char *loginName,
1147 int num, int is_computer)
1149 if (loginName == NULL || loginName[0] == '\0')
1151 return (countComp || !is_computer) && (is_active(num));
1154 static int GetRank(FILE * fp, char *target, int countComp)
1157 int nGames, is_computer;
1158 int playerFound = 0;
1159 char line[MAX_RANK_LINE];
1160 char login[MAX_LOGIN_NAME];
1162 while (fgets(line, MAX_RANK_LINE - 1, fp) && !playerFound) {
1163 sscanf(line, "%s %*d %d %d", login, &nGames, &is_computer);
1164 if ((playerFound = !strcasecmp(login, target))
1165 || CountRankLine(countComp, login, nGames, is_computer))
1168 return (playerFound ? count : -1);
1171 static void PositionFilePtr(FILE * fp, int count, int *last,
1172 int *nTied, int showComp)
1174 int i, rating, nGames, is_computer;
1175 char login[MAX_LOGIN_NAME];
1176 char line[MAX_RANK_LINE];
1181 for (i = 1; i < count; i++) {
1183 fgets(line, MAX_RANK_LINE - 1, fp);
1186 sscanf(line, "%s %d %d %d", login, &rating, &nGames, &is_computer);
1187 } while (!CountRankLine(showComp, login, nGames, is_computer));
1188 if (rating != *last) {
1196 static int ShowRankEntry(int p, FILE * fp, int count, int comp,
1197 char *target, int *lastRating, int *nTied)
1199 char newLine[MAX_RANK_LINE];
1200 char login[MAX_LOGIN_NAME];
1201 int rating, findable, nGames, is_comp;
1203 findable = (count > 0) && !feof(fp);
1206 fgets(newLine, MAX_RANK_LINE - 1, fp);
1209 else if (newLine[0] != '\0')
1210 sscanf(newLine, "%s %d %d %d",
1211 login, &rating, &nGames, &is_comp);
1214 } while (!CountRankLine(comp, login, nGames, is_comp) && findable
1215 && strcasecmp(login, target));
1218 if (!strcasecmp(login, target)
1219 && !CountRankLine(comp, login, nGames, is_comp)) {
1220 pprintf_highlight(p, "---- %-12.12s %4s", login, ratstr(rating));
1223 } else if (*lastRating == rating && *nTied < 1) {
1225 if (!strcasecmp(login, target))
1226 pprintf_highlight(p, "%-12.12s %4s", login, ratstr(rating));
1228 pprintf(p, "%-12.12s %4s", login, ratstr(rating));
1233 if (*lastRating == rating)
1237 if (!strcasecmp(login, target))
1238 pprintf_highlight(p, "%4d. %-12.12s %4s",
1239 count, login, ratstr(rating));
1241 pprintf(p, "%4d. %-12.12s %4s",
1242 count, login, ratstr(rating));
1244 *lastRating = rating;
1248 pprintf(p, "%25s", "");
1253 static int CountAbove(int num, int blitz, int std, int wild, int which)
1261 return (max <= (num + 1) / 2 ? max - 1 : (num + 1) / 2);
1264 static int ShowRankLines(int p, FILE * fb, FILE * fs, FILE * fw, int bCount,
1265 int sCount, int wCount, int n, int showComp, int show, char *target)
1267 int lastBlitz = 9999, nTiedBlitz = 0;
1268 int lastStd = 9999, nTiedStd = 0;
1269 int lastWild = 9999, nTiedWild = 0;
1274 if (CheckFlag(show, SHOW_BLITZ)) {
1275 PositionFilePtr(fb, bCount, &lastBlitz, &nTiedBlitz, showComp);
1277 FlagOFF(show, SHOW_BLITZ);
1279 if (CheckFlag(show, SHOW_STANDARD)) {
1280 PositionFilePtr(fs, sCount, &lastStd, &nTiedStd, showComp);
1282 FlagOFF(show, SHOW_STANDARD);
1284 if (CheckFlag(show, SHOW_WILD)) {
1285 PositionFilePtr(fw, wCount, &lastWild, &nTiedWild, showComp);
1287 FlagOFF(show, SHOW_WILD);
1289 if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD))
1291 DisplayRankHead(p, show);
1293 for (i = 0; i < n && show; i++) {
1294 if (CheckFlag(show, SHOW_BLITZ))
1295 bCount += ShowRankEntry(p, fb, bCount, showComp, target,
1296 &lastBlitz, &nTiedBlitz);
1297 if (CheckFlag(show, SHOW_STANDARD))
1298 sCount += ShowRankEntry(p, fs, sCount, showComp, target,
1299 &lastStd, &nTiedStd);
1300 if (CheckFlag(show, SHOW_WILD))
1301 wCount += ShowRankEntry(p, fw, wCount, showComp, target,
1302 &lastWild, &nTiedWild);
1308 static int DisplayTargetRank(int p, char *target, int show, int showComp)
1311 int blitzRank = -1, blitzCount;
1312 int stdRank = -1, stdCount;
1313 int wildRank = -1, wildCount;
1315 char Path[MAX_FILENAME_SIZE];
1316 FILE *fb = NULL, *fs = NULL, *fw = NULL;
1318 if (CheckFlag(show, SHOW_BLITZ)) {
1319 GetRankFileName(Path, TYPE_BLITZ);
1320 fb = (FILE *) fopen_s(Path, "r");
1322 blitzRank = GetRank(fb, target, showComp);
1324 FlagOFF(show, SHOW_BLITZ);
1326 if (CheckFlag(show, SHOW_STANDARD)) {
1327 GetRankFileName(Path, TYPE_STAND);
1328 fs = (FILE *) fopen_s(Path, "r");
1330 stdRank = GetRank(fs, target, showComp);
1332 FlagOFF(show, SHOW_STANDARD);
1334 if (CheckFlag(show, SHOW_WILD)) {
1335 GetRankFileName(Path, TYPE_WILD);
1336 if (CheckFlag(show, SHOW_WILD))
1337 fw = (FILE *) fopen_s(Path, "r");
1339 wildRank = GetRank(fw, target, showComp);
1341 FlagOFF(show, SHOW_WILD);
1343 if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD)) {
1344 pprintf(p, "No ratings to show.\n");
1345 if (fb != NULL) fclose(fb);
1346 if (fs != NULL) fclose(fs);
1347 if (fw != NULL) fclose(fw);
1350 numAbove = CountAbove(numToShow, blitzRank, stdRank, wildRank, show);
1351 blitzCount = blitzRank - numAbove;
1352 stdCount = stdRank - numAbove;
1353 wildCount = wildRank - numAbove;
1355 ShowRankLines(p, fb, fs, fw, blitzCount, stdCount, wildCount,
1356 numToShow, showComp, show, target);
1357 if (fb != NULL) fclose(fb);
1358 if (fs != NULL) fclose(fs);
1359 if (fw != NULL) fclose(fw);
1363 static int DisplayRankedPlayers(int p, int start, int end,
1364 int show, int showComp)
1366 int num = end - start + 1;
1367 FILE *fb = NULL, *fs = NULL, *fw = NULL;
1368 char Path[MAX_FILENAME_SIZE];
1376 if (CheckFlag(show, SHOW_BLITZ)) {
1377 GetRankFileName(Path, TYPE_BLITZ);
1378 fb = (FILE *) fopen_s(Path, "r");
1380 FlagOFF(show, SHOW_BLITZ);
1382 if (CheckFlag(show, SHOW_STANDARD)) {
1383 GetRankFileName(Path, TYPE_STAND);
1384 fs = (FILE *) fopen_s(Path, "r");
1386 FlagOFF(show, SHOW_STANDARD);
1388 if (CheckFlag(show, SHOW_WILD)) {
1389 GetRankFileName(Path, TYPE_WILD);
1390 fw = (FILE *) fopen_s(Path, "r");
1392 FlagOFF(show, SHOW_WILD);
1394 ShowRankLines(p, fb, fs, fw, start, start, start,
1395 num, showComp, show, "");
1405 static int ShowFromString(char *s)
1407 int i, len = strlen(s);
1410 if (s == NULL || s[0] == '\0')
1411 return SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
1412 for (i = 0; i < len; i++) {
1415 FlagON(show, SHOW_BLITZ);
1418 FlagON(show, SHOW_STANDARD);
1421 FlagON(show, SHOW_WILD);
1428 static int Best(int p, param_list param, int ShowComp)
1430 int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
1432 if (param[0].type != TYPE_NULL)
1433 show = ShowFromString(param[0].val.word);
1435 DisplayRankedPlayers(p, 1, 20, show, ShowComp);