home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
games
/
volume14
/
okbridge2
/
part10
/
oktally.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-27
|
16KB
|
583 lines
/* oktally.c
*
! Copyright (C) 1990-1992 by Matthew Clegg. All Rights Reserved
!
! OKbridge is made available as a free service to the Internet.
! Accordingly, the following restrictions are placed on its use:
!
! 1. OKbridge may not be modified in any way without the explicit
! permission of Matthew Clegg.
!
! 2. OKbridge may not be used in any way for commercial advantage.
! It may not be placed on for-profit networks or on for-profit
! computer systems. It may not be bundled as part of a package
! or service provided by a for-profit organization.
!
! If you have questions about restrictions on the use of OKbridge,
! write to mclegg@cs.ucsd.edu.
!
! DISCLAIMER: The user of OKbridge accepts full responsibility for any
! damage which may be caused by OKbridge.
*
* This program merges email duplicate files, totaling the match
* points for corresponding pairs in each file, generating an output
* describing the results for each board.
*
* Usage:
*
* tally [-c] [-i] [-l] [-m] [-p player_name] [-sn] [-t[b]] [-z]
* file_1 file_2 ... file_n
*
* Reads each of file_1, file_2, ..., file_n. Merges the boards
* from these files and totals the match points for each pair.
* Writes the merged set of boards to standard output.
*
* Parameters:
*
* -c
* Coded output. Writes an encoded email duplicate file as output.
*
* -i
* IMP scoring. Sorts the output play records by IMPs. The default is
* to sort by match points.
*
* -l
* Long output. Writes each board in a format which is similar to
* that used for presentation in bridge books and magazines.
*
* -m
* Merge. Does a selective merge. Boards appearing in the second files
* which do not appear in the file are ignored.
*
* -p player_name
* specifies that only boards which have been played by the specified
* player should be considered.
*
* -sn
* Skip count. Specifies a skip count. The first n boards read into
* memory are ignored.
*
* -t
* Totals. Computes the totals for each player and prints out the
* list of totals, sorted either by match points or IMPs. If -tb
* is specified, then a two-column output is generated, the first
* column given the match point score totals and the second giving
* the match point score totals.
*
* -z
* Zhang format. Writes each board out in a compact format which was
* first introduced by Shangyou Zhang.
*/
#include <stdio.h>
#include <string.h>
#define _BRIDGE_
#include "types.h"
#include "boards.h"
#include "log.h"
#ifdef GCC
extern fprintf (), fclose ();
#endif
extern int errno;
extern char *sys_errlist [];
extern char *malloc ();
extern void exit ();
extern int atoi ();
extern char *strdup ();
char **filename;
/* The list of files from which we are to read email duplicate hands. */
int no_files;
/* The number of entries in the filename array. */
int coded_format;
/* A boolean variable which is true if the output should be written
in coded format. */
char *player_name_of_interest = NULL;
/* If non-NULL, then we only consider boards which have been played by
the given player. */
#define OUTPUT_SUMMARY 0
#define OUTPUT_EMAIL 1
#define OUTPUT_LOG 2
#define OUTPUT_ZLOG 3
#define OUTPUT_TOTALS 4
int output_mode = OUTPUT_SUMMARY;
/* Defines the output mode which will be used:
SUMMARY: Default. For each board, prints out a listing of how
each table bid the board and the score which was made.
EMAIL: Writes the set boards out in email duplicate format.
LOG: Writes each board and play record out in a detailed format
which shows the bidding and the playing.
ZLOG: Writes the boards out in a compact format developed by
Shangyou Zhang.
TOTALS: Writes the total score for each player.
*/
int sort_by_matchpoints = 1;
/* A boolean flag which if true indicates that we will sort the play
records by matchpoints. Otherwise, we will sort them by IMPs. */
int two_column_output = 0;
/* If true, indicates that the output will consist of two columns
of score totals. */
int merge_only = 0;
/* Indicates that boards read from the second and succeeding files will
be merged into those already read. */
/* The following structure is used for maintaining the array of player
scores.
*/
typedef struct Player_score_struct {
char *player_name;
float score; /* In MP or IMP scoring, the total of the individual
scores received by the player. */
float total_score; /* In MP scoring, the total number of match points
received by the player. */
float total_possible; /* In MP scoring, the total number of match points
which were available to the player. */
int total_boards; /* The total number of boards played by this player.*/
} Player_score;
void Abort (m)
char *m;
{
fprintf (stderr, "tally: %s\n", m);
exit (1);
}
static void Read_File (filename)
char *filename;
{
FILE *fp;
int status;
char error_buf[80];
if (strcmp(filename, "-")) {
fp = fopen (filename, "r");
if (fp == NULL) {
sprintf (error_buf, "Error opening file %s: %s", filename,
sys_errlist[errno]);
Abort (error_buf);
}
} else
fp = stdin;
status = Load_Email_Duplicate_File (fp);
if (status == 1) {
sprintf (error_buf, "%s is not an email duplicate file", filename);
Abort (error_buf);
} else if (status == -1) {
sprintf (error_buf, "Error reading file %s", filename);
Abort (error_buf);
}
if (strcmp(filename, "-"))
fclose (fp);
}
static void Merge_File (filename)
char *filename;
{
FILE *fp;
int status;
char error_buf[80];
if (strcmp(filename, "-")) {
fp = fopen (filename, "r");
if (fp == NULL) {
sprintf (error_buf, "Error opening file %s: %s", filename,
sys_errlist[errno]);
Abort (error_buf);
}
} else
fp = stdin;
status = Merge_Email_Duplicate_File (fp, Unplayed_boards);
if (status == 1) {
sprintf (error_buf, "%s is not an email duplicate file", filename);
Abort (error_buf);
} else if (status == -1) {
sprintf (error_buf, "Error reading file %s", filename);
Abort (error_buf);
}
if (strcmp(filename, "-"))
fclose (fp);
}
int Count_Total_Players (blist)
Board *blist;
{
Play_record *p;
int n;
n = 0;
for (; blist != NULL; blist = blist->next)
for (p = blist->play_records; p != NULL; p = p->next)
n += 4;
return (n);
}
int Compare_Score_List_Entries (p, q)
Player_score *p, *q;
{
if (p->score == q->score)
return (strcasecmp(p->player_name, q->player_name));
else if (p->score > q->score)
return (-1);
else
return (1);
}
int Compare_Score_List_Names (p, q)
Player_score *p, *q;
{
return(strcasecmp(p->player_name, q->player_name));
}
int Sort_and_Merge_Scores (scores, entries)
Player_score *scores; int entries;
{
int i, n;
if (entries == 0)
return (0);
qsort (scores, entries, sizeof(Player_score), Compare_Score_List_Names);
n = 0;
for (i = 1; i < entries; i++)
if (strcasecmp(scores[n].player_name, scores[i].player_name)) {
n += 1;
bcopy (scores+i, scores+n, sizeof(Player_score));
} else {
scores[n].score += scores[i].score;
scores[n].total_score += scores[i].total_score;
scores[n].total_possible += scores[i].total_possible;
scores[n].total_boards += scores[i].total_boards;
}
qsort (scores, n+1, sizeof(Player_score), Compare_Score_List_Entries);
return (n+1);
}
static int Tables_Played (b)
Board *b;
/* Returns the number of tables which have played the board b. */
{
int i = 0;
Play_record *p;
for (p = b->play_records; p != NULL; p = p->next)
if (p->hand_completed) i++;
return (i);
}
void Create_Matchpoint_List (blist, scores, no_scores)
Board *blist; Player_score **scores; int *no_scores;
{
int i, ns;
int no_players = Count_Total_Players (blist);
Player_score *score_list;
Board *b;
Play_record *p;
score_list = *scores = (Player_score *)
malloc (no_players * sizeof(Player_score));
ns = 0;
for (b = blist; b != NULL; b = b->next)
if ((b->scoring_mode == MP_SCORING) && (Tables_Played(b) >= 4))
for (p = b->play_records; p != NULL; p = p->next)
for (i = 0; i < 4; i++) {
score_list[ns].player_name = p->player_names[i];
score_list[ns].score = p->match_points[side_of(i)];
score_list[ns].total_possible = (float) (Tables_Played(b) - 1);
score_list[ns].total_score =
p->match_points[side_of(i)] * score_list[ns].total_possible;
score_list[ns].total_boards = 1;
ns += 1;
}
*no_scores = Sort_and_Merge_Scores (score_list, ns);
}
void Create_IMP_List (blist, scores, no_scores)
Board *blist; Player_score **scores; int *no_scores;
{
int i, ns;
int no_players = Count_Total_Players (blist);
Player_score *score_list;
Board *b;
Play_record *p;
score_list = *scores = (Player_score *)
malloc (no_players * sizeof(Player_score));
ns = 0;
for (b = blist; b != NULL; b = b->next)
if ((b->scoring_mode == IMP_SCORING) && (Tables_Played(b) >= 4))
for (p = b->play_records; p != NULL; p = p->next)
for (i = 0; i < 4; i++) {
score_list[ns].player_name = p->player_names[i];
score_list[ns].score = p->imatch_points[side_of(i)];
score_list[ns].total_score = score_list[ns].score;
score_list[ns].total_possible = 0.0;
score_list[ns].total_boards = 1;
ns += 1;
}
*no_scores = Sort_and_Merge_Scores (score_list, ns);
}
void Write_Match_Point_Totals (blist)
Board *blist;
{
Player_score *matchpoint_list;
int no_matchpoint_scores;
int i;
Create_Matchpoint_List (blist, &matchpoint_list, &no_matchpoint_scores);
printf ("%4s %-10s %12s %8s %8s\n", " ", "Name", "Match Points", "Percent",
"Boards");
for (i = 0; i < no_matchpoint_scores; i++)
printf ("%4d %-10s %12.2f %8.1f %8d\n",
i+1,
matchpoint_list[i].player_name,
matchpoint_list[i].score,
100.0 * matchpoint_list[i].total_score /
matchpoint_list[i].total_possible,
matchpoint_list[i].total_boards
);
}
void Write_IMP_Totals (blist)
Board *blist;
{
Player_score *IMP_list;
int no_imp_scores;
int i;
Create_IMP_List (blist, &IMP_list, &no_imp_scores);
printf ("%4s %-10s %8s %8s %8s\n",
" ", "Name", "IMPs", "Average", "Boards");
for (i = 0; i < no_imp_scores; i++)
printf ("%4d %-10s %8.1f %8.1f %8d\n",
i+1,
IMP_list[i].player_name,
IMP_list[i].score,
IMP_list[i].total_score / ((float) IMP_list[i].total_boards),
IMP_list[i].total_boards);
}
void Write_Totals_in_Two_Columns (blist)
Board *blist;
{
Player_score *matchpoint_list, *IMP_list;
int no_matchpoint_scores, no_imp_scores, max_scores;
int i;
Create_Matchpoint_List (blist, &matchpoint_list, &no_matchpoint_scores);
Create_IMP_List (blist, &IMP_list, &no_imp_scores);
printf ("%4s %-10s %12s %4s %5s %-10s %12s %4s\n",
" ", "Name", "Match Points", "Pct", " ", "Name", "IMPs", "Avg");
if (no_matchpoint_scores > no_imp_scores)
max_scores = no_matchpoint_scores;
else
max_scores = no_imp_scores;
for (i = 0; i < max_scores; i++)
if ((i < no_matchpoint_scores) && (i < no_imp_scores))
printf ("%4d %-10s %12.2f %4d %5s %-10s %12.1f %4.1f\n",
i+1,
matchpoint_list[i].player_name,
matchpoint_list[i].score,
(int) (100.0 * matchpoint_list[i].total_score /
matchpoint_list[i].total_possible + 0.5),
" ",
IMP_list[i].player_name,
IMP_list[i].score,
IMP_list[i].total_score / ((float) IMP_list[i].total_boards)
);
else if (i < no_matchpoint_scores)
printf ("%4d %-10s %12.2f %4d\n",
i+1,
matchpoint_list[i].player_name,
matchpoint_list[i].score,
(int) (100.0 * matchpoint_list[i].total_score /
matchpoint_list[i].total_possible + 0.5));
else
printf ("%4d %-10s %12s %10s %-10s %12.1f %4.1f\n",
i+1, " ", " ", " ",
IMP_list[i].player_name,
IMP_list[i].score,
IMP_list[i].total_score / ((float) IMP_list[i].total_boards)
);
}
int Has_Played (b, n)
Board *b; char *n;
/* Returns true if the board b has been played by n. */
{
Play_record *p;
int i;
for (p = b->play_records; p != NULL; p = p->next)
for (i = 0; i < 4; i++)
if (!strcasecmp(p->player_names[i], n))
return (1);
return (0);
}
void main (argc, argv)
int argc; char *argv[];
{
int i, skip_count;
char error_buf [80];
Board *b;
Play_record *p;
Board *output_list, *output_list_tail;
no_files = 0;
skip_count = 0;
filename = (char **) malloc ((argc - 1) * sizeof(char *));
for (i = 1; i < argc; i++) {
if (*argv[i] == '-') {
if(argv[i][1] == '\0')
filename[no_files++] = argv[i];
else if (argv[i][1] == 's') {
skip_count = atoi (argv[i]+2);
if (skip_count == 0) {
sprintf (error_buf, "Error in skip count: %s", argv[i]+2);
Abort (error_buf);
}
} else if (!strcmp(argv[i], "-c"))
output_mode = OUTPUT_EMAIL;
else if (!strcmp(argv[i], "-i"))
sort_by_matchpoints = 0;
else if (!strcmp(argv[i], "-l"))
output_mode = OUTPUT_LOG;
else if (!strcmp(argv[i], "-m"))
merge_only = 1;
else if (!strcmp(argv[i], "-p")) {
if (++i == argc)
Abort ("Player name must follow -p parameter");
player_name_of_interest = strdup(argv[i]);
} else if (!strcmp(argv[i], "-t"))
output_mode = OUTPUT_TOTALS;
else if (!strcmp(argv[i], "-tb")) {
output_mode = OUTPUT_TOTALS;
two_column_output = 1;
} else if (!strcmp(argv[i], "-z"))
output_mode = OUTPUT_ZLOG;
else if (!strcmp(argv[i], "-S"))
output_mode = OUTPUT_SUMMARY;
else
Abort ("Error in parameters");
} else
filename[no_files++] = argv[i];
}
if (no_files < 1)
Abort ("At least one email file name must be given.");
Read_File (filename[0]);
for (i = 1; i < no_files; i++) {
if (merge_only)
Merge_File (filename[i]);
else
Read_File (filename[i]);
}
for (i = 0; i < skip_count; i++)
b = Next_Unplayed_Board ();
if (Unplayed_boards != NULL) {
output_list = output_list_tail = Unplayed_boards;
Unplayed_boards = output_list->next;
} else
output_list = output_list_tail = NULL;
while (Unplayed_boards != NULL) {
if ((player_name_of_interest == NULL) ||
Has_Played (Unplayed_boards, player_name_of_interest)) {
output_list_tail->next = Unplayed_boards;
output_list_tail = output_list_tail->next;
}
Unplayed_boards = Unplayed_boards->next;
}
if (output_list != NULL)
output_list_tail->next = NULL;
else
Abort ("There are no boards on the output list.");
for (b = output_list; b != NULL; b = b->next) {
for (p = b->play_records; p != NULL; p = p->next) {
Compute_contract (b, p);
Compute_MIMP_points (b, p);
}
Compute_Matchpoints (b);
Compute_Intl_Matchpoints (b);
if (sort_by_matchpoints)
Sort_play_records_by_matchpoints (b);
else
Sort_play_records_by_imps (b);
}
switch (output_mode) {
case OUTPUT_SUMMARY:
for (b = output_list; b != NULL; b = b->next)
if (b->play_records != NULL)
Write_summary_of_play (stdout, b);
break;
case OUTPUT_EMAIL:
Played_boards = output_list;
Write_Email_Duplicate_File (stdout);
break;
case OUTPUT_LOG:
for (b = output_list; b != NULL; b = b->next)
for (p = b->play_records; p != NULL; p = p->next)
Write_hand (stdout, b, p);
break;
case OUTPUT_ZLOG:
for (b = output_list; b != NULL; b = b->next)
for (p = b->play_records; p != NULL; p = p->next)
Write_hand_compactly (stdout, 1, b, p);
break;
case OUTPUT_TOTALS:
if (two_column_output)
Write_Totals_in_Two_Columns (output_list);
else if (sort_by_matchpoints)
Write_Match_Point_Totals (output_list);
else
Write_IMP_Totals (output_list);
}
}