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 == TYPE_KNIGHTMATE || type == TYPE_GOTHIC || type == TYPE_CAPABLANCA) { /* 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 == TYPE_KNIGHTMATE || type == TYPE_GOTHIC || type == TYPE_CAPABLANCA) { /* 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 case TYPE_KNIGHTMATE:
470 case TYPE_CAPABLANCA:
472 p_stats = &pp->w_stats;
475 p_stats = &pp->l_stats;
478 p_stats = &pp->bug_stats;
479 part_stats = &player_globals.parray[pp->partner].bug_stats;
480 UpdateStats(part_stats, rPart, sPart, now);
483 /* Calculate effective pre-game sterrs. ltime==0 implies never had sterr. */
484 UpdateStats(p_stats, r, s, now);
485 if (p_stats->rating == 0 && p_stats->num == 0)
490 /* Calculates new rating and standard error. By vek. The person */
491 /* who invented the ratings system is Mark E. Glickman, Ph.D. */
492 /* His e-mail address is glickman@hustat.harvard.edu as of April '95. */
493 /* Postscript copy of the note I coded this from should be available */
494 /* for ftp from ics.onenet.net, if not elsewhere. */
496 void rating_sterr_delta(int p1, int p2, int type, int gtime, int result,
497 int *deltarating, double *newsterr)
500 double s1, s2, s1part, s2part;
501 int r1, r2, r1part, r2part, new; /* Initial sterrs and ratings */
502 double q, E, fs2, denominator, GK, w; /* Parts of fancy formulas */
503 double delta; /* Result to return */
505 GetCurrentStats (p2, &r2, &s2, &new, &r2part, &s2part, type, gtime);
506 GetCurrentStats (p1, &r1, &s1, &new, &r1part, &s1part, type, gtime);
509 if (result == RESULT_WIN) {
511 } else if (result == RESULT_DRAW) {
516 if (type != TYPE_BUGHOUSE) {
518 E = GE(r1, r2, s2, &fs2); /* side effect: calculate fs2 */
521 E = GBE(r1, r1part, r2, r2part, s1part, s2, s2part, &fs2);
523 denominator = 1.0 / (s1 * s1) + q * q * fs2 * fs2 * E * (1.0 - E);
524 GK = q * fs2 / denominator;
526 delta = GK * (w - E);
528 *deltarating = Round(Gr0 + delta);
530 *deltarating = Round(delta); /* Returned values: deltarating,
532 *newsterr = 1.0 / sqrt(denominator);
535 int rating_update(int g, int link_game)
538 double wSigma, bSigma; /* vek */
541 struct statistics *w_stats;
542 struct statistics *b_stats;
546 int inprogress = (g == player_globals.parray[game_globals.garray[g].black].game);
547 /* if this is adjudication of stored game, be quiet about ratings change */
549 if (game_globals.garray[g].type == TYPE_BLITZ) {
550 w_stats = &player_globals.parray[game_globals.garray[g].white].b_stats;
551 b_stats = &player_globals.parray[game_globals.garray[g].black].b_stats;
552 } else if (game_globals.garray[g].type == TYPE_STAND) {
553 w_stats = &player_globals.parray[game_globals.garray[g].white].s_stats;
554 b_stats = &player_globals.parray[game_globals.garray[g].black].s_stats;
555 } else if (game_globals.garray[g].type == TYPE_WILD
556 || game_globals.garray[g].type == TYPE_KNIGHTMATE
557 || game_globals.garray[g].type == TYPE_GOTHIC
558 || game_globals.garray[g].type == TYPE_CAPABLANCA) {
559 w_stats = &player_globals.parray[game_globals.garray[g].white].w_stats;
560 b_stats = &player_globals.parray[game_globals.garray[g].black].w_stats;
561 } else if (game_globals.garray[g].type == TYPE_LIGHT) {
562 w_stats = &player_globals.parray[game_globals.garray[g].white].l_stats;
563 b_stats = &player_globals.parray[game_globals.garray[g].black].l_stats;
564 } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
565 w_stats = &player_globals.parray[game_globals.garray[g].white].bug_stats;
566 b_stats = &player_globals.parray[game_globals.garray[g].black].bug_stats;
568 d_printf( "CHESSD: Can't update untimed ratings!\n");
572 switch (game_globals.garray[g].result) {
577 if (game_globals.garray[g].winner == WHITE) {
592 case END_FLAGNOMATERIAL:
593 wRes = bRes = RESULT_DRAW;
596 d_printf( "CHESSD: Update undecided game %d?\n", game_globals.garray[g].result);
599 /* Use end time, not start time; important for adjourned games. */
600 /* gtime = untenths(game_globals.garray[g].timeOfStart); */
602 rating_sterr_delta(game_globals.garray[g].white, game_globals.garray[g].black,
603 game_globals.garray[g].type, gtime, wRes,
606 rating_sterr_delta(game_globals.garray[g].black, game_globals.garray[g].white,
607 game_globals.garray[g].type, gtime, bRes,
610 /* hersco, 1/23/96: We now know rating changes for these players;
611 update partners' game. */
612 if (link_game >= 0) {
613 game_globals.garray[link_game].winner = CToggle(game_globals.garray[g].winner);
614 game_globals.garray[link_game].result = game_globals.garray[g].result;
615 rating_update (link_game, -1); /* but don't recurse any deeper. */
617 /* vek: Update time of last rated game played, for future ratings calcs. */
618 /* Kept independently for blitz and standard. */
619 w_stats->ltime = gtime;
620 b_stats->ltime = gtime;
621 /* end vek add 4/5/95 */
623 if (wRes == RESULT_WIN) {
625 } else if (wRes == RESULT_LOSS) {
631 if (bRes == RESULT_WIN) {
633 } else if (bRes == RESULT_LOSS) {
639 rating_remove(w_stats->rating, game_globals.garray[g].type);
640 rating_remove(b_stats->rating, game_globals.garray[g].type);
643 pprintf(game_globals.garray[g].white, "%s rating adjustment: %d ",
644 TypeStrings[game_globals.garray[g].type], w_stats->rating);
645 pprintf(game_globals.garray[g].black, "%s rating adjustment: %d ",
646 TypeStrings[game_globals.garray[g].type], b_stats->rating);
648 if (wDelta < -1000) {
649 pprintf(game_globals.garray[g].white, "not changed due to bug (way too small)! sorry!\n");
650 d_printf( "CHESSD: Got too small ratings bug for %s (w) vs. %s\n",
651 player_globals.parray[game_globals.garray[g].white].login, player_globals.parray[game_globals.garray[g].black].login);
652 } else if (wDelta > 3000) {
653 pprintf(game_globals.garray[g].white, "not changed due to bug (way too big)! sorry!\n");
654 d_printf( "CHESSD: Got too big ratings bug for %s (w) vs. %s\n",
655 player_globals.parray[game_globals.garray[g].white].login, player_globals.parray[game_globals.garray[g].black].login);
657 w_stats->rating += wDelta;
658 w_stats->sterr = wSigma;
661 if (bDelta < -1000) {
662 pprintf(game_globals.garray[g].black, "not changed due to bug (way too small)! sorry! ");
663 d_printf( "CHESSD: Got too small ratings bug for %s (b) vs. %s\n",
664 player_globals.parray[game_globals.garray[g].black].login, player_globals.parray[game_globals.garray[g].white].login);
665 } else if (bDelta > 3000) {
666 pprintf(game_globals.garray[g].black, "not changed due to bug (way too big)! sorry! ");
667 d_printf( "CHESSD: Got too big ratings bug for %s (b) vs. %s\n",
668 player_globals.parray[game_globals.garray[g].black].login, player_globals.parray[game_globals.garray[g].white].login);
670 b_stats->rating += bDelta;
671 b_stats->sterr = bSigma;
672 } /* error messages down to vek */
674 rating_add(w_stats->rating, game_globals.garray[g].type);
675 rating_add(b_stats->rating, game_globals.garray[g].type);
677 if ((w_stats->rating > w_stats->best) && (is_active(w_stats->num))) {
678 w_stats->best = w_stats->rating;
679 w_stats->whenbest = time(NULL);
681 if ((b_stats->rating > b_stats->best) && (is_active(b_stats->num))) {
682 b_stats->best = b_stats->rating;
683 b_stats->whenbest = time(NULL);
686 /* ratings are saved later - DAV */
689 if (game_globals.garray[g].type == TYPE_BLITZ) {
691 Rb_total += (w_stats->rating + b_stats->rating) / 2.0;
692 } else if (game_globals.garray[g].type == TYPE_STAND) {
694 Rs_total += (w_stats->rating + b_stats->rating) / 2.0;
695 } else if (game_globals.garray[g].type == TYPE_LIGHT) {
697 Rl_total += (w_stats->rating + b_stats->rating) / 2.0;
698 } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
700 Rbug_total += (w_stats->rating + b_stats->rating) / 2.0;
701 } else if (game_globals.garray[g].type == TYPE_WILD
702 || game_globals.garray[g].type == TYPE_KNIGHTMATE
703 || game_globals.garray[g].type == TYPE_CAPABLANCA
704 || game_globals.garray[g].type == TYPE_GOTHIC) {
706 Rw_total += (w_stats->rating + b_stats->rating) / 2.0;
710 pprintf(game_globals.garray[g].white, "--> %d\n", w_stats->rating);
711 pprintf(game_globals.garray[g].black, "--> %d\n", b_stats->rating);
714 UpdateRank(game_globals.garray[g].type, player_globals.parray[game_globals.garray[g].white].name,
715 w_stats, player_globals.parray[game_globals.garray[g].white].name);
716 UpdateRank(game_globals.garray[g].type, player_globals.parray[game_globals.garray[g].black].name,
717 b_stats, player_globals.parray[game_globals.garray[g].black].name);
721 static void ShowAssess (int p, int p1, int p2, int type1, int type2)
724 int win1, win2, win3, win4;
725 int draw1, draw2, draw3, draw4;
726 int loss1, loss2, loss3, loss4;
727 double newsterr1, newsterr2, newsterr3, newsterr4;
728 int nowtime = time(0);
729 char line[100]; /* should never need any more than 80. */
731 if (type1 == TYPE_BUGHOUSE) {
732 p3 = player_globals.parray[p1].partner;
733 p4 = player_globals.parray[p2].partner;
735 if (!CheckPFlag(p3, PFLAG_REG) || !CheckPFlag(p4, PFLAG_REG)) {
736 pprintf (p, "Bughouse with unregistered partners cannot be rated.\n");
743 rating_sterr_delta(p1, p2, type1, nowtime, RESULT_WIN, &win1, &newsterr1);
744 rating_sterr_delta(p1, p2, type1, nowtime, RESULT_DRAW, &draw1, &newsterr1);
745 rating_sterr_delta(p1, p2, type1, nowtime, RESULT_LOSS, &loss1, &newsterr1);
746 rating_sterr_delta(p2, p1, type1, nowtime, RESULT_WIN, &win2, &newsterr2);
747 rating_sterr_delta(p2, p1, type1, nowtime, RESULT_DRAW, &draw2, &newsterr2);
748 rating_sterr_delta(p2, p1, type1, nowtime, RESULT_LOSS, &loss2, &newsterr2);
751 rating_sterr_delta(p3,p4, type2, nowtime,RESULT_WIN, &win3, &newsterr3);
752 rating_sterr_delta(p3,p4, type2, nowtime,RESULT_DRAW, &draw3, &newsterr3);
753 rating_sterr_delta(p3,p4, type2, nowtime,RESULT_LOSS, &loss3, &newsterr3);
754 rating_sterr_delta(p4,p3, type2, nowtime,RESULT_WIN, &win4, &newsterr4);
755 rating_sterr_delta(p4,p3, type2, nowtime,RESULT_DRAW, &draw4, &newsterr4);
756 rating_sterr_delta(p4,p3, type2, nowtime,RESULT_LOSS, &loss4, &newsterr4);
758 sprintf (line, "%7s", "");
759 if (type1 != TYPE_BUGHOUSE) {
760 CenterText (&line[7], TypeStrings[type1], 35, 1);
763 CenterText (&line[44], TypeStrings[type2], 35, 0);
767 CenterText (&line[7], "Bughouse", 72, 0);
769 pprintf (p, "\n%s\n", line);
771 sprintf (line, "%7s", "");
772 CenterText (&line[7], player_globals.parray[p1].name, 17, 1);
774 CenterText (&line[25], player_globals.parray[p2].name, 17, 1);
777 CenterText (&line[44], player_globals.parray[p3].name, 17, 1);
779 CenterText (&line[62], player_globals.parray[p4].name, 17, 0);
781 pprintf (p, "%s\n", line);
782 pprintf (p, "%7s (%4s RD %5.1f) ",
783 "", ratstrii(GetRating(&player_globals.parray[p1], type1), p1),
784 GetRD(&player_globals.parray[p1], type1));
785 pprintf (p, " (%4s RD %5.1f)",
786 ratstrii(GetRating(&player_globals.parray[p2], type1), p2),
787 GetRD(&player_globals.parray[p2], type1));
789 pprintf (p, " (%4s RD %5.1f)",
790 ratstrii(GetRating(&player_globals.parray[p3], type2), p3),
791 GetRD(&player_globals.parray[p3], type2));
792 pprintf (p, " (%4s RD %5.1f)",
793 ratstrii(GetRating(&player_globals.parray[p4], type2), p4),
794 GetRD(&player_globals.parray[p4], type2));
796 pprintf (p, "\n%7s%9d %17d", "Win:", win1, loss2);
798 pprintf (p, " %17d %17d", win3, loss4);
799 pprintf (p, "\n%7s%9d %17d", "Draw:", draw1, draw2);
801 pprintf (p, " %17d %17d", draw3, draw4);
802 pprintf (p, "\n%7s%9d %17d", "Loss:", loss1, win2);
804 pprintf (p, " %17d %17d", loss3, win4);
805 pprintf (p, "\n%7s%10.1f %17.1f", "New RD:", newsterr1, newsterr2);
807 pprintf (p, " %17.1f %17.1f", newsterr3, newsterr4);
811 int com_assess(int p, param_list param)
813 struct player *pp = &player_globals.parray[p];
814 int p1 = p, p2, g, gametype = -1;
815 int p1_connected = 1, p2_connected = 1;
817 /* Hawk: Now assess can be used with players not */
818 /* logged on -- I wonder if anyone doesn't */
819 /* get just a bit confused here :) */
821 if (param[0].type == TYPE_NULL) {
824 pprintf(p, "You are not playing or examining a game.\n");
826 } else if ((game_globals.garray[g].status == GAME_EXAMINE) || (game_globals.garray[g].status == GAME_SETUP)) {
827 gametype = game_globals.garray[g].type;
828 if (!strcmp(game_globals.garray[g].black_name, pp->name)) {
829 if (game_globals.garray[g].rated) {
830 pcommand(p, "assess %s %s %s\n", game_globals.garray[g].black_name,
831 game_globals.garray[g].white_name, TypeStrings[gametype]);
832 return COM_OK_NOPROMPT;
834 pprintf(p, "You are not involved in a rated game.\n");
837 if (game_globals.garray[g].rated) {
838 pcommand(p, "assess %s %s %s\n", game_globals.garray[g].white_name,
839 game_globals.garray[g].black_name, TypeStrings[gametype]);
840 return COM_OK_NOPROMPT;
842 pprintf(p, "You are not involved in a rated game.\n");
848 gametype = game_globals.garray[g].type;
850 } else if (param[0].type == TYPE_INT) {
851 if (param[1].type != TYPE_NULL) {
852 pprintf(p, "Assess <game number> cannot use other parameters.\n");
855 g = param[0].val.integer - 1;
856 if (g < 0 || g >= game_globals.g_num) {
857 pprintf (p, "There is no game %d.\n", g+1);
860 gametype = game_globals.garray[g].type;
861 pcommand(p, "assess %s %s %s\n", game_globals.garray[g].white_name,
862 game_globals.garray[g].black_name, TypeStrings[gametype]);
863 return COM_OK_NOPROMPT;
865 if (!FindPlayer(p, param[0].val.word, &p2, &p2_connected)) {
866 pprintf(p, "No user named \"%s\" was found.\n", param[0].val.word);
869 if (param[1].type != TYPE_NULL) {
871 p1_connected = p2_connected;
872 if (!FindPlayer(p, param[1].val.word, &p2, &p2_connected)) {
873 pprintf(p, "No user named \"%s\" was found.\n", param[1].val.word);
878 if (param[2].type == TYPE_WORD) {
879 param[2].val.word[0] = toupper(param[2].val.word[0]);
880 for (gametype = 0; gametype < NUM_GAMETYPES; gametype++)
881 if (!strcmp(TypeStrings[gametype], param[2].val.word))
886 if (gametype >= NUM_GAMETYPES) {
887 pprintf(p, "There is no game type %s.\n", param[2].val.word);
888 } else if (p1 == p2) {
889 pprintf(p, "You can't assess the same players.\n");
890 } else if (!CheckPFlag(p1, PFLAG_REG) || !CheckPFlag(p2, PFLAG_REG)) {
891 pprintf(p, "You can only assess games between registered players.\n");
892 } else if (gametype >= 0) {
893 if (gametype == TYPE_BUGHOUSE
894 && (player_globals.parray[p1].partner < 0 || player_globals.parray[p2].partner < 0)) {
895 pprintf(p, "You can only assess bughouse games if both players have partners.\n");
896 } else if (gametype == TYPE_UNTIMED || gametype == TYPE_NONSTANDARD) {
897 pprintf (p, "%s games are never rated.\n", TypeStrings[gametype]);
899 ShowAssess (p, p1, p2, gametype, -1);
902 ShowAssess (p, p1, p2, TYPE_BLITZ, TYPE_STAND);
903 ShowAssess (p, p1, p2, TYPE_LIGHT, TYPE_WILD);
904 if (player_globals.parray[p1].partner >= 0 && player_globals.parray[p2].partner >= 0
905 && player_globals.parray[p1].partner != p2)
906 ShowAssess (p, p1, p2, TYPE_BUGHOUSE, TYPE_BUGHOUSE);
916 int com_best(int p, param_list param)
918 return Best(p, param, 1);
921 int com_hbest(int p, param_list param)
923 return Best(p, param, 0);
927 int com_best(int p, param_list param)
931 pprintf(p, "Standard Blitz Wild\n");
932 for (i = 0; i < MAX_BEST; i++) {
933 if ((i >= numS) && (i >= numB))
936 pprintf(p, "%4d %-17s ", bestS[i].rating, bestS[i].name);
941 pprintf(p, "%4d %-17s ", bestB[i].rating, bestB[i].name);
946 pprintf(p, "%4d %-17s\n", bestW[i].rating, bestW[i].name);
955 int com_statistics(int p, param_list param)
957 pprintf(p, " Standard Blitz Lightning Wild\n");
958 pprintf(p, "average: %7.2f %7.2f %7.2f %7.2f\n", Ratings_S_Average, Ratings_B_Average, Ratings_L_Average, Ratings_W_Average);
959 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);
960 pprintf(p, "number : %7d %7d %7d %7d\n", Rs_count, Rb_count, Rl_count, Rw_count);
964 int com_fixrank(int p, param_list param)
968 if (!FindPlayer(p, param[0].val.word, &p1, &connected))
970 UpdateRank(TYPE_BLITZ, player_globals.parray[p1].name, &player_globals.parray[p1].b_stats,
971 player_globals.parray[p1].name);
972 UpdateRank(TYPE_STAND, player_globals.parray[p1].name, &player_globals.parray[p1].s_stats,
973 player_globals.parray[p1].name);
974 UpdateRank(TYPE_WILD, player_globals.parray[p1].name, &player_globals.parray[p1].w_stats,
975 player_globals.parray[p1].name);
981 int com_rank(int p, param_list param)
983 return DisplayRank(p, param, 1);
986 int com_hrank(int p, param_list param)
988 return DisplayRank(p, param, 0);
991 static int DisplayRank(int p, param_list param, int showComputers)
993 struct player *pp = &player_globals.parray[p];
994 int start, end, target, connected;
995 int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
997 if (param[0].type == TYPE_NULL) {
998 DisplayTargetRank(p, pp->name, show, showComputers);
1000 } else if (isdigit(param[0].val.word[0])) {
1002 sscanf(param[0].val.word, "%d-%d", &start, &end);
1003 if (end > 0 && (param[1].type != TYPE_NULL))
1004 show = ShowFromString(param[1].val.word);
1005 DisplayRankedPlayers(p, start, end, show, showComputers);
1008 target = player_search(p, param[0].val.word);
1010 pprintf(p, "Target %s not found.\n", param[0].val.word);
1013 connected = (target > 0);
1015 target = -target - 1;
1019 if (param[1].type != TYPE_NULL)
1020 show = ShowFromString(param[1].val.word);
1021 DisplayTargetRank(p, player_globals.parray[target].name, show, showComputers);
1023 player_remove(target);
1028 /* CompareStats returns 1 if s1 comes first, -1 if s2 comes first, and 0
1029 if neither takes precedence. */
1031 static int CompareStats(char *name1, statistics *s1,
1032 char *name2, statistics *s2)
1041 else if (s2 == NULL)
1044 if (s1->rating > s2->rating)
1046 if (s1->rating < s2->rating)
1049 for (i = 0; i < l1; i++) {
1050 if (name2[i] == '\0')
1052 if (tolower(name1[i]) < tolower(name2[i]))
1054 if (tolower(name1[i]) > tolower(name2[i]))
1057 if (name2[i] != '\0')
1059 /* if (s1->sterr < s2->sterr) return 1;
1060 if (s1->sterr > s2->sterr) return -1;
1061 if (s1->num > s2->num) return 1;
1062 if (s1->num < s2->num) return -1;
1064 d_printf( "Duplicate entries found: %s.\n", name1);
1068 static int GetRankFileName(char *out, int type)
1072 sprintf(out, "%s/rank.blitz", STATS_DIR);
1075 sprintf(out, "%s/rank.std", STATS_DIR);
1078 sprintf(out, "%s/rank.wild", STATS_DIR);
1085 /* loon: Turning this off 28 Oct 1995 (temporary:)) since we're lagged
1087 void UpdateRank(int type, char *addName,
1088 struct statistics *sNew, char *delName)
1092 void UpdateRank(int type, char *addName,
1093 struct statistics *sNew, char *delName)
1095 char RankFile[MAX_FILENAME_SIZE];
1096 char TmpRankFile[MAX_FILENAME_SIZE];
1097 char line[MAX_RANK_LINE];
1098 char login[MAX_LOGIN_NAME];
1105 if (GetRankFileName(RankFile, type) < 0)
1107 fp = fopen_s(RankFile, "r");
1109 d_printf( "Can't open rank file to update.\n");
1112 sprintf(TmpRankFile, "%s/tmpRank", sdir);
1113 fptemp = fopen_s(TmpRankFile, "w");
1114 if (fptemp == NULL) {
1115 d_printf("Unable to open rank file for updating.\n");
1119 while (fgets(line, MAX_RANK_LINE - 1, fp)) {
1120 sscanf(line, "%s %d %d %d", login, &sCur.rating,
1122 if (delName != NULL && !strcasecmp(delName, login)) { /* Kill name. */
1126 if (addName != NULL && CompareStats(addName, sNew, login, &sCur) > 0) {
1127 int computer = in_list(-1, L_COMPUTER, addName);
1128 fprintf(fptemp, "%s %d %d %d\n", addName, sNew->rating,
1129 sNew->num, computer);
1132 fprintf(fptemp, "%s %d %d %d\n", login, sCur.rating, sCur.num, comp);
1137 rename(TmpRankFile, RankFile);
1141 static void DisplayRankHead(int p, int show)
1143 char Line[MAX_STRING_LENGTH];
1146 if (CheckFlag(show, SHOW_BLITZ))
1147 strcat(Line, " Blitz ");
1148 if (CheckFlag(show, SHOW_STANDARD))
1149 strcat(Line, " Standard ");
1150 if (CheckFlag(show, SHOW_WILD))
1151 strcat(Line, " Wild");
1152 pprintf(p, "%s\n\n", Line);
1155 static int CountRankLine(int countComp, char *loginName,
1156 int num, int is_computer)
1158 if (loginName == NULL || loginName[0] == '\0')
1160 return (countComp || !is_computer) && (is_active(num));
1163 static int GetRank(FILE * fp, char *target, int countComp)
1166 int nGames, is_computer;
1167 int playerFound = 0;
1168 char line[MAX_RANK_LINE];
1169 char login[MAX_LOGIN_NAME];
1171 while (fgets(line, MAX_RANK_LINE - 1, fp) && !playerFound) {
1172 sscanf(line, "%s %*d %d %d", login, &nGames, &is_computer);
1173 if ((playerFound = !strcasecmp(login, target))
1174 || CountRankLine(countComp, login, nGames, is_computer))
1177 return (playerFound ? count : -1);
1180 static void PositionFilePtr(FILE * fp, int count, int *last,
1181 int *nTied, int showComp)
1183 int i, rating, nGames, is_computer;
1184 char login[MAX_LOGIN_NAME];
1185 char line[MAX_RANK_LINE];
1190 for (i = 1; i < count; i++) {
1192 fgets(line, MAX_RANK_LINE - 1, fp);
1195 sscanf(line, "%s %d %d %d", login, &rating, &nGames, &is_computer);
1196 } while (!CountRankLine(showComp, login, nGames, is_computer));
1197 if (rating != *last) {
1205 static int ShowRankEntry(int p, FILE * fp, int count, int comp,
1206 char *target, int *lastRating, int *nTied)
1208 char newLine[MAX_RANK_LINE];
1209 char login[MAX_LOGIN_NAME];
1210 int rating, findable, nGames, is_comp;
1212 findable = (count > 0) && !feof(fp);
1215 fgets(newLine, MAX_RANK_LINE - 1, fp);
1218 else if (newLine[0] != '\0')
1219 sscanf(newLine, "%s %d %d %d",
1220 login, &rating, &nGames, &is_comp);
1223 } while (!CountRankLine(comp, login, nGames, is_comp) && findable
1224 && strcasecmp(login, target));
1227 if (!strcasecmp(login, target)
1228 && !CountRankLine(comp, login, nGames, is_comp)) {
1229 pprintf_highlight(p, "---- %-12.12s %4s", login, ratstr(rating));
1232 } else if (*lastRating == rating && *nTied < 1) {
1234 if (!strcasecmp(login, target))
1235 pprintf_highlight(p, "%-12.12s %4s", login, ratstr(rating));
1237 pprintf(p, "%-12.12s %4s", login, ratstr(rating));
1242 if (*lastRating == rating)
1246 if (!strcasecmp(login, target))
1247 pprintf_highlight(p, "%4d. %-12.12s %4s",
1248 count, login, ratstr(rating));
1250 pprintf(p, "%4d. %-12.12s %4s",
1251 count, login, ratstr(rating));
1253 *lastRating = rating;
1257 pprintf(p, "%25s", "");
1262 static int CountAbove(int num, int blitz, int std, int wild, int which)
1270 return (max <= (num + 1) / 2 ? max - 1 : (num + 1) / 2);
1273 static int ShowRankLines(int p, FILE * fb, FILE * fs, FILE * fw, int bCount,
1274 int sCount, int wCount, int n, int showComp, int show, char *target)
1276 int lastBlitz = 9999, nTiedBlitz = 0;
1277 int lastStd = 9999, nTiedStd = 0;
1278 int lastWild = 9999, nTiedWild = 0;
1283 if (CheckFlag(show, SHOW_BLITZ)) {
1284 PositionFilePtr(fb, bCount, &lastBlitz, &nTiedBlitz, showComp);
1286 FlagOFF(show, SHOW_BLITZ);
1288 if (CheckFlag(show, SHOW_STANDARD)) {
1289 PositionFilePtr(fs, sCount, &lastStd, &nTiedStd, showComp);
1291 FlagOFF(show, SHOW_STANDARD);
1293 if (CheckFlag(show, SHOW_WILD)) {
1294 PositionFilePtr(fw, wCount, &lastWild, &nTiedWild, showComp);
1296 FlagOFF(show, SHOW_WILD);
1298 if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD))
1300 DisplayRankHead(p, show);
1302 for (i = 0; i < n && show; i++) {
1303 if (CheckFlag(show, SHOW_BLITZ))
1304 bCount += ShowRankEntry(p, fb, bCount, showComp, target,
1305 &lastBlitz, &nTiedBlitz);
1306 if (CheckFlag(show, SHOW_STANDARD))
1307 sCount += ShowRankEntry(p, fs, sCount, showComp, target,
1308 &lastStd, &nTiedStd);
1309 if (CheckFlag(show, SHOW_WILD))
1310 wCount += ShowRankEntry(p, fw, wCount, showComp, target,
1311 &lastWild, &nTiedWild);
1317 static int DisplayTargetRank(int p, char *target, int show, int showComp)
1320 int blitzRank = -1, blitzCount;
1321 int stdRank = -1, stdCount;
1322 int wildRank = -1, wildCount;
1324 char Path[MAX_FILENAME_SIZE];
1325 FILE *fb = NULL, *fs = NULL, *fw = NULL;
1327 if (CheckFlag(show, SHOW_BLITZ)) {
1328 GetRankFileName(Path, TYPE_BLITZ);
1329 fb = (FILE *) fopen_s(Path, "r");
1331 blitzRank = GetRank(fb, target, showComp);
1333 FlagOFF(show, SHOW_BLITZ);
1335 if (CheckFlag(show, SHOW_STANDARD)) {
1336 GetRankFileName(Path, TYPE_STAND);
1337 fs = (FILE *) fopen_s(Path, "r");
1339 stdRank = GetRank(fs, target, showComp);
1341 FlagOFF(show, SHOW_STANDARD);
1343 if (CheckFlag(show, SHOW_WILD)) {
1344 GetRankFileName(Path, TYPE_WILD);
1345 if (CheckFlag(show, SHOW_WILD))
1346 fw = (FILE *) fopen_s(Path, "r");
1348 wildRank = GetRank(fw, target, showComp);
1350 FlagOFF(show, SHOW_WILD);
1352 if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD)) {
1353 pprintf(p, "No ratings to show.\n");
1354 if (fb != NULL) fclose(fb);
1355 if (fs != NULL) fclose(fs);
1356 if (fw != NULL) fclose(fw);
1359 numAbove = CountAbove(numToShow, blitzRank, stdRank, wildRank, show);
1360 blitzCount = blitzRank - numAbove;
1361 stdCount = stdRank - numAbove;
1362 wildCount = wildRank - numAbove;
1364 ShowRankLines(p, fb, fs, fw, blitzCount, stdCount, wildCount,
1365 numToShow, showComp, show, target);
1366 if (fb != NULL) fclose(fb);
1367 if (fs != NULL) fclose(fs);
1368 if (fw != NULL) fclose(fw);
1372 static int DisplayRankedPlayers(int p, int start, int end,
1373 int show, int showComp)
1375 int num = end - start + 1;
1376 FILE *fb = NULL, *fs = NULL, *fw = NULL;
1377 char Path[MAX_FILENAME_SIZE];
1385 if (CheckFlag(show, SHOW_BLITZ)) {
1386 GetRankFileName(Path, TYPE_BLITZ);
1387 fb = (FILE *) fopen_s(Path, "r");
1389 FlagOFF(show, SHOW_BLITZ);
1391 if (CheckFlag(show, SHOW_STANDARD)) {
1392 GetRankFileName(Path, TYPE_STAND);
1393 fs = (FILE *) fopen_s(Path, "r");
1395 FlagOFF(show, SHOW_STANDARD);
1397 if (CheckFlag(show, SHOW_WILD)) {
1398 GetRankFileName(Path, TYPE_WILD);
1399 fw = (FILE *) fopen_s(Path, "r");
1401 FlagOFF(show, SHOW_WILD);
1403 ShowRankLines(p, fb, fs, fw, start, start, start,
1404 num, showComp, show, "");
1414 static int ShowFromString(char *s)
1416 int i, len = strlen(s);
1419 if (s == NULL || s[0] == '\0')
1420 return SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
1421 for (i = 0; i < len; i++) {
1424 FlagON(show, SHOW_BLITZ);
1427 FlagON(show, SHOW_STANDARD);
1430 FlagON(show, SHOW_WILD);
1437 static int Best(int p, param_list param, int ShowComp)
1439 int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
1441 if (param[0].type != TYPE_NULL)
1442 show = ShowFromString(param[0].val.word);
1444 DisplayRankedPlayers(p, 1, 20, show, ShowComp);