55eaa74bbf8e89d7cc30b585a4137fc5a7470201
[capablanca.git] / lasker-2.2.3 / src / ratings.c
1 /*
2    Copyright (c) 1993 Richard V. Nash.
3    Copyright (c) 2000 Dan Papasian
4    Copyright (C) Andrew Tridgell 2002
5    
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.
10    
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.
15    
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.
19 */
20
21 #include "includes.h"
22
23 static double Ratings_B_Average;
24 static double Ratings_B_StdDev;
25
26 static double Ratings_S_Average;
27 static double Ratings_S_StdDev;
28
29 static double Ratings_L_Average;
30 static double Ratings_L_StdDev;
31
32 static double Ratings_Bug_Average;
33 static double Ratings_Bug_StdDev;
34
35 static double Ratings_W_Average;
36 static double Ratings_W_StdDev;
37
38
39 static double Rb_M = 0.0, Rb_S = 0.0, Rb_total = 0.0;
40 static int Rb_count = 0;
41
42 static double Rs_M = 0.0, Rs_S = 0.0, Rs_total = 0.0;
43 static int Rs_count = 0;
44
45 static double Rl_M = 0.0, Rl_S = 0.0, Rl_total = 0.0;
46 static int Rl_count = 0;
47
48 static double Rbug_M = 0.0, Rbug_S = 0.0, Rbug_total = 0.0;
49 static int Rbug_count = 0;
50
51 static double Rw_M = 0.0, Rw_S = 0.0, Rw_total = 0.0;
52 static int Rw_count = 0;
53
54 /*
55 rateStruct bestS[MAX_BEST];
56 int numS = 0;
57 rateStruct bestB[MAX_BEST];
58 int numB = 0;
59 rateStruct bestW[MAX_BEST];
60 int numW = 0;
61 */
62
63 #define MAXHIST 30
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];
70
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);
77
78 static int is_active(int Games)
79 {
80         return (Games >= PROVISIONAL);
81 }
82
83
84 static void rating_add(int rating, int type)
85 {
86   int which;
87
88   which = (rating - LOWESTHIST) / 100;
89   if (which < 0)
90     which = 0;
91   if (which >= MAXHIST)
92     which = MAXHIST - 1;
93   if (type == TYPE_BLITZ) {
94     bHist[which] += 1;
95     Rb_count++;
96     Rb_total += rating;
97     if (Rb_count == 1) {
98       Rb_M = rating;
99     } else {
100       Rb_S = Rb_S + (rating - Rb_M) * (rating - Rb_M);
101       Rb_M = Rb_M + (rating - Rb_M) / (Rb_count);
102     }
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 */
106     wHist[which] += 1;
107     Rw_count++;
108     Rw_total += rating;
109     if (Rw_count == 1) {
110       Rw_M = rating;
111     } else {
112       Rw_S = Rw_S + (rating - Rw_M) * (rating - Rw_M);
113       Rw_M = Rw_M + (rating - Rw_M) / (Rw_count);
114     }
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 */
118     lHist[which] += 1;
119     Rl_count++;
120     Rl_total += rating;
121     if (Rl_count == 1) {
122       Rl_M = rating;
123     } else {
124       Rl_S = Rl_S + (rating - Rl_M) * (rating - Rl_M);
125       Rl_M = Rl_M + (rating - Rl_M) / (Rl_count);
126     }
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 */
130     bugHist[which] += 1;
131     Rbug_count++;
132     Rbug_total += rating;
133     if (Rbug_count == 1) {
134       Rbug_M = rating;
135     } else {
136       Rbug_S = Rbug_S + (rating - Rbug_M) * (rating - Rbug_M);
137       Rbug_M = Rbug_M + (rating - Rbug_M) / (Rbug_count);
138     }
139     Ratings_Bug_StdDev = sqrt(Rbug_S / Rbug_count);
140     Ratings_Bug_Average = Rbug_total / (double) Rbug_count;
141   } else {                      /* TYPE_STAND */
142     sHist[which] += 1;
143     Rs_count++;
144     Rs_total += rating;
145     if (Rs_count == 1) {
146       Rs_M = rating;
147     } else {
148       Rs_S = Rs_S + (rating - Rs_M) * (rating - Rs_M);
149       Rs_M = Rs_M + (rating - Rs_M) / (Rs_count);
150     }
151     Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
152     Ratings_S_Average = Rs_total / (double) Rs_count;
153   }
154 }
155
156 static void rating_remove(int rating, int type)
157 {
158   int which;
159
160   which = (rating - LOWESTHIST) / 100;
161   if (which < 0)
162     which = 0;
163   if (which >= MAXHIST)
164     which = MAXHIST - 1;
165   if (type == TYPE_BLITZ) {
166     bHist[which] = bHist[which] - 1;
167     if (bHist[which] < 0)
168       bHist[which] = 0;
169     if (Rb_count == 0)
170       return;
171     Rb_count--;
172     Rb_total -= rating;
173     if (Rb_count == 0) {
174       Rb_M = 0;
175       Rb_S = 0;
176     } else {
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)
180         Rb_S = 0;
181     }
182     if (Rb_count) {
183       Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
184       Ratings_B_Average = Rb_total / (double) Rb_count;
185     } else {
186       Ratings_B_StdDev = 0;
187       Ratings_B_Average = 0;
188     }
189   } else if (type == TYPE_WILD) {       /* TYPE_WILD */
190     wHist[which] = wHist[which] - 1;
191     if (wHist[which] < 0)
192       wHist[which] = 0;
193     if (Rw_count == 0)
194       return;
195     Rw_count--;
196     Rw_total -= rating;
197     if (Rw_count == 0) {
198       Rw_M = 0;
199       Rw_S = 0;
200     } else {
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)
204         Rw_S = 0;
205     }
206     if (Rw_count) {
207       Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
208       Ratings_W_Average = Rw_total / (double) Rw_count;
209     } else {
210       Ratings_W_StdDev = 0;
211       Ratings_W_Average = 0;
212     }
213   } else if (type == TYPE_LIGHT) {       /* TYPE_LIGHT */
214     lHist[which] = lHist[which] - 1;
215     if (lHist[which] < 0)
216       lHist[which] = 0;
217     if (Rl_count == 0)
218       return;
219     Rl_count--;
220     Rl_total -= rating;
221     if (Rl_count == 0) {
222       Rl_M = 0;
223       Rl_S = 0;
224     } else {
225       Rl_M = Rl_M - (rating - Rl_M) / (Rl_count);
226       Rl_S = Rl_S - (rating - Rl_M) * (rating - Rl_M);
227       if (Rl_S < 0)
228         Rl_S = 0;
229     }
230     if (Rl_count) {
231       Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
232       Ratings_L_Average = Rl_total / (double) Rl_count;
233     } else {
234       Ratings_L_StdDev = 0;
235       Ratings_L_Average = 0;
236     }
237   } else if (type == TYPE_BUGHOUSE) {       /* TYPE_BUGHOUSE */
238     bugHist[which] = bugHist[which] - 1;
239     if (bugHist[which] < 0)
240       bugHist[which] = 0;
241     if (Rbug_count == 0)
242       return;
243     Rbug_count--;
244     Rbug_total -= rating;
245     if (Rbug_count == 0) {
246       Rbug_M = 0;
247       Rbug_S = 0;
248     } else {
249       Rbug_M = Rbug_M - (rating - Rbug_M) / (Rbug_count);
250       Rbug_S = Rbug_S - (rating - Rbug_M) * (rating - Rbug_M);
251       if (Rbug_S < 0)
252         Rbug_S = 0;
253     }
254     if (Rbug_count) {
255       Ratings_Bug_StdDev = sqrt(Rbug_S / Rbug_count);
256       Ratings_Bug_Average = Rbug_total / (double) Rbug_count;
257     } else {
258       Ratings_Bug_StdDev = 0;
259       Ratings_Bug_Average = 0;
260     }
261   } else {                      /* TYPE_STAND */
262     sHist[which] = sHist[which] - 1;
263     if (sHist[which] < 0)
264       sHist[which] = 0;
265     if (Rs_count == 0)
266       return;
267     Rs_count--;
268     Rs_total -= rating;
269     if (Rs_count == 0) {
270       Rs_M = 0;
271       Rs_S = 0;
272     } else {
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)
276         Rs_S = 0;
277     }
278     if (Rs_count) {
279       Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
280       Ratings_S_Average = Rs_total / (double) Rs_count;
281     } else {
282       Ratings_S_StdDev = 0;
283       Ratings_S_Average = 0;
284     }
285   }
286 }
287
288 static void load_ratings(void)
289 {
290         FILE *fp;
291         int i;
292
293         fp = fopen_p("%s/newratingsV%d_data", "r", STATS_DIR, STATS_VERSION);
294         if (!fp) {
295                 d_printf( "CHESSD: Can't read ratings data!\n");
296                 return;
297         }
298
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);
303         
304         for (i = 0; i < MAXHIST; i++) {
305                 fscanf(fp, "%d %d %d %d", &sHist[i], &bHist[i], &wHist[i], &lHist[i]);
306         }
307         fclose(fp);
308         if (Rs_count) {
309                 Ratings_S_StdDev = sqrt(Rs_S / Rs_count);
310                 Ratings_S_Average = Rs_total / (double) Rs_count;
311         } else {
312                 Ratings_S_StdDev = 0;
313                 Ratings_S_Average = 0;
314         }
315         if (Rb_count) {
316                 Ratings_B_StdDev = sqrt(Rb_S / Rb_count);
317                 Ratings_B_Average = Rb_total / (double) Rb_count;
318         } else {
319                 Ratings_B_StdDev = 0;
320                 Ratings_B_Average = 0;
321         }
322         if (Rw_count) {
323                 Ratings_W_StdDev = sqrt(Rw_S / Rw_count);
324                 Ratings_W_Average = Rw_total / (double) Rw_count;
325         } else {
326                 Ratings_W_StdDev = 0;
327                 Ratings_W_Average = 0;
328         }
329         if (Rl_count) {
330                 Ratings_L_StdDev = sqrt(Rl_S / Rl_count);
331                 Ratings_L_Average = Rl_total / (double) Rl_count;
332         } else {
333                 Ratings_L_StdDev = 0;
334                 Ratings_L_Average = 0;
335         }
336 }
337
338 static void save_ratings(void)
339 {
340         FILE *fp;
341         int i;
342         
343         fp = fopen_p("%s/newratingsV%d_data", "w", STATS_DIR,STATS_VERSION);
344         if (!fp) {
345                 d_printf( "CHESSD: Can't write ratings data!\n");
346                 return;
347         }
348         
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);
353         
354         for (i = 0; i < MAXHIST; i++) {
355                 fprintf(fp, "%d %d %d %d\n", sHist[i], bHist[i], wHist[i], lHist[i]);
356         }
357         fclose(fp);
358 }
359
360 static void zero_stats(void)
361 {
362         int i;
363         for (i = 0; i < MAXHIST; i++) {
364                 sHist[i] = 0;
365                 bHist[i] = 0;
366                 wHist[i] = 0;
367                 lHist[i] = 0;
368         }
369         Rb_M = 0.0, Rb_S = 0.0, Rb_total = 0.0;
370         Rb_count = 0;
371         
372         Rs_M = 0.0, Rs_S = 0.0, Rs_total = 0.0;
373         Rs_count = 0;
374         
375         Rw_M = 0.0, Rw_S = 0.0, Rw_total = 0.0;
376         Rw_count = 0;
377         
378         Rl_M = 0.0, Rl_S = 0.0, Rl_total = 0.0;
379         Rl_count = 0;
380 }
381
382 void ratings_init(void)
383 {
384         zero_stats();
385         load_ratings();
386 }
387
388 static int Round (double x)
389 {
390   return (x < 0   ?   (int) (x - 0.5)   :   (int) (x + 0.5));
391 }
392
393 /*  Constants for Glicko system */
394 #define Gd 1.483
395 #define Gv 0.0005828166666667  /* (0.187)^2 / 60 */
396 #define Gr0 1720
397 #define Gs0 350
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 */
403
404 static double Gf(double ss)
405 {
406   return (1.0 / sqrt(1.0 + Gp * ss * ss));
407 }
408
409 /* Confusing but economical: calculate error and attenuation function together */
410 static double GE(int r, int rr, double ss, double *fss)
411 {
412   *fss = Gf(ss);
413   return (1.0 / (1.0 + pow(10.0, (rr - r) * (*fss) / 400.0)));
414 }
415
416 static double GBf(double sp, double ss, double ssp)
417 {
418   return (1.0 / sqrt(1.0 + GBp * (ss*ss + sp*sp + ssp*ssp)));
419 }
420
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)
424 {
425   *fss = GBf(sp, ss, ssp);
426   return (1.0 / (1.0 + pow(10.0, (rr + rrp - r - rp) * (*fss) / 800.0)));
427 }
428
429 double current_sterr(double s, long t)
430 {
431   double new;
432
433   if (t < 0)
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 */
437   if (new > Gs0)
438     new = Gs0;
439   return (new);
440 }
441
442 static void UpdateStats(struct statistics *p_stats, int *r, double *s, int now)
443 {
444   /* Calculate effective pre-game sterrs.  ltime==0 implies never had sterr. */
445
446   *s = current_sterr(p_stats->sterr, now - p_stats->ltime);
447
448   /* pre-game ratings */
449   *r = (p_stats->rating == 0 && p_stats->num == 0)  ?  Gr0
450                                                     :  p_stats->rating;
451   return;
452 }
453
454 static void GetCurrentStats (int p, int *r, double *s, int *new,
455                       int *rPart, double *sPart, int type, int now)
456 {
457   struct player *pp = &player_globals.parray[p];
458   struct statistics *p_stats = NULL;
459   struct statistics *part_stats;     /* for bughouse. */
460
461   switch (type) {
462     case TYPE_BLITZ:
463       p_stats = &pp->b_stats;
464       break;
465     case TYPE_STAND:
466       p_stats = &pp->s_stats;
467       break;
468     case TYPE_WILD:
469       p_stats = &pp->w_stats;
470       break;
471     case TYPE_LIGHT:
472       p_stats = &pp->l_stats;
473       break;
474     case TYPE_BUGHOUSE:
475       p_stats = &pp->bug_stats;
476       part_stats = &player_globals.parray[pp->partner].bug_stats;
477       UpdateStats(part_stats, rPart, sPart, now);
478       break;
479   }
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)
483     *new = 1;
484   else *new = 0;
485 }
486
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.                     */
492
493 void rating_sterr_delta(int p1, int p2, int type, int gtime, int result,
494                                 int *deltarating, double *newsterr)
495 {
496
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 */
501
502   GetCurrentStats (p2, &r2, &s2, &new, &r2part, &s2part, type, gtime);
503   GetCurrentStats (p1, &r1, &s1, &new, &r1part, &s1part, type, gtime);
504
505   /* now crunch */
506   if (result == RESULT_WIN) {
507     w = 1.0;
508   } else if (result == RESULT_DRAW) {
509     w = 0.5;
510   } else {
511     w = 0.0;
512   }
513   if (type != TYPE_BUGHOUSE) {
514     q = Gq;
515     E = GE(r1, r2, s2, &fs2);   /* side effect: calculate fs2 */
516   } else {
517     q = GBq;
518     E = GBE(r1, r1part, r2, r2part, s1part, s2, s2part, &fs2);
519   }
520   denominator = 1.0 / (s1 * s1) + q * q * fs2 * fs2 * E * (1.0 - E);
521   GK = q * fs2 / denominator;
522
523   delta = GK * (w - E);
524   if (new)
525     *deltarating = Round(Gr0 + delta);
526   else
527     *deltarating = Round(delta);        /* Returned values: deltarating,
528                                            newsterr */
529   *newsterr = 1.0 / sqrt(denominator);
530 }
531
532 int rating_update(int g, int link_game)
533 {
534   int wDelta, bDelta;
535   double wSigma, bSigma;        /* vek */
536
537   int wRes, bRes;
538   struct statistics *w_stats;
539   struct statistics *b_stats;
540
541   int gtime;
542
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 */
545
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;
561   } else {
562     d_printf( "CHESSD: Can't update untimed ratings!\n");
563     return -1;
564   }
565
566   switch (game_globals.garray[g].result) {
567   case END_CHECKMATE:
568   case END_RESIGN:
569   case END_FLAG:
570   case END_ADJWIN:
571     if (game_globals.garray[g].winner == WHITE) {
572       wRes = RESULT_WIN;
573       bRes = RESULT_LOSS;
574     } else {
575       bRes = RESULT_WIN;
576       wRes = RESULT_LOSS;
577     }
578     break;
579   case END_AGREEDDRAW:
580   case END_REPETITION:
581   case END_50MOVERULE:
582   case END_STALEMATE:
583   case END_NOMATERIAL:
584   case END_BOTHFLAG:
585   case END_ADJDRAW:
586   case END_FLAGNOMATERIAL:
587     wRes = bRes = RESULT_DRAW;
588     break;
589   default:
590     d_printf( "CHESSD: Update undecided game %d?\n", game_globals.garray[g].result);
591     return -1;
592   }
593   /* Use end time, not start time; important for adjourned games. */
594   /*  gtime = untenths(game_globals.garray[g].timeOfStart); */
595   gtime = time(0);
596   rating_sterr_delta(game_globals.garray[g].white, game_globals.garray[g].black,
597                      game_globals.garray[g].type, gtime, wRes,
598                      &wDelta, &wSigma);
599
600   rating_sterr_delta(game_globals.garray[g].black, game_globals.garray[g].white,
601                      game_globals.garray[g].type, gtime, bRes,
602                      &bDelta, &bSigma);
603
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. */
610   }
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 */
616
617   if (wRes == RESULT_WIN) {
618     w_stats->win++;
619   } else if (wRes == RESULT_LOSS) {
620     w_stats->los++;
621   } else {
622     w_stats->dra++;
623   }
624   w_stats->num++;
625   if (bRes == RESULT_WIN) {
626     b_stats->win++;
627   } else if (bRes == RESULT_LOSS) {
628     b_stats->los++;
629   } else {
630     b_stats->dra++;
631   }
632   b_stats->num++;
633   rating_remove(w_stats->rating, game_globals.garray[g].type);
634   rating_remove(b_stats->rating, game_globals.garray[g].type);
635
636   if (inprogress) {
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);
641   }
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);
650   } else {
651     w_stats->rating += wDelta;
652     w_stats->sterr = wSigma;
653   }
654
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);
663   } else {
664     b_stats->rating += bDelta;
665     b_stats->sterr = bSigma;
666   }                             /* error messages down to vek */
667
668   rating_add(w_stats->rating, game_globals.garray[g].type);
669   rating_add(b_stats->rating, game_globals.garray[g].type);
670
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);
674   }
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);
678   }
679
680 /* ratings are saved later - DAV */
681
682 /* foxbat 3.11.95 */
683   if (game_globals.garray[g].type == TYPE_BLITZ) {
684     Rb_count++;
685     Rb_total += (w_stats->rating + b_stats->rating) / 2.0;
686   } else if (game_globals.garray[g].type == TYPE_STAND) {
687     Rs_count++;
688     Rs_total += (w_stats->rating + b_stats->rating) / 2.0;
689   } else if (game_globals.garray[g].type == TYPE_LIGHT) {
690     Rl_count++;
691     Rl_total += (w_stats->rating + b_stats->rating) / 2.0;
692   } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
693     Rbug_count++;
694     Rbug_total += (w_stats->rating + b_stats->rating) / 2.0;
695   } else if (game_globals.garray[g].type == TYPE_WILD) {
696     Rw_count++;
697     Rw_total += (w_stats->rating + b_stats->rating) / 2.0;
698   }
699 /* end add */
700   if (inprogress) {
701     pprintf(game_globals.garray[g].white, "--> %d\n", w_stats->rating);
702     pprintf(game_globals.garray[g].black, "--> %d\n", b_stats->rating);
703   }
704   save_ratings();
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);
709   return 0;
710 }
711
712 static void ShowAssess (int p, int p1, int p2, int type1, int type2)
713 {
714   int p3, p4;
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. */
721
722   if (type1 == TYPE_BUGHOUSE) {
723     p3 = player_globals.parray[p1].partner;
724     p4 = player_globals.parray[p2].partner;
725     type2 = type1;
726     if (!CheckPFlag(p3, PFLAG_REG) || !CheckPFlag(p4, PFLAG_REG)) {
727       pprintf (p, "Bughouse with unregistered partners cannot be rated.\n");
728       return;
729     }
730   } else {
731     p3 = p1;
732     p4 = p2;
733   }
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);
740
741   if (type2 >= 0) {
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);
748   }
749   sprintf (line, "%7s", "");
750   if (type1 != TYPE_BUGHOUSE) {
751     CenterText (&line[7], TypeStrings[type1], 35, 1);
752     if (type2 >= 0) {
753       strcat (line, "  ");
754       CenterText (&line[44], TypeStrings[type2], 35, 0);
755     }
756   }
757   else {
758     CenterText (&line[7], "Bughouse", 72, 0);
759   }
760   pprintf (p, "\n%s\n", line);
761
762   sprintf (line, "%7s", "");
763   CenterText (&line[7], player_globals.parray[p1].name, 17, 1);
764   strcat (line, " ");
765   CenterText (&line[25], player_globals.parray[p2].name, 17, 1);
766   if (type2 >= 0) {
767     strcat (line, "  ");
768     CenterText (&line[44], player_globals.parray[p3].name, 17, 1);
769     strcat (line, " ");
770     CenterText (&line[62], player_globals.parray[p4].name, 17, 0);
771   }
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));
779   if (type2 >= 0) {
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));
786   }
787   pprintf (p, "\n%7s%9d %17d", "Win:", win1, loss2);
788   if (type2 >= 0)
789     pprintf (p, "  %17d %17d", win3, loss4);
790   pprintf (p, "\n%7s%9d %17d", "Draw:", draw1, draw2);
791   if (type2 >= 0)
792     pprintf (p, "  %17d %17d", draw3, draw4);
793   pprintf (p, "\n%7s%9d %17d", "Loss:", loss1, win2);
794   if (type2 >= 0)
795     pprintf (p, "  %17d %17d", loss3, win4);
796   pprintf (p, "\n%7s%10.1f %17.1f", "New RD:", newsterr1, newsterr2);
797   if (type2 >= 0)
798     pprintf (p, "  %17.1f %17.1f", newsterr3, newsterr4);
799   pprintf(p, "\n");
800 }
801
802 int com_assess(int p, param_list param)
803 {
804   struct player *pp = &player_globals.parray[p];
805   int p1 = p, p2, g, gametype = -1;
806   int p1_connected = 1, p2_connected = 1;
807
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 :)          */
811
812   if (param[0].type == TYPE_NULL) {
813     g = pp->game;
814     if (g < 0) {
815       pprintf(p, "You are not playing or examining a game.\n");
816       return COM_OK;
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;
824         } else {
825           pprintf(p, "You are not involved in a rated game.\n");
826         }
827       } else {
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;
832         } else {
833           pprintf(p, "You are not involved in a rated game.\n");
834         }
835       }
836       return COM_OK;
837     } else {
838       p2 = pp->opponent;
839       gametype = game_globals.garray[g].type;
840     }
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");
844       return COM_OK;
845     }
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);
849       return COM_OK;
850     }
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;
855   } else {
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);
858       return COM_OK;
859     }
860     if (param[1].type != TYPE_NULL) {
861       p1 = p2;
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);
865         if (!p1_connected)
866           player_remove(p1);
867         return COM_OK;
868       }
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))
873             break;
874       }
875     }
876   }
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]);
889     } else {
890       ShowAssess (p, p1, p2, gametype, -1);
891     }
892   } else {
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);
898   }
899   if (!p1_connected)
900       player_remove(p1);
901   if (!p2_connected)
902     player_remove(p2);
903   return COM_OK;
904 }
905
906
907 int com_best(int p, param_list param)
908 {
909   return Best(p, param, 1);
910 }
911
912 int com_hbest(int p, param_list param)
913 {
914   return Best(p, param, 0);
915 }
916
917 #if 0
918 int com_best(int p, param_list param)
919 {
920   int i;
921
922   pprintf(p, "Standard                Blitz                   Wild\n");
923   for (i = 0; i < MAX_BEST; i++) {
924     if ((i >= numS) && (i >= numB))
925       break;
926     if (i < numS) {
927       pprintf(p, "%4d %-17s  ", bestS[i].rating, bestS[i].name);
928     } else {
929       pprintf(p, "                        ");
930     }
931     if (i < numB) {
932       pprintf(p, "%4d %-17s  ", bestB[i].rating, bestB[i].name);
933     } else {
934       pprintf(p, "                        ");
935     }
936     if (i < numW) {
937       pprintf(p, "%4d %-17s\n", bestW[i].rating, bestW[i].name);
938     } else {
939       pprintf(p, "\n");
940     }
941   }
942   return COM_OK;
943 }
944 #endif
945
946 int com_statistics(int p, param_list param)
947 {
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);
952   return COM_OK;
953 }
954
955 int com_fixrank(int p, param_list param)
956 {
957   int p1, connected;
958
959   if (!FindPlayer(p, param[0].val.word, &p1, &connected))
960     return COM_OK;
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);
967   if (!connected)
968     player_remove(p1);
969   return COM_OK;
970 }
971
972 int com_rank(int p, param_list param)
973 {
974         return DisplayRank(p, param, 1);
975 }
976
977 int com_hrank(int p, param_list param)
978 {
979   return DisplayRank(p, param, 0);
980 }
981
982 static int DisplayRank(int p, param_list param, int showComputers)
983 {
984   struct player *pp = &player_globals.parray[p];
985   int start, end, target, connected;
986   int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
987
988   if (param[0].type == TYPE_NULL) {
989     DisplayTargetRank(p, pp->name, show, showComputers);
990     return COM_OK;
991   } else if (isdigit(param[0].val.word[0])) {
992     end = -1;
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);
997     return COM_OK;
998   } else {
999     target = player_search(p, param[0].val.word);
1000     if (target == 0) {
1001       pprintf(p, "Target %s not found.\n", param[0].val.word);
1002       return COM_OK;
1003     }
1004     connected = (target > 0);
1005     if (!connected)
1006       target = -target - 1;
1007     else
1008       target--;
1009
1010     if (param[1].type != TYPE_NULL)
1011       show = ShowFromString(param[1].val.word);
1012     DisplayTargetRank(p, player_globals.parray[target].name, show, showComputers);
1013     if (!connected)
1014       player_remove(target);
1015     return COM_OK;
1016   }
1017 }
1018
1019 /* CompareStats returns 1 if s1 comes first, -1 if s2 comes first, and 0
1020    if neither takes precedence. */
1021 #if 0
1022 static int CompareStats(char *name1, statistics *s1,
1023                           char *name2, statistics *s2)
1024 {
1025   int i, l1;
1026
1027   if (s1 == NULL)
1028     if (s2 == NULL)
1029       return 0;
1030     else
1031       return -1;
1032   else if (s2 == NULL)
1033     return 1;
1034
1035   if (s1->rating > s2->rating)
1036     return 1;
1037   if (s1->rating < s2->rating)
1038     return -1;
1039   l1 = strlen(name1);
1040   for (i = 0; i < l1; i++) {
1041     if (name2[i] == '\0')
1042       return -1;
1043     if (tolower(name1[i]) < tolower(name2[i]))
1044       return 1;
1045     if (tolower(name1[i]) > tolower(name2[i]))
1046       return -1;
1047   }
1048   if (name2[i] != '\0')
1049     return 1;
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;
1054 */
1055   d_printf( "Duplicate entries found: %s.\n", name1);
1056   return 0;
1057 }
1058 #endif
1059 static int GetRankFileName(char *out, int type)
1060 {
1061   switch (type) {
1062     case TYPE_BLITZ:
1063         sprintf(out, "%s/rank.blitz", STATS_DIR);
1064         return type;
1065     case TYPE_STAND:
1066         sprintf(out, "%s/rank.std", STATS_DIR);
1067         return type;
1068     case TYPE_WILD:
1069         sprintf(out, "%s/rank.wild", STATS_DIR);
1070         return type;
1071     default:
1072         return -1;
1073   }
1074 }
1075
1076 /* loon: Turning this off 28 Oct 1995 (temporary:)) since we're lagged
1077    into outer space */
1078 void UpdateRank(int type, char *addName,
1079                 struct statistics *sNew, char *delName)
1080 {}
1081
1082 #if 0
1083 void UpdateRank(int type, char *addName,
1084                 struct statistics *sNew, char *delName)
1085 {
1086   char RankFile[MAX_FILENAME_SIZE];
1087   char TmpRankFile[MAX_FILENAME_SIZE];
1088   char line[MAX_RANK_LINE];
1089   char login[MAX_LOGIN_NAME];
1090   
1091   int comp;
1092   statistics sCur;
1093   FILE *fp;
1094   FILE *fptemp;
1095
1096   if (GetRankFileName(RankFile, type) < 0)
1097     return;
1098   fp = fopen_s(RankFile, "r");
1099   if (fp == NULL) {
1100     d_printf( "Can't open rank file to update.\n");
1101     return;
1102   }
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");
1107     fclose (fp);
1108     return;
1109   }
1110   while (fgets(line, MAX_RANK_LINE - 1, fp)) {
1111     sscanf(line, "%s %d %d %d", login, &sCur.rating,
1112            &sCur.num, &comp);
1113     if (delName != NULL && !strcasecmp(delName, login)) {       /* Kill name. */
1114       delName = NULL;
1115       continue;
1116     }
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);
1121       addName = NULL;
1122     }
1123     fprintf(fptemp, "%s %d %d %d\n", login, sCur.rating, sCur.num, comp);
1124   }
1125   fclose(fptemp);
1126   fclose(fp);
1127
1128   rename(TmpRankFile, RankFile);
1129 }
1130 #endif
1131
1132 static void DisplayRankHead(int p, int show)
1133 {
1134   char Line[MAX_STRING_LENGTH];
1135
1136   Line[0] = '\0';
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);
1144 }
1145
1146 static int CountRankLine(int countComp, char *loginName,
1147                            int num, int is_computer)
1148 {
1149   if (loginName == NULL || loginName[0] == '\0')
1150     return 0;
1151   return (countComp || !is_computer) && (is_active(num));
1152 }
1153
1154 static int GetRank(FILE * fp, char *target, int countComp)
1155 {
1156   int count = 0;
1157   int nGames, is_computer;
1158   int playerFound = 0;
1159   char line[MAX_RANK_LINE];
1160   char login[MAX_LOGIN_NAME];
1161
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))
1166       count++;
1167   }
1168   return (playerFound ? count : -1);
1169 }
1170
1171 static void PositionFilePtr(FILE * fp, int count, int *last,
1172                               int *nTied, int showComp)
1173 {
1174   int i, rating, nGames, is_computer;
1175   char login[MAX_LOGIN_NAME];
1176   char line[MAX_RANK_LINE];
1177
1178   if (fp == NULL)
1179     return;
1180   rewind(fp);
1181   for (i = 1; i < count; i++) {
1182     do {
1183       fgets(line, MAX_RANK_LINE - 1, fp);
1184       if (feof(fp))
1185         break;
1186       sscanf(line, "%s %d %d %d", login, &rating, &nGames, &is_computer);
1187     } while (!CountRankLine(showComp, login, nGames, is_computer));
1188     if (rating != *last) {
1189       *nTied = 1;
1190       *last = rating;
1191     } else
1192       (*nTied)++;
1193   }
1194 }
1195
1196 static int ShowRankEntry(int p, FILE * fp, int count, int comp,
1197                            char *target, int *lastRating, int *nTied)
1198 {
1199   char newLine[MAX_RANK_LINE];
1200   char login[MAX_LOGIN_NAME];
1201   int rating, findable, nGames, is_comp;
1202
1203   findable = (count > 0) && !feof(fp);
1204   if (findable) {
1205     do {
1206       fgets(newLine, MAX_RANK_LINE - 1, fp);
1207       if (feof(fp))
1208         findable = 0;
1209       else if (newLine[0] != '\0')
1210         sscanf(newLine, "%s %d %d %d",
1211                login, &rating, &nGames, &is_comp);
1212       else
1213         login[0] = '\0';
1214     } while (!CountRankLine(comp, login, nGames, is_comp) && findable
1215              && strcasecmp(login, target));
1216   }
1217   if (findable) {
1218     if (!strcasecmp(login, target)
1219         && !CountRankLine(comp, login, nGames, is_comp)) {
1220       pprintf_highlight(p, "----  %-12.12s %4s", login, ratstr(rating));
1221       pprintf(p, "  ");
1222       return 0;
1223     } else if (*lastRating == rating && *nTied < 1) {
1224       pprintf(p, "      ");
1225       if (!strcasecmp(login, target))
1226         pprintf_highlight(p, "%-12.12s %4s", login, ratstr(rating));
1227       else
1228         pprintf(p, "%-12.12s %4s", login, ratstr(rating));
1229       pprintf(p, "  ");
1230       return 1;
1231     } else {
1232       if (*nTied >= 1) {
1233         if (*lastRating == rating)
1234           count -= *nTied;
1235         *nTied = -1;
1236       }
1237       if (!strcasecmp(login, target))
1238         pprintf_highlight(p, "%4d. %-12.12s %4s",
1239                           count, login, ratstr(rating));
1240       else
1241         pprintf(p, "%4d. %-12.12s %4s",
1242                 count, login, ratstr(rating));
1243       pprintf(p, "  ");
1244       *lastRating = rating;
1245       return 1;
1246     }
1247   } else {
1248     pprintf(p, "%25s", "");
1249     return 1;
1250   }
1251 }
1252
1253 static int CountAbove(int num, int blitz, int std, int wild, int which)
1254 {
1255   int max = blitz;
1256
1257   if (max < std)
1258     max = std;
1259   if (max < wild)
1260     max = wild;
1261   return (max <= (num + 1) / 2 ? max - 1 : (num + 1) / 2);
1262 }
1263
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)
1266 {
1267   int lastBlitz = 9999, nTiedBlitz = 0;
1268   int lastStd = 9999, nTiedStd = 0;
1269   int lastWild = 9999, nTiedWild = 0;
1270   int i;
1271
1272   if (n <= 0)
1273     return 0;
1274   if (CheckFlag(show, SHOW_BLITZ)) {
1275     PositionFilePtr(fb, bCount, &lastBlitz, &nTiedBlitz, showComp);
1276     if (feof(fb))
1277       FlagOFF(show, SHOW_BLITZ);
1278   }
1279   if (CheckFlag(show, SHOW_STANDARD)) {
1280     PositionFilePtr(fs, sCount, &lastStd, &nTiedStd, showComp);
1281     if (feof(fs))
1282       FlagOFF(show, SHOW_STANDARD);
1283   }
1284   if (CheckFlag(show, SHOW_WILD)) {
1285     PositionFilePtr(fw, wCount, &lastWild, &nTiedWild, showComp);
1286     if (feof(fw))
1287       FlagOFF(show, SHOW_WILD);
1288   }
1289   if (!CheckFlag(show, SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD))
1290     return 0;
1291   DisplayRankHead(p, show);
1292
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);
1303     pprintf(p, "\n");
1304   }
1305   return 1;
1306 }
1307
1308 static int DisplayTargetRank(int p, char *target, int show, int showComp)
1309 {
1310   int numToShow = 20;
1311   int blitzRank = -1, blitzCount;
1312   int stdRank = -1, stdCount;
1313   int wildRank = -1, wildCount;
1314   int numAbove;
1315   char Path[MAX_FILENAME_SIZE];
1316   FILE *fb = NULL, *fs = NULL, *fw = NULL;
1317
1318   if (CheckFlag(show, SHOW_BLITZ)) {
1319     GetRankFileName(Path, TYPE_BLITZ);
1320     fb = (FILE *) fopen_s(Path, "r");
1321     if (fb != NULL)
1322       blitzRank = GetRank(fb, target, showComp);
1323     if (blitzRank < 0)
1324       FlagOFF(show, SHOW_BLITZ);
1325   }
1326   if (CheckFlag(show, SHOW_STANDARD)) {
1327     GetRankFileName(Path, TYPE_STAND);
1328     fs = (FILE *) fopen_s(Path, "r");
1329     if (fs != NULL)
1330       stdRank = GetRank(fs, target, showComp);
1331     if (stdRank < 0)
1332       FlagOFF(show, SHOW_STANDARD);
1333   }
1334   if (CheckFlag(show, SHOW_WILD)) {
1335     GetRankFileName(Path, TYPE_WILD);
1336     if (CheckFlag(show, SHOW_WILD))
1337       fw = (FILE *) fopen_s(Path, "r");
1338     if (fw != NULL)
1339       wildRank = GetRank(fw, target, showComp);
1340     if (wildRank < 0)
1341       FlagOFF(show, SHOW_WILD);
1342   }
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);
1348     return (0);
1349   }
1350   numAbove = CountAbove(numToShow, blitzRank, stdRank, wildRank, show);
1351   blitzCount = blitzRank - numAbove;
1352   stdCount = stdRank - numAbove;
1353   wildCount = wildRank - numAbove;
1354
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);
1360   return (1);
1361 }
1362
1363 static int DisplayRankedPlayers(int p, int start, int end,
1364                                  int show, int showComp)
1365 {
1366   int num = end - start + 1;
1367   FILE *fb = NULL, *fs = NULL, *fw = NULL;
1368   char Path[MAX_FILENAME_SIZE];
1369
1370   if (start <= 0)
1371     start = 1;
1372   if (num <= 0)
1373     return 0;
1374   if (num > 100)
1375     num = 100;
1376   if (CheckFlag(show, SHOW_BLITZ)) {
1377     GetRankFileName(Path, TYPE_BLITZ);
1378     fb = (FILE *) fopen_s(Path, "r");
1379     if (fb == NULL)
1380       FlagOFF(show, SHOW_BLITZ);
1381   }
1382   if (CheckFlag(show, SHOW_STANDARD)) {
1383     GetRankFileName(Path, TYPE_STAND);
1384     fs = (FILE *) fopen_s(Path, "r");
1385     if (fs == NULL)
1386       FlagOFF(show, SHOW_STANDARD);
1387   }
1388   if (CheckFlag(show, SHOW_WILD)) {
1389     GetRankFileName(Path, TYPE_WILD);
1390     fw = (FILE *) fopen_s(Path, "r");
1391     if (fw == NULL)
1392       FlagOFF(show, SHOW_WILD);
1393   }
1394   ShowRankLines(p, fb, fs, fw, start, start, start,
1395                 num, showComp, show, "");
1396   if (fb)
1397     fclose(fb);
1398   if (fs)
1399     fclose(fs);
1400   if (fw)
1401     fclose(fw);
1402   return 1;
1403 }
1404
1405 static int ShowFromString(char *s)
1406 {
1407   int i, len = strlen(s);
1408   int show = 0;
1409
1410   if (s == NULL || s[0] == '\0')
1411     return SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
1412   for (i = 0; i < len; i++) {
1413     switch (s[i]) {
1414     case 'b':
1415       FlagON(show, SHOW_BLITZ);
1416       break;
1417     case 's':
1418       FlagON(show, SHOW_STANDARD);
1419       break;
1420     case 'w':
1421       FlagON(show, SHOW_WILD);
1422       break;
1423     }
1424   }
1425   return (show);
1426 }
1427
1428 static int Best(int p, param_list param, int ShowComp)
1429 {
1430   int show = SHOW_BLITZ | SHOW_STANDARD | SHOW_WILD;
1431
1432   if (param[0].type != TYPE_NULL)
1433     show = ShowFromString(param[0].val.word);
1434
1435   DisplayRankedPlayers(p, 1, 20, show, ShowComp);
1436   return COM_OK;
1437 }
1438