#include "types.h" #include "bigdeal.h" #include "binomial.h" #include #include 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; idi_hand[compass][0]; else skip=dip->di_hand[compass][i] - dip->di_hand[compass][i-1]; for (skipcnt=0; skipcntdh_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; idh_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; idh_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; idc_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; idc_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; idh_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; ihr_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; compassdh_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 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; ipp_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); } }