home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / games / volume14 / okbridge2 / part10 / oktally.c < prev    next >
C/C++ Source or Header  |  1993-01-27  |  16KB  |  583 lines

  1. /* oktally.c
  2.  *
  3.  ! Copyright (C) 1990-1992 by Matthew Clegg.  All Rights Reserved
  4.  ! 
  5.  ! OKbridge is made available as a free service to the Internet.
  6.  ! Accordingly, the following restrictions are placed on its use:
  7.  ! 
  8.  ! 1.  OKbridge may not be modified in any way without the explicit 
  9.  !     permission of Matthew Clegg.  
  10.  ! 
  11.  ! 2.  OKbridge may not be used in any way for commercial advantage.
  12.  !     It may not be placed on for-profit networks or on for-profit
  13.  !     computer systems.  It may not be bundled as part of a package
  14.  !     or service provided by a for-profit organization.
  15.  ! 
  16.  ! If you have questions about restrictions on the use of OKbridge,
  17.  ! write to mclegg@cs.ucsd.edu.
  18.  ! 
  19.  ! DISCLAIMER:  The user of OKbridge accepts full responsibility for any
  20.  ! damage which may be caused by OKbridge.
  21.  *
  22.  * This program merges email duplicate files, totaling the match
  23.  * points for corresponding pairs in each file, generating an output
  24.  * describing the results for each board.
  25.  *
  26.  * Usage:
  27.  *
  28.  *   tally [-c] [-i] [-l] [-m] [-p player_name] [-sn] [-t[b]] [-z] 
  29.  *     file_1 file_2 ... file_n
  30.  *
  31.  * Reads each of file_1, file_2, ..., file_n.  Merges the boards
  32.  * from these files and totals the match points for each pair.
  33.  * Writes the merged set of boards to standard output.  
  34.  *
  35.  * Parameters:
  36.  *
  37.  *   -c
  38.  *    Coded output.  Writes an encoded email duplicate file as output.
  39.  *
  40.  *   -i
  41.  *    IMP scoring. Sorts the output play records by IMPs.  The default is 
  42.  *    to sort by match points.
  43.  *
  44.  *   -l
  45.  *    Long output.  Writes each board in a format which is similar to 
  46.  *    that used for presentation in bridge books and magazines.
  47.  *
  48.  *   -m
  49.  *    Merge.  Does a selective merge.  Boards appearing in the second files
  50.  *    which do not appear in the file are ignored.
  51.  *
  52.  *   -p player_name
  53.  *    specifies that only boards which have been played by the specified
  54.  *    player should be considered.
  55.  *
  56.  *   -sn
  57.  *    Skip count.  Specifies a skip count.  The first n boards read into 
  58.  *    memory are ignored.
  59.  *
  60.  *   -t
  61.  *    Totals.  Computes the totals for each player and prints out the
  62.  *    list of totals, sorted either by match points or IMPs.  If -tb
  63.  *    is specified, then a two-column output is generated, the first
  64.  *    column given the match point score totals and the second giving
  65.  *    the match point score totals.
  66.  *
  67.  *   -z
  68.  *    Zhang format.  Writes each board out in a compact format which was 
  69.  *    first introduced by Shangyou Zhang.
  70.  */
  71.  
  72. #include <stdio.h>
  73. #include <string.h>
  74.  
  75. #define _BRIDGE_
  76.  
  77. #include "types.h"
  78. #include "boards.h"
  79. #include "log.h"
  80.  
  81. #ifdef GCC
  82. extern fprintf (), fclose ();
  83. #endif
  84.  
  85. extern int errno;
  86. extern char *sys_errlist [];
  87. extern char *malloc ();
  88. extern void exit ();
  89. extern int  atoi ();
  90. extern char *strdup ();
  91.  
  92. char **filename;
  93.   /* The list of files from which we are to read email duplicate hands. */
  94.  
  95. int no_files;
  96.   /* The number of entries in the filename array. */
  97.  
  98. int coded_format;
  99.   /* A boolean variable which is true if the output should be written
  100.      in coded format. */
  101.  
  102. char *player_name_of_interest = NULL;
  103.   /* If non-NULL, then we only consider boards which have been played by
  104.      the given player. */
  105.  
  106. #define OUTPUT_SUMMARY    0
  107. #define OUTPUT_EMAIL      1
  108. #define OUTPUT_LOG        2
  109. #define OUTPUT_ZLOG       3
  110. #define OUTPUT_TOTALS     4
  111.  
  112. int output_mode = OUTPUT_SUMMARY;
  113.   /* Defines the output mode which will be used:
  114.      SUMMARY:  Default.  For each board, prints out a listing of how
  115.                each table bid the board and the score which was made.
  116.      EMAIL:    Writes the set boards out in email duplicate format.
  117.      LOG:      Writes each board and play record out in a detailed format
  118.                which shows the bidding and the playing.
  119.      ZLOG:     Writes the boards out in a compact format developed by
  120.                Shangyou Zhang.
  121.      TOTALS:   Writes the total score for each player.
  122.   */
  123.  
  124. int sort_by_matchpoints = 1;
  125.   /* A boolean flag which if true indicates that we will sort the play 
  126.      records by matchpoints.  Otherwise, we will sort them by IMPs. */
  127.  
  128. int two_column_output = 0;
  129.   /* If true, indicates that the output will consist of two columns
  130.      of score totals. */
  131.  
  132. int merge_only = 0;
  133.   /* Indicates that boards read from the second and succeeding files will
  134.      be merged into those already read. */
  135.  
  136. /* The following structure is used for maintaining the array of player
  137.    scores. 
  138. */
  139. typedef struct Player_score_struct {
  140.   char  *player_name;
  141.   float score;          /* In MP or IMP scoring, the total of the individual
  142.                scores received by the player. */
  143.   float total_score;    /* In MP scoring, the total number of match points
  144.                received by the player. */
  145.   float total_possible; /* In MP scoring, the total number of match points
  146.                which were available to the player. */
  147.   int   total_boards;   /* The total number of boards played by this player.*/
  148. } Player_score;
  149.  
  150.  
  151.  
  152. void Abort (m)
  153.      char *m;
  154. {
  155.   fprintf (stderr, "tally: %s\n", m);
  156.   exit (1);
  157. }
  158.  
  159. static void Read_File (filename)
  160.      char *filename;
  161. {
  162.   FILE *fp;
  163.   int status;
  164.   char error_buf[80];
  165.  
  166.   if (strcmp(filename, "-")) {
  167.     fp = fopen (filename, "r");
  168.     if (fp == NULL) {
  169.       sprintf (error_buf, "Error opening file %s: %s", filename,
  170.            sys_errlist[errno]);
  171.       Abort (error_buf);
  172.     }
  173.   } else
  174.     fp = stdin;
  175.  
  176.   status = Load_Email_Duplicate_File (fp);
  177.   if (status == 1) {
  178.     sprintf (error_buf, "%s is not an email duplicate file", filename);
  179.     Abort (error_buf);
  180.   } else if (status == -1) {
  181.     sprintf (error_buf, "Error reading file %s", filename);
  182.     Abort (error_buf);
  183.   }
  184.  
  185.   if (strcmp(filename, "-"))
  186.     fclose (fp);
  187. }
  188.  
  189. static void Merge_File (filename)
  190.      char *filename;
  191. {
  192.   FILE *fp;
  193.   int status;
  194.   char error_buf[80];
  195.  
  196.   if (strcmp(filename, "-")) {
  197.     fp = fopen (filename, "r");
  198.     if (fp == NULL) {
  199.       sprintf (error_buf, "Error opening file %s: %s", filename,
  200.            sys_errlist[errno]);
  201.       Abort (error_buf);
  202.     }
  203.   } else
  204.     fp = stdin;
  205.  
  206.   status = Merge_Email_Duplicate_File (fp, Unplayed_boards);
  207.   if (status == 1) {
  208.     sprintf (error_buf, "%s is not an email duplicate file", filename);
  209.     Abort (error_buf);
  210.   } else if (status == -1) {
  211.     sprintf (error_buf, "Error reading file %s", filename);
  212.     Abort (error_buf);
  213.   }
  214.  
  215.   if (strcmp(filename, "-"))
  216.     fclose (fp);
  217. }
  218.  
  219. int Count_Total_Players (blist)
  220.      Board *blist;
  221. {
  222.   Play_record *p;
  223.   int n;
  224.  
  225.   n = 0;
  226.   for (; blist != NULL; blist = blist->next)
  227.     for (p = blist->play_records; p != NULL; p = p->next)
  228.       n += 4;
  229.  
  230.   return (n);
  231. }
  232.  
  233. int Compare_Score_List_Entries (p, q)
  234.      Player_score *p, *q;
  235. {
  236.   if (p->score == q->score)
  237.     return (strcasecmp(p->player_name, q->player_name));
  238.   else if (p->score > q->score)
  239.     return (-1);
  240.   else
  241.     return (1);
  242. }
  243.  
  244. int Compare_Score_List_Names (p, q)
  245.      Player_score *p, *q;
  246. {
  247.   return(strcasecmp(p->player_name, q->player_name));
  248. }
  249.  
  250. int Sort_and_Merge_Scores (scores, entries)
  251.      Player_score *scores; int entries;
  252. {
  253.   int i, n;
  254.  
  255.   if (entries == 0)
  256.     return (0);
  257.  
  258.   qsort (scores, entries, sizeof(Player_score), Compare_Score_List_Names);
  259.   
  260.   n = 0;
  261.   for (i = 1; i < entries; i++)
  262.     if (strcasecmp(scores[n].player_name, scores[i].player_name)) {
  263.       n += 1;
  264.       bcopy (scores+i, scores+n, sizeof(Player_score));
  265.     } else {
  266.       scores[n].score += scores[i].score;
  267.       scores[n].total_score += scores[i].total_score;
  268.       scores[n].total_possible += scores[i].total_possible;
  269.       scores[n].total_boards += scores[i].total_boards;
  270.     }
  271.  
  272.   qsort (scores, n+1, sizeof(Player_score), Compare_Score_List_Entries);
  273.   return (n+1);
  274. }
  275.  
  276. static int Tables_Played (b)
  277.      Board *b;
  278. /* Returns the number of tables which have played the board b. */
  279. {
  280.   int i = 0;
  281.   Play_record *p;
  282.  
  283.   for (p = b->play_records; p != NULL; p = p->next) 
  284.     if (p->hand_completed) i++;
  285.  
  286.   return (i);
  287. }
  288.  
  289. void Create_Matchpoint_List (blist, scores, no_scores)
  290.      Board *blist; Player_score **scores; int *no_scores;
  291. {
  292.   int i, ns;
  293.   int no_players = Count_Total_Players (blist);
  294.   Player_score *score_list;
  295.   Board *b;
  296.   Play_record *p;
  297.  
  298.   score_list = *scores = (Player_score *)
  299.     malloc (no_players * sizeof(Player_score));
  300.  
  301.   ns = 0;
  302.   for (b = blist; b != NULL; b = b->next)
  303.     if ((b->scoring_mode == MP_SCORING) && (Tables_Played(b) >= 4))
  304.       for (p = b->play_records; p != NULL; p = p->next)
  305.     for (i = 0; i < 4; i++) {
  306.       score_list[ns].player_name = p->player_names[i];
  307.       score_list[ns].score = p->match_points[side_of(i)];
  308.       score_list[ns].total_possible = (float) (Tables_Played(b) - 1);
  309.       score_list[ns].total_score = 
  310.         p->match_points[side_of(i)] * score_list[ns].total_possible;
  311.       score_list[ns].total_boards = 1;
  312.       ns += 1;
  313.     }
  314.  
  315.   *no_scores = Sort_and_Merge_Scores (score_list, ns);
  316. }
  317.  
  318. void Create_IMP_List (blist, scores, no_scores)
  319.      Board *blist; Player_score **scores; int *no_scores;
  320. {
  321.   int i, ns;
  322.   int no_players = Count_Total_Players (blist);
  323.   Player_score *score_list;
  324.   Board *b;
  325.   Play_record *p;
  326.  
  327.   score_list = *scores = (Player_score *)
  328.     malloc (no_players * sizeof(Player_score));
  329.  
  330.   ns = 0;
  331.   for (b = blist; b != NULL; b = b->next)
  332.     if ((b->scoring_mode == IMP_SCORING) && (Tables_Played(b) >= 4))
  333.       for (p = b->play_records; p != NULL; p = p->next)
  334.     for (i = 0; i < 4; i++) {
  335.       score_list[ns].player_name = p->player_names[i];
  336.       score_list[ns].score = p->imatch_points[side_of(i)];
  337.       score_list[ns].total_score = score_list[ns].score;
  338.       score_list[ns].total_possible = 0.0;
  339.       score_list[ns].total_boards = 1;
  340.       ns += 1;
  341.     }
  342.  
  343.   *no_scores = Sort_and_Merge_Scores (score_list, ns);
  344. }
  345.  
  346. void Write_Match_Point_Totals (blist)
  347.      Board *blist;
  348. {
  349.   Player_score *matchpoint_list;
  350.   int no_matchpoint_scores;
  351.   int i;
  352.  
  353.   Create_Matchpoint_List (blist, &matchpoint_list, &no_matchpoint_scores);
  354.  
  355.   printf ("%4s %-10s %12s %8s %8s\n", " ", "Name", "Match Points", "Percent",
  356.       "Boards");
  357.  
  358.   for (i = 0; i < no_matchpoint_scores; i++)
  359.     printf ("%4d %-10s %12.2f %8.1f %8d\n", 
  360.         i+1,
  361.         matchpoint_list[i].player_name,
  362.         matchpoint_list[i].score,
  363.         100.0 * matchpoint_list[i].total_score /
  364.           matchpoint_list[i].total_possible,
  365.         matchpoint_list[i].total_boards
  366.         );
  367. }
  368.  
  369. void Write_IMP_Totals (blist)
  370.      Board *blist;
  371. {
  372.   Player_score *IMP_list;
  373.   int no_imp_scores;
  374.   int i;
  375.  
  376.   Create_IMP_List (blist, &IMP_list, &no_imp_scores);
  377.  
  378.   printf ("%4s %-10s %8s %8s %8s\n",
  379.       " ", "Name", "IMPs", "Average", "Boards");
  380.  
  381.   for (i = 0; i < no_imp_scores; i++)
  382.     printf ("%4d %-10s %8.1f %8.1f %8d\n",
  383.         i+1, 
  384.         IMP_list[i].player_name,
  385.         IMP_list[i].score,
  386.         IMP_list[i].total_score / ((float) IMP_list[i].total_boards),
  387.         IMP_list[i].total_boards);
  388. }
  389.  
  390. void Write_Totals_in_Two_Columns (blist)
  391.      Board *blist;
  392. {
  393.   Player_score *matchpoint_list, *IMP_list;
  394.   int no_matchpoint_scores, no_imp_scores, max_scores;
  395.   int i;
  396.  
  397.   Create_Matchpoint_List (blist, &matchpoint_list, &no_matchpoint_scores);
  398.   Create_IMP_List (blist, &IMP_list, &no_imp_scores);
  399.  
  400.   printf ("%4s %-10s %12s %4s %5s %-10s %12s %4s\n", 
  401.       " ", "Name", "Match Points", "Pct", " ", "Name", "IMPs", "Avg");
  402.  
  403.   if (no_matchpoint_scores > no_imp_scores)
  404.     max_scores = no_matchpoint_scores;
  405.   else
  406.     max_scores = no_imp_scores;
  407.  
  408.   for (i = 0; i < max_scores; i++)
  409.     if ((i < no_matchpoint_scores) && (i < no_imp_scores))
  410.       printf ("%4d %-10s %12.2f %4d %5s %-10s %12.1f %4.1f\n", 
  411.           i+1, 
  412.           matchpoint_list[i].player_name,
  413.           matchpoint_list[i].score, 
  414.           (int) (100.0 * matchpoint_list[i].total_score /
  415.           matchpoint_list[i].total_possible + 0.5),
  416.           " ",
  417.           IMP_list[i].player_name,
  418.           IMP_list[i].score,
  419.           IMP_list[i].total_score / ((float) IMP_list[i].total_boards)
  420.           );
  421.  
  422.     else if (i < no_matchpoint_scores)
  423.       printf ("%4d %-10s %12.2f %4d\n",
  424.           i+1, 
  425.           matchpoint_list[i].player_name,
  426.           matchpoint_list[i].score,
  427.           (int) (100.0 * matchpoint_list[i].total_score /
  428.           matchpoint_list[i].total_possible + 0.5));
  429.     else
  430.       printf ("%4d %-10s %12s %10s %-10s %12.1f %4.1f\n", 
  431.           i+1, " ", " ", " ",
  432.           IMP_list[i].player_name,
  433.           IMP_list[i].score,
  434.           IMP_list[i].total_score / ((float) IMP_list[i].total_boards)
  435.           );
  436.           
  437. }
  438.  
  439. int Has_Played (b, n)
  440.      Board *b; char *n;
  441. /* Returns true if the board b has been played by n. */
  442. {
  443.   Play_record *p;
  444.   int i;
  445.  
  446.   for (p = b->play_records; p != NULL; p = p->next)
  447.     for (i = 0; i < 4; i++)
  448.       if (!strcasecmp(p->player_names[i], n))
  449.     return (1);
  450.  
  451.   return (0);
  452. }
  453.  
  454. void main (argc, argv)
  455.      int argc; char *argv[];
  456. {
  457.   int i, skip_count;
  458.   char error_buf [80];
  459.   Board *b;
  460.   Play_record *p;
  461.   Board *output_list, *output_list_tail;
  462.  
  463.   no_files = 0;
  464.   skip_count = 0;
  465.   filename = (char **) malloc ((argc - 1) * sizeof(char *));
  466.   for (i = 1; i < argc; i++) {
  467.     if (*argv[i] == '-') {
  468.       if(argv[i][1] == '\0')
  469.     filename[no_files++] = argv[i];
  470.       else if (argv[i][1] == 's') {
  471.     skip_count = atoi (argv[i]+2);
  472.     if (skip_count == 0) {
  473.       sprintf (error_buf, "Error in skip count: %s", argv[i]+2);
  474.       Abort (error_buf);
  475.     }
  476.       } else if (!strcmp(argv[i], "-c"))
  477.     output_mode = OUTPUT_EMAIL;
  478.       else if (!strcmp(argv[i], "-i"))
  479.     sort_by_matchpoints = 0;
  480.       else if (!strcmp(argv[i], "-l"))
  481.     output_mode = OUTPUT_LOG;
  482.       else if (!strcmp(argv[i], "-m"))
  483.     merge_only = 1;
  484.       else if (!strcmp(argv[i], "-p")) {
  485.     if (++i == argc)
  486.       Abort ("Player name must follow -p parameter");
  487.     player_name_of_interest = strdup(argv[i]);
  488.       } else if (!strcmp(argv[i], "-t"))
  489.     output_mode = OUTPUT_TOTALS;
  490.       else if (!strcmp(argv[i], "-tb")) {
  491.     output_mode = OUTPUT_TOTALS;
  492.     two_column_output = 1;
  493.       } else if (!strcmp(argv[i], "-z"))
  494.     output_mode = OUTPUT_ZLOG;
  495.       else if (!strcmp(argv[i], "-S"))
  496.     output_mode = OUTPUT_SUMMARY;
  497.       else
  498.     Abort ("Error in parameters");
  499.     } else
  500.       filename[no_files++] = argv[i];
  501.   }
  502.  
  503.   if (no_files < 1)
  504.     Abort ("At least one email file name must be given.");
  505.  
  506.   Read_File (filename[0]);
  507.  
  508.   for (i = 1; i < no_files; i++) {
  509.     if (merge_only)
  510.       Merge_File (filename[i]);
  511.     else
  512.       Read_File (filename[i]);
  513.   }
  514.  
  515.   for (i = 0; i < skip_count; i++)
  516.     b = Next_Unplayed_Board ();
  517.  
  518.   if (Unplayed_boards != NULL) {
  519.     output_list = output_list_tail = Unplayed_boards;
  520.     Unplayed_boards = output_list->next;
  521.   } else
  522.     output_list = output_list_tail = NULL;
  523.  
  524.   while (Unplayed_boards != NULL) {
  525.     if ((player_name_of_interest == NULL) || 
  526.     Has_Played (Unplayed_boards, player_name_of_interest)) {
  527.       output_list_tail->next = Unplayed_boards;
  528.       output_list_tail = output_list_tail->next;
  529.     }
  530.     Unplayed_boards = Unplayed_boards->next;
  531.   }
  532.   if (output_list != NULL)
  533.     output_list_tail->next = NULL;
  534.   else
  535.     Abort ("There are no boards on the output list.");
  536.  
  537.   for (b = output_list; b != NULL; b = b->next) {
  538.     for (p = b->play_records; p != NULL; p = p->next) {
  539.       Compute_contract (b, p);
  540.       Compute_MIMP_points (b, p);
  541.     }
  542.     Compute_Matchpoints (b);
  543.     Compute_Intl_Matchpoints (b);
  544.     if (sort_by_matchpoints)
  545.       Sort_play_records_by_matchpoints (b);
  546.     else
  547.       Sort_play_records_by_imps (b);
  548.   }
  549.  
  550.   switch (output_mode) {
  551.   case OUTPUT_SUMMARY:
  552.     for (b = output_list; b != NULL; b = b->next)
  553.       if (b->play_records != NULL)
  554.     Write_summary_of_play (stdout, b);
  555.     break;
  556.  
  557.   case OUTPUT_EMAIL:
  558.     Played_boards = output_list;
  559.     Write_Email_Duplicate_File (stdout);
  560.     break;
  561.  
  562.   case OUTPUT_LOG:
  563.     for (b = output_list; b != NULL; b = b->next)
  564.       for (p = b->play_records; p != NULL; p = p->next)
  565.     Write_hand (stdout, b, p);
  566.     break;
  567.  
  568.   case OUTPUT_ZLOG:
  569.     for (b = output_list; b != NULL; b = b->next)
  570.       for (p = b->play_records; p != NULL; p = p->next)
  571.     Write_hand_compactly (stdout, 1, b, p);
  572.     break;
  573.  
  574.   case OUTPUT_TOTALS:
  575.     if (two_column_output)
  576.       Write_Totals_in_Two_Columns (output_list);
  577.     else if (sort_by_matchpoints)
  578.       Write_Match_Point_Totals (output_list);
  579.     else 
  580.       Write_IMP_Totals (output_list);
  581.   }
  582. }
  583.