summaryrefslogtreecommitdiff
path: root/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'output.c')
-rw-r--r--output.c1132
1 files changed, 1132 insertions, 0 deletions
diff --git a/output.c b/output.c
new file mode 100644
index 0000000..b29bcbd
--- /dev/null
+++ b/output.c
@@ -0,0 +1,1132 @@
+#include "types.h"
+#include "bigdeal.h"
+#include "binomial.h"
+#include <stdio.h>
+#include <string.h>
+
+static char rcsid[] = "$Header: /home/sater/bridge/bigdeal/RCS/output.c,v 1.20 2000/09/08 06:01:46 sater Exp $";
+
+/*
+ * Next type is a tagged union to prevent compilers complaining
+ * Should check on tag
+ */
+typedef
+struct hand_rep {
+ int hr_baseform; /* Which baseform is this in */
+ union {
+ dl_num *hv_dnp;
+ dl_int *hv_dip;
+ dl_byh *hv_dbhp;
+ dl_byc *hv_dbcp;
+ } hr_value;
+} hr_t, *hr_p;
+
+static progparams_p prparp; /* All parameters */
+
+/*
+ * Information per output type is collected here
+ */
+typedef
+struct output_format {
+ char * of_name; /* Name of format */
+ int of_flags; /* Various flags */
+ FILE * of_file; /* Output file to write it to */
+ char * of_suffix; /* File suffix/type */
+ int of_baseform; /* Which internal format it is printed from */
+ char * of_help; /* Help description */
+ void (*of_init)(FILE *f); /* Routine to init */
+ void (*of_printit)(FILE *f, int boardno, hr_p arg); /* Routine to print it */
+ void (*of_finish)(FILE *f); /* Routine to finish */
+} of_t, *of_p;
+
+/*
+ * Values for the of_flags struct member
+ */
+#define OFF_USEIT 0x1 /* Use the format this run */
+#define OFF_BINARY 0x2 /* Output is binary, not text */
+
+static void
+cnv_int_byh(dl_int *dip, dl_byh *dbhp)
+/*
+ * Converts the internal handrepresentation from *dip to a "bri" like coding
+ * in *dbhp. This representation is as follows:
+ * The 52 cards are numbered 1: AS, 2 KS, .... , 52 2C
+ * and all thirteen slots for all four hands are filled in with one of
+ * the 52 cards.
+ *
+ * Given the way this program works the order of cards per hand will
+ * be strictly increasing.
+ */
+{
+ int nextcard[NCARDSPERDECK+1];
+ int i;
+ int card,prevcard;
+ int skip, skipcnt;
+ int compass;
+
+ /*
+ * Setup a chain of 52 cards with a linked list type structure
+ */
+ for (i=0; i<NCARDSPERDECK; i++)
+ nextcard[i] = i+1;
+ nextcard[NCARDSPERDECK] = 0;
+
+ /*
+ * Run through the hand converting skip numbers to cards
+ */
+ for (compass=COMPASS_NORTH; compass<=COMPASS_WEST; compass++) {
+ /*
+ * Get to the beginning of all leftover cards
+ */
+ prevcard = 0;
+ card = nextcard[prevcard];
+ /*
+ * Go select the thirteen cards for this hand
+ */
+ for (i=0; i<NCARDSPERHAND; i++) {
+ /*
+ * For the first card use number of skips directly
+ * for all other cards the extra skip compared to
+ * the previous card
+ */
+ if (i==0)
+ skip=dip->di_hand[compass][0];
+ else
+ skip=dip->di_hand[compass][i] -
+ dip->di_hand[compass][i-1];
+ for (skipcnt=0; skipcnt<skip; skipcnt++) {
+ prevcard = card;
+ card = nextcard[card];
+ }
+ dbhp->dh_hand[compass][i] = card;
+ /*
+ * Take card just selected out of the linked list
+ */
+ card = nextcard[card];
+ nextcard[prevcard] = card;
+ }
+ }
+}
+
+static void
+cnv_byh_byc(dl_byh *dbhp, dl_byc *dbcp)
+/*
+ * Convert from a "bri" like coding to a cardposition type coding.
+ * Representation is an array of 52 cards, from AS to 2C,
+ * with as a value the compass position, 0 for N, 3 for W
+ *
+ * Note the -1 to convert from 1..52 cardnotation to 0..51 array index
+ */
+{
+ int compass, card;
+
+ for (compass = COMPASS_NORTH; compass <= COMPASS_WEST; compass++)
+ for (card = 0; card < NCARDSPERHAND; card++)
+ dbcp->dc_card[dbhp->dh_hand[compass][card] - 1] =
+ compass;
+}
+
+static void
+out_fill(FILE *out, char filler, int count)
+/*
+ * Write filler character, count times
+ */
+{
+ int i;
+
+ for(i=0; i<count; i++)
+ putc(filler, out);
+}
+
+static void
+out_bri(FILE *out, dl_byh *dbhp)
+/*
+ * Write out the "bri" record, no filler
+ */
+{
+ int compass, i;
+
+ /*
+ * Note the <COMPASS_WEST
+ * In this format only the hands of North, East and South are given
+ * West is left as an exercise for the Duplimate
+ * (West just gets what is left over)
+ */
+ for (compass=COMPASS_NORTH; compass<COMPASS_WEST; compass++)
+ for (i=0; i<NCARDSPERHAND; i++)
+ fprintf(out,"%02d",dbhp->dh_hand[compass][i]);
+}
+
+/*ARGSUSED*/
+static void
+print_bri(FILE *out, int boardno, hr_p hrp)
+/*
+ * Writes out "bri" format, including filler
+ *
+ * It is unknown whether the strange half space, half zero fill
+ * is actually necessary. This is just the way I saw my first BRI file
+ */
+{
+ dl_byh *dbhp = hrp->hr_value.hv_dbhp;
+
+ out_bri(out, dbhp);
+ out_fill(out, ' ', 32);
+ out_fill(out, 0, 18);
+}
+
+static void
+out_cardrep(FILE *out, dl_byh *dbhp, char *suitstr[NCOMPASS][NSUIT], char *cardrep)
+/*
+ * Write out a "dge" or "pbn" record, no filler
+ */
+{
+ int compass;
+ int suit;
+ int i;
+ int card, card_in_suit;
+
+ for (compass=COMPASS_NORTH; compass<=COMPASS_WEST; compass++) {
+ for (suit=SUIT_SPADES; suit<=SUIT_CLUBS; suit++) {
+ fputs(suitstr[compass][suit], out);
+ for (i=0; i<NCARDSPERHAND; i++) {
+ /*
+ * Get card in this position for player
+ * If it is part of the suit we are working on
+ * (1..13 for spades, upto 40..52 for clubs)
+ * print it.
+ */
+ card = dbhp->dh_hand[compass][i];
+ card_in_suit = card - 13*suit;
+ if (card_in_suit > 0 && card_in_suit <= 13)
+ putc(cardrep[card_in_suit-1], out);
+ }
+ }
+ }
+}
+
+/*
+ * International card representation
+ *
+ * Maybe this will need to be changed for local formats
+ */
+static char cardrep_int[13] = "AKQJT98765432";
+
+/*
+ * The suit symbols in the DOS character set
+ */
+#define DOSCHAR_SPADE 6
+#define DOSCHAR_HEART 3
+#define DOSCHAR_DIAMOND 4
+#define DOSCHAR_CLUB 5
+
+static char dosstr_spade[] = { DOSCHAR_SPADE, 0 };
+static char dosstr_heart[] = { DOSCHAR_HEART, 0 };
+static char dosstr_diamond[] = { DOSCHAR_DIAMOND, 0 };
+static char dosstr_club[] = { DOSCHAR_CLUB, 0 };
+static char *dge_suitstr[NCOMPASS][NSUIT] = {
+ { dosstr_spade, dosstr_heart, dosstr_diamond, dosstr_club },
+ { dosstr_spade, dosstr_heart, dosstr_diamond, dosstr_club },
+ { dosstr_spade, dosstr_heart, dosstr_diamond, dosstr_club },
+ { dosstr_spade, dosstr_heart, dosstr_diamond, dosstr_club },
+};
+
+static void
+out_dge(FILE *out, dl_byh *dbhp)
+/*
+ * Write out the "dge" record, no filler
+ */
+{
+
+ out_cardrep(out, dbhp, dge_suitstr, cardrep_int);
+}
+
+/*ARGSUSED*/
+static void
+print_dge(FILE *out, int boardno, hr_p hrp)
+/*
+ * Writes out "dge" format, including filler
+ */
+{
+ dl_byh *dbhp = hrp->hr_value.hv_dbhp;
+
+ out_dge(out, dbhp);
+ out_fill(out, 0, 60);
+}
+
+/*
+ * Portable Bridge Notation
+ *
+ * We generate PBN "Import" format, according to the instructions
+ * from KGB
+ */
+static char pbn_colon[] = ":";
+static char pbn_space[] = " ";
+static char pbn_dot[] = ".";
+static char *pbn_suitstr[NCOMPASS][NSUIT] = {
+ { pbn_colon, pbn_dot, pbn_dot, pbn_dot },
+ { pbn_space, pbn_dot, pbn_dot, pbn_dot },
+ { pbn_space, pbn_dot, pbn_dot, pbn_dot },
+ { pbn_space, pbn_dot, pbn_dot, pbn_dot },
+};
+
+static void
+out_pbn(FILE *out, dl_byh *dbhp)
+/*
+ * Write out the "pbn" record, just the hand itself
+ */
+{
+
+ out_cardrep(out, dbhp, pbn_suitstr, cardrep_int);
+}
+
+static char dealerchar[4] = "WNES";
+static char *pbn_vulnrep[] = { "None", "NS", "EW", "All" };
+static int vuln_index[16] = {
+ 2, 0, 1, 2,
+ 3, 1, 2, 3,
+ 0, 2, 3, 0,
+ 1, 3, 0, 1
+};
+
+static void
+init_pbn(FILE *out)
+{
+
+ fprintf(out, "%% PBN 1.0\n");
+ fprintf(out, "[Generator \"Big Deal version %d.%d\"]\n",
+ VERSION_MAJOR, VERSION_MINOR);
+}
+
+static void
+print_pbn(FILE *out, int boardno, hr_p hrp)
+/*
+ * Write out the "pbn" record, with furnishings
+ */
+{
+ dl_byh *dbhp = hrp->hr_value.hv_dbhp;
+
+ fprintf(out, "[Board \"%d\"]\n", boardno);
+ fprintf(out, "[Dealer \"%c\"]\n", dealerchar[boardno%4]);
+ fprintf(out, "[Vulnerable \"%s\"]\n",
+ pbn_vulnrep[vuln_index[boardno%16]]);
+ fprintf(out, "[Deal \"N");
+ out_pbn(out, dbhp);
+ fprintf(out, "\"]\n");
+ fprintf(out, "\n");
+}
+
+/*
+ * CSV format, for database import
+ */
+
+static char csv_begin[] = "\"";
+static char csv_intra[] = "\",\"";
+static char *csv_suitstr[NCOMPASS][NSUIT] = {
+ { csv_begin, csv_intra, csv_intra, csv_intra },
+ { csv_intra, csv_intra, csv_intra, csv_intra },
+ { csv_intra, csv_intra, csv_intra, csv_intra },
+ { csv_intra, csv_intra, csv_intra, csv_intra },
+};
+static char *csv_vulnrep[] = { "-", "NS", "EW", "All" };
+
+static void
+print_csv(FILE *out, int boardno, hr_p hrp)
+/*
+ * Write out a CSV record
+ */
+{
+ dl_byh *dbhp = hrp->hr_value.hv_dbhp;
+
+ out_cardrep(out, dbhp, csv_suitstr, cardrep_int);
+ fprintf(out, "\",\"%d\",\"%c/%s\"\n",
+ boardno,
+ dealerchar[boardno%4],
+ csv_vulnrep[vuln_index[boardno%16]]);
+}
+
+/*
+ * DUP and DLM formats, for Jannersten Duplimate machine
+ */
+
+static void
+print_dup_common(FILE *out, int boardno, dl_byh *dbhp, int not_on_screen)
+/*
+ * Write out a "dup" record
+ * This is actually a BRI, followed by a DGE, and some padding
+ */
+{
+
+ /*
+ * Horribly convoluted hack upon hack format
+ *
+ * Duplimate, generation X
+ */
+ out_bri(out, dbhp);
+ if (boardno == 1 && not_on_screen) {
+ out_fill(out, ' ', 68);
+ } else {
+ out_dge(out, dbhp);
+ }
+ if (boardno == 1) {
+ putc('Y', out); /* Y to Random Hands */
+ putc('N', out); /* N to reverse order */
+ putc('1', out); /* Start at board 1 */
+ putc(' ', out);
+ putc(' ', out);
+ putc('0', out); /* No copies immediate */
+ putc(' ', out);
+ putc('0' + (prparp->pp_nboards/10)%10, out);/* tens of number */
+ putc('0' + prparp->pp_nboards%10, out); /* units of number */
+ putc(' ', out);
+ } else {
+ out_fill(out, 0, 10);
+ }
+}
+
+/*ARGSUSED*/
+static void
+print_dup(FILE *out, int boardno, hr_p hrp)
+{
+ dl_byh *dbhp = hrp->hr_value.hv_dbhp;
+
+ print_dup_common(out, boardno, dbhp, 0);
+}
+
+/*ARGSUSED*/
+static void
+print_dupblind(FILE *out, int boardno, hr_p hrp)
+{
+ dl_byh *dbhp = hrp->hr_value.hv_dbhp;
+
+ print_dup_common(out, boardno, dbhp, 1);
+}
+
+static void
+init_dlmcommon(FILE *out, int show)
+{
+ int checksum;
+
+ /*
+ * Very badly documented, sorry(not my fault)
+ */
+ fprintf(out, "[Document]\n");
+ fprintf(out, "Headline=Generated by Big Deal version %d.%d\n",
+ VERSION_MAJOR, VERSION_MINOR);
+ fprintf(out, "Status=%s\n", show ? "Show" : "Sealed");
+ fprintf(out, "Duplicates=1\n");
+ fprintf(out, "From board=1\n");
+ fprintf(out, "To board=%d\n",
+ prparp->pp_nboards<=99 ? prparp->pp_nboards : 99);
+ fprintf(out, "Next board to duplimate=0\n");
+ fprintf(out, "PrintOuts=0\n");
+ fprintf(out, "Crypto key=0\n");
+ checksum = prparp->pp_nboards;
+ if (show)
+ checksum ^= 1; /* XOR with 1 if show */
+ fprintf(out, "Checksum=%d\n", checksum);
+}
+
+static void
+init_dlm(FILE *out)
+{
+
+ init_dlmcommon(out, 1);
+}
+
+static void
+init_dlmblind(FILE *out)
+{
+
+ init_dlmcommon(out, 0);
+}
+
+static void
+print_dlm(FILE *out, int boardno, hr_p hrp)
+/*
+ * Writes out one DLM record, including "checksum"
+ * Record is mainly one lower case character per two cards
+ */
+{
+ dl_byc *dbcp = hrp->hr_value.hv_dbcp;
+ int i;
+ int c;
+ int checksum;
+
+ if (boardno>99) /* Format cannot do more than 99 */
+ return;
+ fprintf(out, "Duplicates %02d=0\n", boardno);
+ fprintf(out, "Board %02d=", boardno);
+ checksum = boardno;
+ for(i=0; i<NCARDSPERDECK/2; i++) {
+ c = 'a';
+ c += dbcp->dc_card[2*i+0]<<2;
+ c += dbcp->dc_card[2*i+1]<<0;
+ putc(c, out);
+ checksum ^= c;
+ }
+ fprintf(out, "%03d\n", checksum);
+}
+
+static void
+finish_dlm(FILE *out)
+/*
+ * For some reason a DLM file always contains exactly 99 hands
+ * fill it up with bogus hands here
+ */
+{
+ int i;
+ int checksum;
+
+ for (i=prparp->pp_nboards+1; i<=99; i++) {
+ checksum = i^14; /* KGB calculated */
+ fprintf(out, "Duplicates %02d=0\n", i);
+ fprintf(out, "Board %02d=aaaaaabffffffkkkkkklpppppp%03d\n",
+ i, checksum);
+ }
+}
+
+/*ARGSUSED*/
+static void
+print_ber(FILE *out, int boardno, hr_p hrp)
+/*
+ * Writes out "ber" format, no filler or separators
+ */
+{
+ dl_byc *dbcp = hrp->hr_value.hv_dbcp;
+ int i;
+
+ for(i=0; i<NCARDSPERDECK; i++)
+ putc('1' + dbcp->dc_card[i], out);
+}
+
+/*
+ * Borel files contain lines of 52 characters [A-Za-z]
+ * A is AS to z is 2c
+ * For some reason the line does not start with the cards of North
+ * but the cards of the dealer.
+ */
+static void
+init_borel(FILE *out)
+{
+
+ fprintf(out, "\n");
+}
+
+static void
+print_borel(FILE *out, int boardno, hr_p hrp)
+/*
+ * Write out a "bhg" record
+ */
+{
+ dl_byh *dbhp = hrp->hr_value.hv_dbhp;
+ int i, j, compass, card;
+
+ for (i=0; i<NCOMPASS; i++) {
+ /*
+ * Start at dealer of this hand
+ */
+ compass = (boardno - 1 + i) % NCOMPASS;
+ for (j=0; j<NCARDSPERHAND; j++) {
+ card = dbhp->dh_hand[compass][j];
+ putc(card <= 2*NCARDSPERHAND ?
+ 'A'+card-1 : 'a'+card-2*NCARDSPERHAND-1,
+ out);
+ }
+ }
+ fprintf(out, "\n");
+}
+
+#ifdef BIGDEALX
+
+#include "mp.h"
+
+/*
+ * A decimal number corresponding to a deal has maximum 29 digits
+ */
+#define MAXDECIMALS 29
+static byte powers_of_ten[MAXDECIMALS][L];
+
+static void
+init_goedel(FILE *out)
+{
+ int i;
+ byte ten[L];
+
+ /*
+ * Initialize the array with powers of ten
+ * We need them in the next function
+ */
+ mp96_zero(ten);
+ ten[L-1] = 10;
+ mp96_one(powers_of_ten[0]);
+ for (i=1; i<MAXDECIMALS; i++) {
+ mp96_mul(powers_of_ten[i], powers_of_ten[i-1], ten);
+ }
+}
+
+/*ARGSUSED*/
+static void
+print_goedel(FILE *out, int boardno, hr_p hrp)
+/*
+ * Write out the internal 96 bit numbers as a decimal number
+ *
+ * Unlikely that anyone would ever want this in real life
+ */
+{
+ dl_num *dnp = hrp->hr_value.hv_dnp;
+ byte value[L];
+ int i;
+ int decimal;
+ int zero_suppress;
+ char outchar;
+
+ mp96_assign(value, dnp->dn_num);
+ zero_suppress = 1;
+ for (i=MAXDECIMALS-1; i>=0; i--) {
+ /*
+ * Since we do not have division we do repeated subtraction
+ */
+ decimal = 0;
+ while (mp96_cmp(value, powers_of_ten[i])>=0) {
+ mp96_sub(value, value, powers_of_ten[i]);
+ decimal++;
+ }
+ if (decimal) {
+ outchar = decimal + '0';
+ zero_suppress = 0;
+ } else {
+ /*
+ * Suppress leading zeroes
+ * Special case all zeroes also handled
+ * That will be the day....
+ */
+ outchar = zero_suppress && i ? ' ' : '0';
+ }
+ putc(outchar, out);
+ /*
+ * Intersperse with commas for readability
+ */
+ if (i%3 == 0) {
+ putc(i==0 ? '\n' : zero_suppress ? ' ' : ',', out);
+ }
+ }
+}
+
+/*
+ * Statistics computation:
+ * There is a strong argument *not* to print statistics of a set of deals
+ * since it leads itself to abuse only too easily.
+ * Therefore this is only included in the non-safe version.
+ */
+
+static struct {
+ int st_sumsquare;
+ int st_hcp[NCOMPASS];
+ int st_hcpfreq[38]; /* 0..37, maximum points per hand */
+ int st_lsuitfreq[NCOMPASS][14]; /* 4..13, longest suit per compass */
+ int st_longestsuit; /* longest suit in whole set */
+} stats;
+
+/*
+ * Theoretical values taken from Encyclopedia, fourth edition, page 278
+ */
+static int theory_hcpfreq[38] = { /* times 10000 */
+ 3639, /* 0 */
+ 7884, /* 1 */
+ 13561, /* 2 */
+ 24624, /* 3 */
+ 38454, /* 4 */
+ 51862, /* 5 */
+ 65541, /* 6 */
+ 80281, /* 7 */
+ 88922, /* 8 */
+ 93562, /* 9 */
+ 94051, /* 10 */
+ 89447, /* 11 */
+ 80269, /* 12 */
+ 69143, /* 13 */
+ 56933, /* 14 */
+ 44237, /* 15 */
+ 33109, /* 16 */
+ 23617, /* 17 */
+ 16051, /* 18 */
+ 10362, /* 19 */
+ 6435, /* 20 */
+ 3779, /* 21 */
+ 2100, /* 22 */
+ 1119, /* 23 */
+ 559, /* 24 */
+ 264, /* 25 */
+ 117, /* 26 */
+ 49, /* 27 */
+ 19, /* 28 */
+ 7, /* 29 */
+ 2, /* 30 */
+ 1, 1, 1, 1, 1, 1, 1 /* 31..37 */
+};
+
+static int theory_lsuitfreq[14] = { /* probability * 1000000 */
+ 0, 0, 0, 0, /* cannot be 0 1 2 3 */
+ 350805, /* 4 */
+ 443596, /* 5 */
+ 165477, /* 6 */
+ 35265, /* 7 */
+ 4668, /* 8 */
+ 370, /* 9 */
+ 16, /* 10 */
+ 0, 0, 0 /* 11 .. 13 */
+};
+
+
+static int high_card_points[13] = { 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/*ARGSUSED*/
+static void
+init_stat(FILE *out)
+{
+
+ /*
+ * Warn of consequences of looking at statistics
+ */
+ printf("\nStatistics will be produced in an accompanying file.\n");
+ printf("Usage of these statistics for a decision on whether or when\n");
+ printf("to play this set of deals is a violation of the spirit of the\n");
+ printf("Laws of Duplicate Contract Bridge.\n\n");
+ printf("STATISTICS ARE FOR DISPLAY PURPOSES ONLY!!\n\n");
+ /*
+ * At this point initialize counters if non-zero
+ */
+}
+
+/*ARGSUSED*/
+static void
+compute_stat(FILE *out, int boardno, hr_p hrp)
+/*
+ * Compute statistics on this hand
+ */
+{
+ dl_byh *dbhp = hrp->hr_value.hv_dbhp;
+ int i, compass, card;
+ int length, maxlength, suitlength[NCOMPASS][NSUIT];
+ int hcp;
+
+ for (compass=0; compass<NCOMPASS; compass++) {
+ for (i=0; i<NSUIT; i++)
+ suitlength[compass][i] = 0;
+ hcp = 0;
+ for (i=0; i<NCARDSPERHAND; i++) {
+ /*
+ * Note the -1 to convert from 1..52 to 0..51
+ */
+ card = dbhp->dh_hand[compass][i]-1;
+ /*
+ * card/13 should be suit
+ */
+ suitlength[compass][card/13]++;
+ /*
+ * card%13 should be pip
+ */
+ hcp += high_card_points[card%13];
+ }
+ stats.st_hcp[compass] += hcp;
+ stats.st_hcpfreq[hcp]++;
+ maxlength = 0;
+ for (i=0; i<NSUIT; i++) {
+ length = suitlength[compass][i];
+ if (length > maxlength)
+ maxlength = length;
+ stats.st_sumsquare += length*length;
+ }
+ if (maxlength > stats.st_longestsuit)
+ stats.st_longestsuit = maxlength;
+ stats.st_lsuitfreq[compass][maxlength]++;
+ }
+ /*
+ * Now suitlength[][] contains all suit lengths for all hands
+ * 16 numbers in all.
+ * Statistics about distribution to be added here.
+ */
+}
+
+static void
+print_avg(FILE *out, int sum, int divisor, int decimals, char *termstr)
+/*
+ * Prevent use of floating point
+ */
+{
+ int power_of_ten;
+ int i;
+ int result;
+
+ power_of_ten = 1;
+ for (i=0; i<decimals; i++)
+ power_of_ten *= 10;
+
+ sum *= power_of_ten;
+ /*
+ * Make sure of proper rounding
+ */
+ sum += divisor/2;
+ result = sum/divisor;
+
+ fprintf(out, "%d", result/power_of_ten);
+ if (decimals) {
+ fprintf(out, ".");
+ for (i=0; i<decimals; i++) {
+ result -= result/power_of_ten*power_of_ten;
+ power_of_ten /= 10;
+ fprintf(out, "%d", result/power_of_ten);
+ }
+ }
+ fprintf(out, "%s", termstr);
+}
+
+static void
+print_stat(FILE *out)
+{
+ int i, length;
+
+ fprintf(out, "Number of deals: %d\n", prparp->pp_nboards);
+
+ fprintf(out, "Average sum of squares: ");
+ print_avg(out, stats.st_sumsquare, prparp->pp_nboards, 0, "\n");
+
+ fprintf(out, "Average points NESW: ");
+ print_avg(out, stats.st_hcp[0], prparp->pp_nboards, 2, " ");
+ print_avg(out, stats.st_hcp[1], prparp->pp_nboards, 2, " ");
+ print_avg(out, stats.st_hcp[2], prparp->pp_nboards, 2, " ");
+ print_avg(out, stats.st_hcp[3], prparp->pp_nboards, 2, "\n");
+
+ fprintf(out, "Frequency of high card points per hand:\n");
+ for (i=0; i<38; i++) {
+ if (stats.st_hcpfreq[i] == 0)
+ continue;
+ fprintf(out, "%2d points: ", i);
+ print_avg(out, stats.st_hcpfreq[i]*100, prparp->pp_nboards*4, 4, "% (");
+ print_avg(out, theory_hcpfreq[i], 10000, 4, "%)\n");
+ }
+
+ fprintf(out, "Frequency of longest suitlength per compass:\n");
+ fprintf(out, "%8s%8s%8s%8s%8s Theory\n", "Length",
+ "North", "East", "South", "West");
+ for(length = 4; length <= stats.st_longestsuit; length++) {
+ fprintf(out, "%8d%8d%8d%8d%8d ", length,
+ stats.st_lsuitfreq[COMPASS_NORTH][length],
+ stats.st_lsuitfreq[COMPASS_EAST][length],
+ stats.st_lsuitfreq[COMPASS_SOUTH][length],
+ stats.st_lsuitfreq[COMPASS_WEST][length]);
+ print_avg(out, 100*theory_lsuitfreq[length],
+ 100000000/prparp->pp_nboards, 1, "\n");
+ }
+}
+#endif
+
+/*
+ * All formats are set to not in use.
+ * The output_specify_formats routine will set the wanted formats
+ */
+static
+of_t output_formats[] = {
+ { "dup", OFF_BINARY, NULL, ".dup", BF_BYHAND,
+ "Duplimate format",
+ 0, print_dup, 0 },
+ { "dupblind", OFF_BINARY, NULL, ".dup", BF_BYHAND,
+ "Duplimate format, no cards on screen",
+ 0, print_dupblind, 0 },
+ { "dlm", 0, NULL, ".dlm", BF_BYCARD,
+ "New Duplimate format",
+ init_dlm, print_dlm, finish_dlm },
+ { "dlmblind", 0, NULL, ".dlm", BF_BYCARD,
+ "New Duplimate format, no cards on screen",
+ init_dlmblind, print_dlm, finish_dlm },
+ { "bri", OFF_BINARY, NULL, ".bri", BF_BYHAND,
+ "BRI format",
+ 0, print_bri, 0 },
+ { "dge", OFF_BINARY, NULL, ".dge", BF_BYHAND,
+ "DGE format",
+ 0, print_dge, 0 },
+ { "pbn", 0, NULL, ".pbn", BF_BYHAND,
+ "Portable Bridge Notation",
+ init_pbn, print_pbn, 0 },
+ { "csv", 0, NULL, ".csv", BF_BYHAND,
+ "Comma Separated Values",
+ 0, print_csv, 0 },
+ { "ber", OFF_BINARY, NULL, ".ber", BF_BYCARD,
+ "Bernasconi format",
+ 0, print_ber, 0 },
+ { "borel", 0, NULL, ".bhg", BF_BYHAND,
+ "Borel Hand Generator format",
+ init_borel, print_borel, 0 },
+#ifdef BIGDEALX
+ { "goedel", 0, NULL, ".goe", BF_GOEDEL,
+ "Internal goedel number format(development only)",
+ init_goedel, print_goedel, 0 },
+ { "stats", 0, NULL, ".txt", BF_BYHAND,
+ "Statistics on set",
+ init_stat, compute_stat, print_stat },
+ { "stdout", 0, NULL, 0, BF_BYHAND,
+ "Portable Bridge Notation on standard output",
+ init_pbn, print_pbn, 0 },
+#endif
+ { 0 }
+};
+
+void
+output_help() {
+ of_p ofp;
+
+ printf("%-8s%8s Explanation\n\n", "Name", "Suffix");
+ /* For all formats: */
+ for (ofp=output_formats; ofp->of_name; ofp++) {
+ printf("%-8s%8s %s\n",
+ ofp->of_name,
+ ofp->of_suffix? ofp->of_suffix : "",
+ ofp->of_help);
+ }
+ printf("\n");
+}
+
+#define NTOKENS 10
+static char **
+tokenize(char *tokenstring, char separator)
+{
+ static char *tokenvec[NTOKENS+1];
+ char *tsp, *sepp;
+ int tokencount;
+
+ tsp = tokenstring;
+ tokencount = 0;
+ do {
+ tokenvec[tokencount++] = tsp;
+ if ((sepp = strchr(tsp, separator)) != 0) {
+ *sepp++ = 0;
+ tsp = sepp;
+ }
+ } while (sepp && tokencount < NTOKENS);
+ if (sepp)
+ fprintf(stderr, "too many words, \"%s\" ignored\n", sepp);
+ tokenvec[tokencount] = 0;
+ return tokenvec;
+}
+
+static int
+suf_in_use(char *name, char *suffix)
+{
+ of_p ofp;
+
+ /*
+ * Check for potential duplicate suffix use
+ */
+ for (ofp=output_formats; ofp->of_name; ofp++) {
+ if (!(ofp->of_flags&OFF_USEIT)) {
+ /*
+ * Format not in use, no problem
+ */
+ continue;
+ }
+ if (ofp->of_suffix != 0) {
+ if (suffix == 0 || strcmp(ofp->of_suffix, suffix) != 0) {
+ /*
+ * Not same suffix, no problem
+ */
+ continue;
+ }
+ } else {
+ /*
+ * To check suffix is 0 (stdout)
+ */
+ if (suffix != 0) {
+ /*
+ * One 0, other not, no problem
+ */
+ continue;
+ }
+ }
+ /*
+ * Ok, we have a problem
+ */
+ fprintf(stderr, "Cannot use %s, because %s shares suffix %s\n",
+ name, ofp->of_name, suffix ? suffix : "NONE");
+ return 1;
+ }
+ return 0;
+}
+
+int
+output_specify_formats(char *format_string, int for_real)
+/*
+ * Comma separated list of output formats to use
+ *
+ * Return value is used to check validity of string
+ */
+{
+ char **ofvector;
+ of_p ofp;
+ int found;
+ int retval;
+
+ retval = 1; /* While all OK */
+ for(ofvector = tokenize(format_string, ','); *ofvector; ofvector++) {
+ found = 0;
+ for (ofp=output_formats; ofp->of_name; ofp++) {
+ if (strcmp(ofp->of_name, *ofvector) == 0) {
+ if (suf_in_use(ofp->of_name, ofp->of_suffix)) {
+ retval = 0;
+ } else {
+ ofp->of_flags |= OFF_USEIT;
+ }
+ found++;
+ break;
+ }
+ }
+ if (!found) {
+ fprintf(stderr, "Format %s unknown\n", *ofvector);
+ retval = 0;
+ }
+ }
+
+ if (!for_real) {
+ /*
+ * This was just a test, clean up
+ */
+ for (ofp=output_formats; ofp->of_name; ofp++) {
+ ofp->of_flags &= ~OFF_USEIT;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * This next section is to support multiple internal formats,
+ * where there is a limited number of possible transformations
+ * and we try to minimize the internal formats generated
+ * Just generate what we need
+ */
+#define bit(x) (1<<(x))
+static int formatset; /* Bitmap of internal formats to support */
+static int formatclosure[NBASEFORM] = {
+ bit(BF_GOEDEL),
+ bit(BF_GOEDEL) | bit(BF_INTERNAL),
+ bit(BF_GOEDEL) | bit(BF_INTERNAL) | bit(BF_BYHAND),
+ bit(BF_GOEDEL) | bit(BF_INTERNAL) | bit(BF_BYHAND) | bit(BF_BYCARD),
+};
+
+void
+output_createfiles(char *fileprefix, progparams_p ppp)
+/*
+ * Create a file for every wanted output format
+ * Set the formatset variable
+ */
+{
+ char filename[100];
+ of_p ofp;
+ FILE *f;
+
+ prparp = ppp;
+ /* For all formats: */
+ for (ofp=output_formats; ofp->of_name; ofp++) {
+ /* If not in use forget it */
+ if (!(ofp->of_flags&OFF_USEIT))
+ continue;
+
+ /* Make note of internal formats we have to make */
+ formatset |= formatclosure[ofp->of_baseform];
+
+ /* If no suffix use Standard Output */
+ if (ofp->of_suffix) {
+ if (strlen(fileprefix)+strlen(ofp->of_suffix)+1 >
+ sizeof(filename)) {
+ fprintf(stderr, "File name %s%s too long(cannot happen)\n", fileprefix, ofp->of_suffix);
+ exit(-1);
+ }
+ strcpy(filename, fileprefix);
+ strcat(filename, ofp->of_suffix);
+ f = fopen(filename, ofp->of_flags&OFF_BINARY ? "wb" : "w");
+ if (f == NULL) {
+ fprintf(stderr, "Cannot create %s\n", filename);
+ exit(-1);
+ }
+ ofp->of_file = f;
+ } else {
+ ofp->of_file = stdout;
+ }
+ /*
+ * If there is an init routine call it now
+ */
+ if (ofp->of_init)
+ (*ofp->of_init)(ofp->of_file);
+ }
+}
+
+void
+output_closefiles()
+/*
+ * Close all output files
+ */
+{
+ of_p ofp;
+
+ /* For all formats: */
+ for (ofp=output_formats; ofp->of_name; ofp++) {
+ /* If not in use forget it */
+ if (ofp->of_file != NULL) {
+ /*
+ * If there is a finish routine call it first
+ */
+ if (ofp->of_finish)
+ (*ofp->of_finish)(ofp->of_file);
+ if (ofp->of_suffix) /* So not STDOUT */
+ fclose(ofp->of_file);
+ }
+ }
+}
+
+void
+output_hand(int boardno, dl_num *dnp)
+/*
+ * Convert goedel number to various internal forms as necessary
+ * Output all desired formats
+ */
+{
+ dl_int dinternal;
+ dl_byh dbyhand;
+ dl_byc dbycard;
+ of_p ofp;
+ hr_t handrep;
+
+ /*
+ * First create all necessary internal forms
+ * Bits in formatset have been inited in output_createfiles
+ */
+ if (formatset&bit(BF_INTERNAL))
+ code_to_hand(dnp, &dinternal);
+ if (formatset&bit(BF_BYHAND))
+ cnv_int_byh(&dinternal, &dbyhand);
+ if (formatset&bit(BF_BYCARD))
+ cnv_byh_byc(&dbyhand, &dbycard);
+
+ /*
+ * Now call output routines
+ */
+ for(ofp = output_formats; ofp->of_name; ofp++) {
+ if (ofp->of_file == NULL)
+ continue;
+ handrep.hr_baseform = ofp->of_baseform;
+ switch (ofp->of_baseform) {
+ case BF_GOEDEL:
+ handrep.hr_value.hv_dnp = dnp;
+ break;
+ case BF_INTERNAL:
+ handrep.hr_value.hv_dip = &dinternal;
+ break;
+ case BF_BYHAND:
+ handrep.hr_value.hv_dbhp = &dbyhand;
+ break;
+ case BF_BYCARD:
+ handrep.hr_value.hv_dbcp = &dbycard;
+ break;
+ }
+ (*ofp->of_printit)(ofp->of_file, boardno, &handrep);
+ }
+}