/* * Copyright (C) 2002-2004 Joern Thyssen * Copyright (C) 2002-2021 the AUTHORS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #if HAVE_UNISTD_H #include #endif #include "analysis.h" #include "backgammon.h" #include "drawboard.h" #include "format.h" #include "export.h" #include "eval.h" #include "positionid.h" #include "matchid.h" #include "formatgs.h" #include "relational.h" #include #include #ifdef WIN32 #include #endif #include "util.h" typedef enum { CLASS_MOVETABLE, CLASS_MOVEHEADER, CLASS_MOVENUMBER, CLASS_MOVEPLY, CLASS_MOVEMOVE, CLASS_MOVEEQUITY, CLASS_MOVETHEMOVE, CLASS_MOVEODD, CLASS_BLUNDER, CLASS_JOKER, CLASS_STATTABLE, CLASS_STATTABLEHEADER, CLASS_RESULT, CLASS_TINY, CLASS_CUBEDECISION, CLASS_CUBEDECISIONHEADER, CLASS_COMMENT, CLASS_COMMENTHEADER, CLASS_NUMBER, CLASS_FONT_FAMILY, CLASS_BLOCK, CLASS_PERCENT, CLASS_POSITIONID, CLASS_CUBE_EQUITY, CLASS_CUBE_ACTION, CLASS_CUBE_PLY, CLASS_CUBE_PROBABILITIES, CLASS_CUBE_CUBELESS_TEXT, CLASS_BOARD_IMG_HEADER, CLASS_BOARD_IMG, NUM_CLASSES } stylesheetclass; static const char *aaszStyleSheetClasses[NUM_CLASSES][2] = { {"movetable", "background-color: #ddddee"}, {"moveheader", "background-color: #89d0e2; padding: 0.5em"}, {"movenumber", "width: 2em; text-align: right"}, {"moveply", "width: 5em; text-align: center"}, {"movemove", "width: 20em; text-align: left"}, {"moveequity", "width: 10em; text-align: left"}, {"movethemove", "background-color: #ffffcc"}, {"moveodd", "background-color: #d0d0d0"}, {"blunder", "background-color: red; color: yellow"}, {"joker", "background-color: red; color: yellow"}, {"stattable", "text-align: left; width: 40em; background-color: #fff2cc; " "border: 0px; padding: 0px"}, {"stattableheader", "background-color: #d15b34"}, {"result", "background-color: yellow; font-weight: bold; text-align: center; " "color: black; width: 40em; padding: 0.2em"}, {"tiny", "font-size: 25%"}, {"cubedecision", "background-color: #ddddee; text-align: left;"}, {"cubedecisionheader", "background-color: #89d0e2; text-align: center; padding: 0.5em"}, {"comment", "background-color: #ccffcc; width: 39.5em; padding: 0.5em"}, {"commentheader", "background-color: #6f9915; font-weight: bold; text-align: center; " "width: 40em; padding: 0.25em"}, {"number", "text-align: center; font-weight: bold; font-size: 60%; " "font-family: sans-serif"}, {"fontfamily", "font-family: sans-serif"}, {"block", "display: block"}, {"percent", "text-align: right"}, {"positionid", "font-size: 75%; color: #787878"}, {"cubeequity", "font-weight: bold"}, {"cubeaction", "color: red"}, {"cubeply", "font-weight: bold"}, {"cubeprobs", "font-weight: bold"}, {"cubecubelesstext", "font-style: italic"}, {"boardimagetop", "vertical-align: bottom"}, {"boardimage", "vertical-align: top"} }; const char *aszHTMLExportType[NUM_HTML_EXPORT_TYPES] = { "gnu", "bbs", "fibs2html" }; const char *aszHTMLExportCSS[NUM_HTML_EXPORT_CSS] = { N_("in "), N_("inline (inside tags)"), N_("external file (\"gnubg.css\")") }; const char *aszHTMLExportCSSCommand[NUM_HTML_EXPORT_CSS] = { "head", "inline", "external" }; /* text for links on html page */ static const char *aszLinkText[] = { N_("[First Game]"), N_("[Previous Game]"), N_("[Next Game]"), N_("[Last Game]") }; static const char *bullet = "• "; static void WriteStyleSheet(FILE * pf, const htmlexportcss hecss) { int i; if (hecss == HTML_EXPORT_CSS_HEAD) fputs("\n", pf); else if (hecss == HTML_EXPORT_CSS_EXTERNAL) fputs("\n" "/* end of file */\n", pf); } static char * GetStyle(const stylesheetclass ssc, const htmlexportcss hecss) { static char sz[200]; switch (hecss) { case HTML_EXPORT_CSS_INLINE: sprintf(sz, "style=\"%s\"", aaszStyleSheetClasses[ssc][1]); break; case HTML_EXPORT_CSS_EXTERNAL: case HTML_EXPORT_CSS_HEAD: sprintf(sz, "class=\"%s\"", aaszStyleSheetClasses[ssc][0]); break; default: strcpy(sz, ""); break; } return sz; } static char * GetStyleGeneral(const int hecss, ...) { static char sz[2048]; va_list val; int i = 0; int j; va_start(val, (const int) hecss); switch (hecss) { case HTML_EXPORT_CSS_INLINE: strcpy(sz, "style=\""); break; case HTML_EXPORT_CSS_EXTERNAL: case HTML_EXPORT_CSS_HEAD: strcpy(sz, "class=\""); break; default: strcpy(sz, ""); break; } while ((j = va_arg(val, int)) > -1) { stylesheetclass ssc = (stylesheetclass) j; switch (hecss) { case HTML_EXPORT_CSS_INLINE: if (i) strcat(sz, "; "); strcat(sz, aaszStyleSheetClasses[ssc][1]); break; case HTML_EXPORT_CSS_EXTERNAL: case HTML_EXPORT_CSS_HEAD: if (i) strcat(sz, " "); strcat(sz, aaszStyleSheetClasses[ssc][0]); break; default: break; } ++i; } va_end(val); strcat(sz, "\""); return sz; } static void printRolloutTable(FILE * pf, char asz[][1024], float aarOutput[][NUM_ROLLOUT_OUTPUTS], float aarStdDev[][NUM_ROLLOUT_OUTPUTS], const cubeinfo aci[], const int cci, const int fCubeful, const int fHeader, const htmlexportcss hecss) { int ici; fputs("\n", pf); if (fHeader) { fputs("", pf); if (asz) fputs("", pf); fprintf(pf, "" "" "" "" "" "" "" "", _("Win"), _("W g"), _("W bg"), _("Lose"), _("L g"), _("L bg"), _("Cubeless")); if (fCubeful) fprintf(pf, "", _("Cubeful")); fputs("\n", pf); } for (ici = 0; ici < cci; ici++) { fputs("", pf); /* output */ if (asz) fprintf(pf, "", asz[ici]); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarOutput[ici][OUTPUT_WIN])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarOutput[ici][OUTPUT_WINGAMMON])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarOutput[ici][OUTPUT_WINBACKGAMMON])); fputs("", pf); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(1.0f - aarOutput[ici][OUTPUT_WIN])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarOutput[ici][OUTPUT_LOSEGAMMON])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarOutput[ici][OUTPUT_LOSEBACKGAMMON])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputEquityScale(aarOutput[ici][OUTPUT_EQUITY], &aci[ici], &aci[0], TRUE)); if (fCubeful) fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputMWC(aarOutput[ici][OUTPUT_CUBEFUL_EQUITY], &aci[0], TRUE)); fputs("\n", pf); /* stddev */ fputs("", pf); if (asz) fprintf(pf, "", _("Standard deviation")); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarStdDev[ici][OUTPUT_WIN])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarStdDev[ici][OUTPUT_WINGAMMON])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarStdDev[ici][OUTPUT_WINBACKGAMMON])); fputs("", pf); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarStdDev[ici][OUTPUT_WIN])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarStdDev[ici][OUTPUT_LOSEGAMMON])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputPercent(aarStdDev[ici][OUTPUT_LOSEBACKGAMMON])); fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputEquityScale(aarStdDev[ici][OUTPUT_EQUITY], &aci[ici], &aci[0], FALSE)); if (fCubeful) fprintf(pf, "", GetStyle(CLASS_PERCENT, hecss), OutputMWC(aarStdDev[ici][OUTPUT_CUBEFUL_EQUITY], &aci[0], FALSE)); fputs("\n", pf); } fputs("
%s%s%s%s%s%s%s%s
%s%s%s%s-%s%s%s%s%s
%s%s%s%s-%s%s%s%s%s
\n", pf); } static void printStatTableHeader(FILE * pf, const htmlexportcss hecss, const char *header) { fprintf(pf, "\n" "", GetStyle(CLASS_STATTABLEHEADER, hecss)); fputs(header, pf); fputs("\n\n", pf); } static void printStatTableRow(FILE * pf, const char *format1, const char *format2, ...) { va_list val; char *sz; va_start(val, format2); sz = g_strdup_printf("\n" "%s\n" "%s\n" "%s\n" "\n", format1, format2, format2); vfprintf(pf, sz, val); g_free(sz); va_end(val); } /* * Print img tag. * * Input: * pf : write to file * szImageDir: path (URI) to images * szImage: the image to print * szExtension: extension of the image (e.g. gif or png) * */ static void printImageClass(FILE * pf, const char *szImageDir, const char *szImage, const char *szExtension, const char *szAlt, const htmlexportcss hecss, const htmlexporttype het, const stylesheetclass ssc) { fprintf(pf, "\"%s\"/", (szImageDir) ? szImageDir : "", (!szImageDir || szImageDir[strlen(szImageDir) - 1] == '/') ? "" : "/", szImage, szExtension, (het == HTML_EXPORT_TYPE_GNU || (het == HTML_EXPORT_TYPE_BBS && ssc != CLASS_BLOCK)) ? GetStyle(ssc, hecss) : "", (szAlt) ? szAlt : ""); } /* * Print img tag. * * Input: * pf : write to file * szImageDir: path (URI) to images * szImage: the image to print * szExtension: extension of the image (e.g. gif or png) * */ static void printImage(FILE * pf, const char *szImageDir, const char *szImage, const char *szExtension, const char *szAlt, const htmlexportcss hecss, const htmlexporttype het) { printImageClass(pf, szImageDir, szImage, szExtension, szAlt, hecss, het, CLASS_BLOCK); } /* * print image for a point * * Input: * pf : write to file * szImageDir: path (URI) to images * szImage: the image to print * szExtension: extension of the image (e.g. gif or png) * anBoard: the board * iPoint: the point to draw * fColor: color * fUp: upper half or lower half of board * */ static void printPointBBS(FILE * pf, const char *szImageDir, const char *szExtension, unsigned int iPoint0, unsigned int iPoint1, const int fColor, const int fUp, const htmlexportcss hecss) { char sz[100]; char szAlt[100]; const char *aasz[2][2] = { {"dd", "dn"}, {"ud", "up"} }; if (iPoint0) { /* player 0 owns the point */ sprintf(sz, "p_%s_w_%u", aasz[fUp][fColor], iPoint0); sprintf(szAlt, "%1xX", iPoint0); } else if (iPoint1) { /* player 1 owns the point */ sprintf(sz, "p_%s_b_%u", aasz[fUp][fColor], iPoint1); sprintf(szAlt, "%1xO", iPoint1); } else { /* empty point */ sprintf(sz, "p_%s_0", aasz[fUp][fColor]); sprintf(szAlt, " '"); } printImageClass(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); } static void printHTMLBoardBBS(FILE * pf, matchstate * pms, int fTurn, const char *szImageDir, const char *szExtension, const htmlexportcss hecss) { TanBoard anBoard; unsigned int anPips[2]; int acOff[2] = { 15, 15 }; int i, j; char sz[1024]; memcpy(anBoard, pms->anBoard, sizeof(anBoard)); if (pms->fMove) SwapSides(anBoard); PipCount((ConstTanBoard) anBoard, anPips); for (i = 0; i < 2; i++) { for (j = 0; j < 25; j++) acOff[i] -= anBoard[i][j]; } PipCount((ConstTanBoard) anBoard, anPips); /* Begin table and print for player 0 */ fprintf(pf, "", ap[0].szName, anPips[1]); /* avoid page break when printing */ fputs("\n", pf); fprintf(pf, "", ap[1].szName, anPips[0]); /* pip counts */ fputs("
%s%u
", pf); /* * Top row */ printImageClass(pf, szImageDir, fTurn ? "n_high" : "n_low", szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG_HEADER); fputs("
\n", pf); /* chequers off */ sprintf(sz, "o_w_%d", acOff[1]); printImageClass(pf, szImageDir, sz, szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); /* player 0's inner board */ for (i = 0; i < 6; i++) printPointBBS(pf, szImageDir, szExtension, anBoard[1][i], anBoard[0][23 - i], !(i % 2), TRUE, hecss); /* player 0's chequers on the bar */ sprintf(sz, "b_up_%u", anBoard[0][24]); printImageClass(pf, szImageDir, sz, szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); /* player 0's outer board */ for (i = 0; i < 6; i++) printPointBBS(pf, szImageDir, szExtension, anBoard[1][i + 6], anBoard[0][17 - i], !(i % 2), TRUE, hecss); /* player 0 owning cube */ if (!pms->fCubeOwner) { sprintf(sz, "c_up_%d", pms->nCube); printImageClass(pf, szImageDir, sz, szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); } else printImageClass(pf, szImageDir, "c_up_0", szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); fputs("
\n", pf); /* end of first row */ /* * center row (dice) */ if (pms->anDice[0]) { /* dice rolled */ sprintf(sz, "b_center%u%u%s", (pms->anDice[0] < pms->anDice[1]) ? pms->anDice[0] : pms->anDice[1], (pms->anDice[0] < pms->anDice[1]) ? pms->anDice[1] : pms->anDice[0], pms->fMove ? "right" : "left"); printImageClass(pf, szImageDir, sz, szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); } else /* no dice rolled */ printImageClass(pf, szImageDir, "b_center", szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); /* center cube */ if (pms->fCubeOwner == -1) printImageClass(pf, szImageDir, "c_center", szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); else printImageClass(pf, szImageDir, "c_blank", szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); fputs("
\n", pf); /* end of center row */ /* * Bottom row */ /* player 1's chequers off */ sprintf(sz, "o_b_%d", acOff[0]); printImageClass(pf, szImageDir, sz, szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); /* player 1's inner board */ for (i = 0; i < 6; i++) printPointBBS(pf, szImageDir, szExtension, anBoard[1][23 - i], anBoard[0][i], (i % 2), FALSE, hecss); /* player 1's chequers on the bar */ sprintf(sz, "b_dn_%u", anBoard[1][24]); printImageClass(pf, szImageDir, sz, szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); /* player 1's outer board */ for (i = 0; i < 6; i++) printPointBBS(pf, szImageDir, szExtension, anBoard[1][17 - i], anBoard[0][i + 6], (i % 2), FALSE, hecss); /* player 1 owning cube */ if (pms->fCubeOwner == 1) { sprintf(sz, "c_dn_%d", pms->nCube); printImageClass(pf, szImageDir, sz, szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); } else printImageClass(pf, szImageDir, "c_dn_0", szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); fputs("
\n", pf); /* point numbers */ printImageClass(pf, szImageDir, fTurn ? "n_low" : "n_high", szExtension, NULL, hecss, HTML_EXPORT_TYPE_BBS, CLASS_BOARD_IMG); fputs("
%s%u
", pf); /* position ID Player 1 and end of table */ fprintf(pf, "", GetStyle(CLASS_POSITIONID, hecss)); fprintf(pf, "%s %s %s %s
\n", _("Position ID:"), PositionID((ConstTanBoard) pms->anBoard), _("Match ID:"), MatchIDFromMatchState(pms)); } /* * print image for a point * * Input: * pf : write to file * szImageDir: path (URI) to images * szImage: the image to print * szExtension: extension of the image (e.g. gif or png) * anBoard: the board * iPoint: the point to draw * fColor: color * fUp: upper half or lower half of board * */ static void printPointF2H(FILE * pf, const char *szImageDir, const char *szExtension, unsigned int iPoint0, unsigned int iPoint1, const int fColor, const int fUp, const htmlexportcss hecss) { char sz[100]; char szAlt[100]; if (iPoint0) { /* player 0 owns the point */ sprintf(sz, "b-%c%c-o%u", fColor ? 'g' : 'y', fUp ? 'd' : 'u', (iPoint0 >= 11) ? 11 : iPoint0); sprintf(szAlt, "%1xX", iPoint0); } else if (iPoint1) { /* player 1 owns the point */ sprintf(sz, "b-%c%c-x%u", fColor ? 'g' : 'y', fUp ? 'd' : 'u', (iPoint1 >= 11) ? 11 : iPoint1); sprintf(szAlt, "%1xO", iPoint1); } else { /* empty point */ sprintf(sz, "b-%c%c", fColor ? 'g' : 'y', fUp ? 'd' : 'u'); sprintf(szAlt, " '"); } printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_FIBS2HTML); } static void printHTMLBoardF2H(FILE * pf, matchstate * pms, int fTurn, const char *szImageDir, const char *szExtension, const htmlexportcss hecss) { char sz[100]; char szAlt[100]; int fColor; int i; const char *aszDieO[] = { "b-odie1", "b-odie2", "b-odie3", "b-odie4", "b-odie5", "b-odie6" }; const char *aszDieX[] = { "b-xdie1", "b-xdie2", "b-xdie3", "b-xdie4", "b-xdie5", "b-xdie6" }; TanBoard anBoard; unsigned int anPips[2]; memcpy(anBoard, pms->anBoard, sizeof(anBoard)); if (pms->fMove) SwapSides(anBoard); /* top line with board numbers */ fprintf(pf, "

\n"); printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); sprintf(sz, "b-%stop%s", fTurn ? "hi" : "lo", fClockwise ? "" : "r"); printImage(pf, szImageDir, sz, szExtension, fTurn ? "+-13-14-15-16-17-18-+---+-19-20-21-22-23-24-+" : "+-12-11-10--9--8--7-+---+--6--5--4--3--2--1-+", hecss, HTML_EXPORT_TYPE_FIBS2HTML); fprintf(pf, "
\n"); /* cube image */ if (pms->fDoubled) { /* questionmark with cube (if player 0 is doubled) */ if (!pms->fTurn) { sprintf(sz, "b-dup%d", 2 * pms->nCube); printImage(pf, szImageDir, sz, szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); } else printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); } else { /* display arrow */ if (pms->fCubeOwner) printImage(pf, szImageDir, fTurn ? "b-topdn" : "b-topup", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); else { sprintf(sz, "%s%d", fTurn ? "b-tdn" : "b-tup", pms->nCube); printImage(pf, szImageDir, sz, szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); } } /* display left border */ printImage(pf, szImageDir, "b-left", szExtension, "|", hecss, HTML_EXPORT_TYPE_FIBS2HTML); /* display player 0's outer quadrant */ fColor = 1; for (i = 0; i < 6; i++) { printPointF2H(pf, szImageDir, szExtension, anBoard[1][11 - i], anBoard[0][12 + i], fColor, TRUE, hecss); fColor = !fColor; } /* display bar */ if (anBoard[0][24]) { sprintf(sz, "b-bar-x%u", (anBoard[0][24] > 4) ? 4 : anBoard[0][24]); sprintf(szAlt, "|%1X |", anBoard[0][24]); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_FIBS2HTML); } else printImage(pf, szImageDir, "b-bar", szExtension, "|   |", hecss, HTML_EXPORT_TYPE_FIBS2HTML); /* display player 0's home quadrant */ fColor = 1; for (i = 0; i < 6; i++) { printPointF2H(pf, szImageDir, szExtension, anBoard[1][5 - i], anBoard[0][18 + i], fColor, TRUE, hecss); fColor = !fColor; } /* right border */ printImage(pf, szImageDir, "b-right", szExtension, "|", hecss, HTML_EXPORT_TYPE_FIBS2HTML); fprintf(pf, "
\n"); /* center of board */ if (pms->anDice[0] && pms->anDice[1]) { printImage(pf, szImageDir, "b-midin", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, "b-midl", szExtension, "|", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, fTurn ? "b-midg" : "b-midg2", szExtension, "        " "         ", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, fTurn ? "b-midc" : aszDieO[pms->anDice[0] - 1], szExtension, "|   |", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, fTurn ? "b-midg2" : aszDieO[pms->anDice[1] - 1], szExtension, "|   |", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, fTurn ? aszDieX[pms->anDice[0] - 1] : "b-midg2", szExtension, "|   |", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, fTurn ? aszDieX[pms->anDice[1] - 1] : "b-midc", szExtension, "|   |", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, fTurn ? "b-midg2" : "b-midg", szExtension, "        " "         ", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, "b-midr", szExtension, "|", hecss, HTML_EXPORT_TYPE_FIBS2HTML); fprintf(pf, "
\n"); } else { if (pms->fDoubled) printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); else printImage(pf, szImageDir, "b-midin", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, "b-midl", szExtension, "|", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, "b-midg", szExtension, "        " "         ", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, "b-midc", szExtension, "|   |", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, "b-midg", szExtension, "        " "         ", hecss, HTML_EXPORT_TYPE_FIBS2HTML); printImage(pf, szImageDir, "b-midr", szExtension, "|", hecss, HTML_EXPORT_TYPE_FIBS2HTML); fprintf(pf, "
\n"); } /* cube image */ if (pms->fDoubled) { /* questionmark with cube (if player 0 is doubled) */ if (pms->fTurn) { sprintf(sz, "b-ddn%d", 2 * pms->nCube); printImage(pf, szImageDir, sz, szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); } else printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); } else { /* display cube */ if (pms->fCubeOwner == -1 || !pms->fCubeOwner) printImage(pf, szImageDir, fTurn ? "b-botdn" : "b-botup", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); else { sprintf(sz, "%s%d", fTurn ? "b-bdn" : "b-bup", pms->nCube); printImage(pf, szImageDir, sz, szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); } } printImage(pf, szImageDir, "b-left", szExtension, "|", hecss, HTML_EXPORT_TYPE_FIBS2HTML); /* display player 1's outer quadrant */ fColor = 0; for (i = 0; i < 6; i++) { printPointF2H(pf, szImageDir, szExtension, anBoard[1][12 + i], anBoard[0][11 - i], fColor, FALSE, hecss); fColor = !fColor; } /* display bar */ if (anBoard[1][24]) { sprintf(sz, "b-bar-o%u", (anBoard[1][24] > 4) ? 4 : anBoard[1][24]); sprintf(szAlt, "| %1uO|", anBoard[1][24]); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_FIBS2HTML); } else printImage(pf, szImageDir, "b-bar", szExtension, "|   |", hecss, HTML_EXPORT_TYPE_FIBS2HTML); /* display player 1's outer quadrant */ fColor = 0; for (i = 0; i < 6; i++) { printPointF2H(pf, szImageDir, szExtension, anBoard[1][18 + i], anBoard[0][5 - i], fColor, FALSE, hecss); fColor = !fColor; } /* right border */ printImage(pf, szImageDir, "b-right", szExtension, "|", hecss, HTML_EXPORT_TYPE_FIBS2HTML); fprintf(pf, "
\n"); /* bottom */ printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); sprintf(sz, "b-%sbot%s", fTurn ? "lo" : "hi", fClockwise ? "" : "r"); printImage(pf, szImageDir, sz, szExtension, fTurn ? "+-12-11-10--9--8--7-+---+--6--5--4--3--2--1-+" : "+-13-14-15-16-17-18-+---+-19-20-21-22-23-24-+", hecss, HTML_EXPORT_TYPE_FIBS2HTML); fprintf(pf, "
\n"); /* pip counts */ printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); PipCount((ConstTanBoard) anBoard, anPips); fprintf(pf, "%s %s %u, %s %u
\n", _("Pip counts:"), ap[0].szName, anPips[1], ap[1].szName, anPips[0]); /* position ID */ printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, HTML_EXPORT_TYPE_FIBS2HTML); fprintf(pf, "", GetStyle(CLASS_POSITIONID, hecss)); fprintf(pf, _("Position ID: %s Match ID: %s
\n"), PositionID((ConstTanBoard) pms->anBoard), MatchIDFromMatchState(pms)); fprintf(pf, "
"); fprintf(pf, "

\n"); } /* * print image for a point * * Input: * pf : write to file * szImageDir: path (URI) to images * szImage: the image to print * szExtension: extension of the image (e.g. gif or png) * anBoard: the board * iPoint: the point to draw * fColor: color * fUp: upper half or lower half of board * */ static void printPointGNU(FILE * pf, const char *szImageDir, const char *szExtension, unsigned int iPoint0, unsigned int iPoint1, const int fColor, const int fUp, const htmlexportcss hecss) { char sz[100]; char szAlt[100]; if (iPoint0) { /* player 0 owns the point */ sprintf(sz, "b-%c%c-x%u", fColor ? 'g' : 'r', fUp ? 'd' : 'u', iPoint0); sprintf(szAlt, "%1xX", iPoint0); } else if (iPoint1) { /* player 1 owns the point */ sprintf(sz, "b-%c%c-o%u", fColor ? 'g' : 'r', fUp ? 'd' : 'u', iPoint1); sprintf(szAlt, "%1xO", iPoint1); } else { /* empty point */ sprintf(sz, "b-%c%c", fColor ? 'g' : 'r', fUp ? 'd' : 'u'); sprintf(szAlt, " '"); } printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); } static void printHTMLBoardGNU(FILE * pf, matchstate * pms, int fTurn, const char *szImageDir, const char *szExtension, const htmlexportcss hecss) { char sz[1000]; char szAlt[1000]; int i, j; TanBoard anBoard; unsigned int anPips[2]; int acOff[2] = { 15, 15 }; memcpy(anBoard, pms->anBoard, sizeof(anBoard)); if (pms->fMove) SwapSides(anBoard); for (i = 0; i < 2; i++) { for (j = 0; j < 25; j++) acOff[i] -= anBoard[i][j]; } /* top line with board numbers */ fputs("\n", pf); fputs("", pf); fputs("\n", pf); /* display left bearoff tray */ fputs("", pf); fputs("", pf); /* display player 0's outer quadrant */ for (i = 0; i < 6; i++) { fputs("", pf); } /* display cube */ fputs("", pf); /* display player 0's home quadrant */ for (i = 0; i < 6; i++) { fputs("", pf); } /* right bearoff tray */ fputs("", pf); fputs("\n", pf); /* display bar */ fputs("", pf); fputs("", pf); fputs("\n", pf); /* center of board */ fputs("", pf); /* left part of bar */ fputs("", pf); /* center of board */ fputs("", pf); /* centered cube */ if (pms->fCubeOwner == -1 && !pms->fDoubled) { sprintf(sz, "b-midc-%d", pms->nCube); sprintf(szAlt, "|%2d |", pms->nCube); } else { strcpy(sz, "b-midc"); strcpy(szAlt, "|   |"); } fputs("", pf); /* player 1 */ fputs("", pf); /* right part of bar */ fputs("", pf); fputs("\n", pf); /* display left bearoff tray */ fputs("", pf); fputs("", pf); /* display player 1's outer quadrant */ for (i = 0; i < 6; i++) { fputs("", pf); } /* display bar */ fputs("", pf); /* display player 1's outer quadrant */ for (i = 0; i < 6; i++) { fputs("", pf); } /* right bearoff tray */ fputs("", pf); fputs("\n", pf); /* display cube */ fputs("", pf); fputs("", pf); fputs("\n", pf); /* bottom */ fputs("", pf); fputs("", pf); fputs("", pf); fputs("
", pf); sprintf(sz, "b-%stop%s", fTurn ? "hi" : "lo", fClockwise ? "" : "r"); printImage(pf, szImageDir, sz, szExtension, fClockwise ? (fTurn ? "+-24-23-22-21-20-19-+---+-18-17-16-15-14-13-+" : "+--1--2--3--4--5--6-+---+--7--8--9-10-11-12-+") : (fTurn ? "+-13-14-15-16-17-18-+---+-19-20-21-22-23-24-+" : "+-12-11-10--9--8--7-+---+--6--5--4--3--2--1-+"), hecss, HTML_EXPORT_TYPE_GNU); fputs("
", pf); if (fClockwise) /* Use roff as loff not generated (and probably not needed) */ sprintf(sz, "b-roff-x%d", acOff[1]); else strcpy(sz, "b-loff-x0"); printImage(pf, szImageDir, sz, szExtension, "|", hecss, HTML_EXPORT_TYPE_GNU); fputs("", pf); if (fClockwise) printPointGNU(pf, szImageDir, szExtension, anBoard[1][i], anBoard[0][23 - i], !(i % 2), TRUE, hecss); else printPointGNU(pf, szImageDir, szExtension, anBoard[1][11 - i], anBoard[0][12 + i], !(i % 2), TRUE, hecss); fputs("", pf); if (!pms->fCubeOwner) { sprintf(sz, "b-ct-%d", pms->nCube); sprintf(szAlt, "|%2d |", pms->nCube); } else { strcpy(sz, "b-ct"); strcpy(szAlt, "|   |"); } printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); fputs("", pf); if (fClockwise) printPointGNU(pf, szImageDir, szExtension, anBoard[1][i + 6], anBoard[0][17 - i], !(i % 2), TRUE, hecss); else printPointGNU(pf, szImageDir, szExtension, anBoard[1][5 - i], anBoard[0][18 + i], !(i % 2), TRUE, hecss); fputs("", pf); if (!fClockwise) sprintf(sz, "b-roff-x%d", acOff[1]); else strcpy(sz, "b-roff-x0"); printImage(pf, szImageDir, sz, szExtension, "|", hecss, HTML_EXPORT_TYPE_GNU); fputs("
", pf); sprintf(sz, "b-bar-o%u", anBoard[1][24]); if (anBoard[1][24]) sprintf(szAlt, "|                   " "| %1XX|" "                   |", anBoard[1][24]); else strcpy(szAlt, "|                   " "|   |" "                   |"); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); fputs("
", pf); if (fClockwise) printImage(pf, szImageDir, "b-midlb", szExtension, "|", hecss, HTML_EXPORT_TYPE_GNU); else printImage(pf, szImageDir, fTurn ? "b-midlb-o" : "b-midlb-x", szExtension, "|", hecss, HTML_EXPORT_TYPE_GNU); fputs("", pf); if (!pms->fMove && pms->anDice[0] && pms->anDice[1]) { /* player has rolled the dice */ sprintf(sz, "b-midl-x%u%u", pms->anDice[0], pms->anDice[1]); sprintf(szAlt, "       %u %u       ", pms->anDice[0], pms->anDice[1]); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); } else if (!pms->fMove && pms->fDoubled) { /* player 0 has doubled */ sprintf(sz, "b-midl-c%d", 2 * pms->nCube); sprintf(szAlt, "       [%d]       ", 2 * pms->nCube); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); } else { /* player 0 on roll */ printImage(pf, szImageDir, "b-midl", szExtension, "                 ", hecss, HTML_EXPORT_TYPE_GNU); } fputs("", pf); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); fputs("", pf); if (pms->fMove && pms->anDice[0] && pms->anDice[1]) { /* player 1 has rolled the dice */ sprintf(sz, "b-midr-o%u%u", pms->anDice[0], pms->anDice[1]); sprintf(szAlt, "       %u %u       ", pms->anDice[0], pms->anDice[1]); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); } else if (pms->fMove && pms->fDoubled) { /* player 1 has doubled */ sprintf(sz, "b-midr-c%d", 2 * pms->nCube); sprintf(szAlt, "       [%d]       ", 2 * pms->nCube); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); } else { /* player 1 on roll */ printImage(pf, szImageDir, "b-midr", szExtension, "                 ", hecss, HTML_EXPORT_TYPE_GNU); } fputs("", pf); if (!fClockwise) printImage(pf, szImageDir, "b-midrb", szExtension, "|", hecss, HTML_EXPORT_TYPE_GNU); else printImage(pf, szImageDir, fTurn ? "b-midrb-o" : "b-midrb-x", szExtension, "|", hecss, HTML_EXPORT_TYPE_GNU); fputs("
", pf); if (fClockwise) /* Use roff as loff not generated (and probably not needed) */ sprintf(sz, "b-roff-o%d", acOff[0]); else strcpy(sz, "b-loff-o0"); printImage(pf, szImageDir, sz, szExtension, "|", hecss, HTML_EXPORT_TYPE_GNU); fputs("", pf); if (fClockwise) printPointGNU(pf, szImageDir, szExtension, anBoard[1][23 - i], anBoard[0][i], i % 2, FALSE, hecss); else printPointGNU(pf, szImageDir, szExtension, anBoard[1][12 + i], anBoard[0][11 - i], i % 2, FALSE, hecss); fputs("", pf); sprintf(sz, "b-bar-x%u", anBoard[0][24]); if (pms->fCubeOwner == 1) sprintf(szAlt, "|%2d |", pms->nCube); else strcpy(szAlt, "|   |"); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); fputs("", pf); if (fClockwise) printPointGNU(pf, szImageDir, szExtension, anBoard[1][17 - i], anBoard[0][i + 6], i % 2, FALSE, hecss); else printPointGNU(pf, szImageDir, szExtension, anBoard[1][18 + i], anBoard[0][5 - i], i % 2, FALSE, hecss); fputs("", pf); if (!fClockwise) sprintf(sz, "b-roff-o%d", acOff[0]); else strcpy(sz, "b-roff-o0"); printImage(pf, szImageDir, sz, szExtension, "|", hecss, HTML_EXPORT_TYPE_GNU); fputs("
", pf); if (pms->fCubeOwner == 1) sprintf(sz, "b-cb-%d", pms->nCube); else strcpy(sz, "b-cb"); if (anBoard[0][24]) /* text for alt tag - bottom bar piece */ sprintf(szAlt, "|                   " "| %1XO|" "                   |", anBoard[0][24]); else strcpy(szAlt, ""); printImage(pf, szImageDir, sz, szExtension, szAlt, hecss, HTML_EXPORT_TYPE_GNU); fputs("
", pf); sprintf(sz, "b-%sbot%s", fTurn ? "lo" : "hi", fClockwise ? "" : "r"); printImage(pf, szImageDir, sz, szExtension, fClockwise ? (fTurn ? "+--1--2--3--4--5--6-+---+--7--8--9-10-11-12-+" : "+-24-23-22-21-20-19-+---+-18-17-16-15-14-13-+") : (fTurn ? "+-12-11-10--9--8--7-+---+--6--5--4--3--2--1-+" : "+-13-14-15-16-17-18-+---+-19-20-21-22-23-24-+"), hecss, HTML_EXPORT_TYPE_GNU); fputs("
\n\n", pf); /* pip counts */ fputs("

", pf); PipCount((ConstTanBoard) anBoard, anPips); fprintf(pf, "%s %s %u, %s %u
\n", _("Pip counts:"), ap[0].szName, anPips[1], ap[1].szName, anPips[0]); /* position ID */ fprintf(pf, "", GetStyle(CLASS_POSITIONID, hecss)); fprintf(pf, _("Position ID: %s Match ID: %s
\n"), PositionID((ConstTanBoard) pms->anBoard), MatchIDFromMatchState(pms)); fprintf(pf, "
"); fputs("

\n", pf); } static void printHTMLBoard(FILE * pf, matchstate * pms, int fTurn, const char *szImageDir, const char *szExtension, const htmlexporttype het, const htmlexportcss hecss) { fputs("\n\n\n", pf); switch (het) { case HTML_EXPORT_TYPE_FIBS2HTML: printHTMLBoardF2H(pf, pms, fTurn, szImageDir, szExtension, hecss); break; case HTML_EXPORT_TYPE_BBS: printHTMLBoardBBS(pf, pms, fTurn, szImageDir, szExtension, hecss); break; case HTML_EXPORT_TYPE_GNU: printHTMLBoardGNU(pf, pms, fTurn, szImageDir, szExtension, hecss); break; default: printf(_("unknown board type\n")); break; } fputs("\n\n\n", pf); } /* * Print html header for board: move or cube decision * * Input: * pf: output file * ms: current match state * iMove: move no. * */ static void HTMLBoardHeader(FILE * pf, const matchstate * pms, const htmlexporttype UNUSED(het), const htmlexportcss UNUSED(hecss), const int iGame, const int iMove, const int fHR) { fputs("\n\n\n", pf); if (fHR) fputs("
\n", pf); fputs("

", pf); if (iMove >= 0) { fprintf(pf, "" "", iGame + 1, iMove + 1); fprintf(pf, _("Move number %d:"), iMove + 1); fputs("", pf); } if (pms->fResigned) /* resignation */ fprintf(pf, ngettext(" %s resigns %d point", " %s resigns %d points", pms->fResigned * pms->nCube), ap[pms->fTurn].szName, pms->fResigned * pms->nCube); else if (pms->anDice[0] && pms->anDice[1]) /* chequer play decision */ fprintf(pf, _(" %s to play %u%u"), ap[pms->fMove].szName, pms->anDice[0], pms->anDice[1] ); else if (pms->fDoubled) /* take decision */ fprintf(pf, _(" %s doubles to %d"), ap[!pms->fTurn].szName, pms->nCube * 2); else /* cube decision */ fprintf(pf, _(" %s on roll, cube decision?"), ap[pms->fMove].szName); fputs("

\n", pf); fputs("\n\n\n", pf); } /* * Print html header: dtd, head etc. * * Input: * pf: output file * ms: current match state * */ static void HTMLPrologue(FILE * pf, const matchstate * pms, const int iGame, char *aszLinks[4], const htmlexporttype UNUSED(het), const htmlexportcss hecss) { GString *gszTitle; int i; int fFirst; /* DTD */ gszTitle = g_string_new(NULL); g_string_printf(gszTitle, ngettext("The score (after %d game) is: %s %d, %s %d", "The score (after %d games) is: %s %d, %s %d", pms->cGames), pms->cGames, ap[0].szName, pms->anScore[0], ap[1].szName, pms->anScore[1]); if (pms->nMatchTo > 0) g_string_append_printf(gszTitle, ngettext(" (match to %d point%s)", " (match to %d points%s)", pms->nMatchTo), pms->nMatchTo, (pms->nMatchTo > 1 && pms->fCrawford) ? _(", Crawford game") : (pms->nMatchTo > 1 && pms->fPostCrawford) ? _(", post-Crawford play") : ""); fprintf(pf, "\n" "\n" "\n" "\n" "\n" "\n" "nMatchTo) ? _("match play") : _("money game")); fprintf(pf, _("%s (analysed by %s)"), gszTitle->str, VERSION_STRING); fprintf(pf, "\"/>\n" "%s\n", gszTitle->str); if (hecss == HTML_EXPORT_CSS_HEAD) WriteStyleSheet(pf, hecss); else if (hecss == HTML_EXPORT_CSS_EXTERNAL) fputs("\n", pf); fprintf(pf, "\n" "\n" "\n" "

", GetStyle(CLASS_FONT_FAMILY, hecss)); fprintf(pf, _("Game number %d"), iGame + 1); fprintf(pf, "

\n" "

%s

\n", gszTitle->str); g_string_free(gszTitle, TRUE); /* add links to other games */ fFirst = TRUE; for (i = 0; i < 4; i++) if (aszLinks && aszLinks[i]) { if (fFirst) { fprintf(pf, "
\n"); fputs("

\n", pf); fFirst = FALSE; } fprintf(pf, "%s\n", aszLinks[i], gettext(aszLinkText[i])); } if (!fFirst) fputs("

\n", pf); } /* * Print full HTML footer * Used for export to file */ static void HTMLEpilogue(FILE * pf, const matchstate * UNUSED(pms), char *aszLinks[4], const htmlexportcss UNUSED(hecss)) { time_t t; int fFirst; int i; char tstr[11]; /* ISO 8601 date format: YYYY-MM-DD\0 */ fputs("\n\n\n", pf); /* add links to other games */ fFirst = TRUE; for (i = 0; i < 4; i++) if (aszLinks && aszLinks[i]) { if (fFirst) { fprintf(pf, "
\n"); fputs("

\n", pf); fFirst = FALSE; } fprintf(pf, "%s\n", aszLinks[i], gettext(aszLinkText[i])); } if (!fFirst) fputs("

\n", pf); time(&t); strftime(tstr, 11, "%Y-%m-%d", localtime(&t)); fputs("
\n" "
", pf); fprintf(pf, _("Output generated %s by " "%s"), tstr, VERSION_STRING); fprintf(pf, "
\n" "

\n" "" "\n" "" "" "\n" "

\n" "\n" "\n", _("Valid XHTML 1.0 Strict!"), _("Valid CSS!")); } /* * Print short HTML footer. * Used for copy to GammOnLine */ static void HTMLEpilogueComment(FILE * pf) { time_t t; char tstr[11]; /* ISO 8601 date format: YYYY-MM-DD\0 */ time(&t); strftime(tstr, 11, "%Y-%m-%d", localtime(&t)); fputs("\n\n\n", pf); fprintf(pf, _("", pf); } /* * Print cube analysis * * Input: * pf: output file * arDouble: equitites for cube decisions * fPlayer: player who doubled * esDouble: eval setup * pci: cubeinfo * fDouble: double/no double * fTake: take/drop * */ static void HTMLPrintCubeAnalysisTable(FILE * pf, float aarOutput[2][NUM_ROLLOUT_OUTPUTS], float aarStdDev[2][NUM_ROLLOUT_OUTPUTS], int UNUSED(fPlayer), evalsetup * pes, cubeinfo * pci, int fDouble, int fTake, skilltype stDouble, skilltype stTake, const htmlexportcss hecss) { const char *aszCube[] = { NULL, N_("No double"), N_("Double, take"), N_("Double, pass") }; float arDouble[4]; int i; int ai[3]; float r; int fActual, fClose, fMissed; int fDisplayIt; int fAnno = FALSE; cubedecision cd; /* check if cube analysis should be printed */ if (pes->et == EVAL_NONE) return; /* no evaluation */ cd = FindCubeDecision(arDouble, aarOutput, pci); fActual = fDouble > 0; fClose = isCloseCubedecision(arDouble); fMissed = fDouble > -1 && isMissedDouble(arDouble, aarOutput, fDouble, pci); fDisplayIt = (fActual && exsExport.afCubeDisplay[EXPORT_CUBE_ACTUAL]) || (fClose && exsExport.afCubeDisplay[EXPORT_CUBE_CLOSE]) || (fMissed && exsExport.afCubeDisplay[EXPORT_CUBE_MISSED]) || (exsExport.afCubeDisplay[stDouble]) || (exsExport.afCubeDisplay[stTake]); if (!fDisplayIt) return; fputs("\n\n\n", pf); if (fTake >= 0) { /* take or drop */ InvertEvaluationR(aarOutput[0], pci); InvertEvaluationR(aarOutput[1], pci); if (pes->et == EVAL_ROLLOUT) { InvertStdDev(aarStdDev[0]); InvertStdDev(aarStdDev[1]); } } /* print alerts */ if (fMissed) { fAnno = TRUE; /* missed double */ fprintf(pf, "

%s (%s)!", GetStyle(CLASS_BLUNDER, hecss), _("Alert: missed double"), OutputEquityDiff(arDouble[OUTPUT_NODOUBLE], (arDouble[OUTPUT_TAKE] > arDouble[OUTPUT_DROP]) ? arDouble[OUTPUT_DROP] : arDouble[OUTPUT_TAKE], pci)); if (badSkill(stDouble)) fprintf(pf, " [%s]", gettext(aszSkillType[stDouble])); fprintf(pf, "

\n"); } r = arDouble[OUTPUT_TAKE] - arDouble[OUTPUT_DROP]; if (fTake > 0 && r > 0.0f) { fAnno = TRUE; /* wrong take */ fprintf(pf, "

%s (%s)!", GetStyle(CLASS_BLUNDER, hecss), _("Alert: wrong take"), OutputEquityDiff(arDouble[OUTPUT_DROP], arDouble[OUTPUT_TAKE], pci)); if (badSkill(stTake)) fprintf(pf, " [%s]", gettext(aszSkillType[stTake])); fprintf(pf, "

\n"); } r = arDouble[OUTPUT_DROP] - arDouble[OUTPUT_TAKE]; if (fDouble > 0 && !fTake && r > 0.0f) { fAnno = TRUE; /* wrong pass */ fprintf(pf, "

%s (%s)!", GetStyle(CLASS_BLUNDER, hecss), _("Alert: wrong pass"), OutputEquityDiff(arDouble[OUTPUT_TAKE], arDouble[OUTPUT_DROP], pci)); if (badSkill(stTake)) fprintf(pf, " [%s]", gettext(aszSkillType[stTake])); fprintf(pf, "

\n"); } if (arDouble[OUTPUT_TAKE] > arDouble[OUTPUT_DROP]) r = arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_DROP]; else r = arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_TAKE]; if (fDouble > 0 && fTake < 0 && r > 0.0f) { fAnno = TRUE; /* wrong double */ fprintf(pf, "

%s (%s)!", GetStyle(CLASS_BLUNDER, hecss), _("Alert: wrong double"), OutputEquityDiff((arDouble[OUTPUT_TAKE] > arDouble[OUTPUT_DROP]) ? arDouble[OUTPUT_DROP] : arDouble[OUTPUT_TAKE], arDouble[OUTPUT_NODOUBLE], pci)); if (badSkill(stDouble)) fprintf(pf, " [%s]", gettext(aszSkillType[stDouble])); fprintf(pf, "

\n"); } if ((badSkill(stDouble) || badSkill(stTake)) && !fAnno) { if (badSkill(stDouble)) { fprintf(pf, "

", GetStyle(CLASS_BLUNDER, hecss)); fprintf(pf, _("Alert: double decision marked %s"), gettext(aszSkillType[stDouble])); fputs("

\n", pf); } if (badSkill(stTake)) { fprintf(pf, "

", GetStyle(CLASS_BLUNDER, hecss)); fprintf(pf, _("Alert: take decision marked %s"), gettext(aszSkillType[stTake])); fputs("

\n", pf); } } /* print table */ fprintf(pf, "\n", GetStyle(CLASS_CUBEDECISION, hecss)); /* header */ fprintf(pf, "\n", GetStyle(CLASS_CUBEDECISIONHEADER, hecss), _("Cube decision")); /* ply & cubeless equity */ /* FIXME: about parameters if exsExport.afCubeParameters */ fprintf(pf, ""); /* ply */ fprintf(pf, "" "" "\n", (!pci->nMatchTo || !fOutputMWC) ? _("cubeless equity") : _("cubeless MWC"), GetStyle(CLASS_CUBE_EQUITY, hecss), OutputEquity(aarOutput[0][OUTPUT_EQUITY], pci, TRUE), GetStyle(CLASS_CUBE_CUBELESS_TEXT, hecss), _("Money"), GetStyle(CLASS_CUBE_EQUITY, hecss), OutputMoneyEquity(aarOutput[0], TRUE)); else fprintf(pf, " %s\n", _("cubeless equity"), OutputMoneyEquity(aarOutput[0], TRUE)); fprintf(pf, "\n"); /* percentages */ if (exsExport.fCubeDetailProb && pes->et == EVAL_EVAL) { fprintf(pf, "" "\n", pf); } /* equities */ fprintf(pf, "\n", _("Cubeful equities:")); /* evaluate parameters */ if (pes->et == EVAL_EVAL && exsExport.afCubeParameters[0]) { fputs("" "\n", pf); } getCubeDecisionOrdering(ai, arDouble, aarOutput, pci); for (i = 0; i < 3; i++) { fprintf(pf, "", i + 1, gettext(aszCube[ai[i]])); if (fTake == -1) { /* double */ fprintf(pf, "", GetStyle(CLASS_CUBE_EQUITY, hecss), OutputEquity(arDouble[ai[i]], pci, TRUE)); if (i) fprintf(pf, "", OutputEquityDiff(arDouble[ai[i]], arDouble[OUTPUT_OPTIMAL], pci)); else fputs("", pf); } else { /* take or drop */ fprintf(pf, "", GetStyle(CLASS_CUBE_EQUITY, hecss), OutputEquity(-arDouble[ai[i]], pci, TRUE)); if (i) fprintf(pf, "", OutputEquityDiff(-arDouble[ai[i]], -arDouble[OUTPUT_OPTIMAL], pci)); else fputs("", pf); } fputs("\n", pf); } /* cube decision */ fprintf(pf, "" "\n"); /* rollout details */ if (pes->et == EVAL_ROLLOUT && (exsExport.fCubeDetailProb || exsExport.afCubeParameters[1])) { fprintf(pf, "\n", _("Rollout details")); } if (pes->et == EVAL_ROLLOUT && exsExport.fCubeDetailProb) { char asz[2][1024]; cubeinfo aci[2]; for (i = 0; i < 2; i++) { memcpy(&aci[i], pci, sizeof(cubeinfo)); if (i) { aci[i].fCubeOwner = !pci->fMove; aci[i].nCube *= 2; } FormatCubePosition(asz[i], &aci[i]); } fputs("" "\n", pf); } if (fTake >= 0) { /* take or drop */ InvertEvaluationR(aarOutput[0], pci); InvertEvaluationR(aarOutput[1], pci); if (pes->et == EVAL_ROLLOUT) { InvertStdDev(aarStdDev[0]); InvertStdDev(aarStdDev[1]); } } if (pes->et == EVAL_ROLLOUT && exsExport.afCubeParameters[1]) { char *sz = g_strdup(OutputRolloutContext(NULL, &pes->rc)); char *pcS = sz, *pcE; while ((pcE = strstr(pcS, "\n"))) { *pcE = 0; fprintf(pf, "\n", pcS); pcS = pcE + 1; } g_free(sz); } fprintf(pf, "
%s
" "", GetStyle(CLASS_CUBE_PLY, hecss)); switch (pes->et) { case EVAL_NONE: fputs(_("n/a"), pf); break; case EVAL_EVAL: fprintf(pf, _("%d-ply"), pes->ec.nPlies); break; case EVAL_ROLLOUT: fputs(_("Rollout"), pf); break; } /* cubeless equity */ if (pci->nMatchTo) fprintf(pf, " %s%s(%s: %s)%s
", GetStyle(CLASS_CUBE_PROBABILITIES, hecss)); fputs(OutputPercents(aarOutput[0], TRUE), pf); fputs("
%s
", pf); fputs(OutputEvalContext(&pes->ec, FALSE), pf); fputs("
%d.%s%s%s%s%s
%s%s", _("Proper cube action:"), GetStyle(CLASS_CUBE_ACTION, hecss), GetCubeRecommendation(cd)); if ((r = getPercent(cd, arDouble)) >= 0.0f) fprintf(pf, " (%.1f%%)", 100.0f * r); fprintf(pf, "
%s
", pf); printRolloutTable(pf, asz, aarOutput, aarStdDev, aci, 2, pes->rc.fCubeful, TRUE, hecss); fputs("
%s
\n"); fputs("

\n", pf); fputs("\n\n\n", pf); } /* * Wrapper for print cube analysis * * Input: * pf: output file * pms: match state * pmr: current move record * szImageDir: URI to images * szExtension: extension of images * fTake: take/drop * */ static void HTMLPrintCubeAnalysis(FILE * pf, matchstate * pms, moverecord * pmr, const char *UNUSED(szImageDir), const char *UNUSED(szExtension), const htmlexporttype UNUSED(het), const htmlexportcss hecss) { cubeinfo ci; /* we need to remember the double type to be able to do the right * thing for beavers and racoons */ static doubletype dt = DT_NORMAL; GetMatchStateCubeInfo(&ci, pms); switch (pmr->mt) { case MOVE_NORMAL: /* cube analysis from move */ HTMLPrintCubeAnalysisTable(pf, pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, pmr->fPlayer, &pmr->CubeDecPtr->esDouble, &ci, FALSE, -1, pmr->stCube, SKILL_NONE, hecss); dt = DT_NORMAL; break; case MOVE_DOUBLE: dt = DoubleType(pms->fDoubled, pms->fMove, pms->fTurn); if (dt != DT_NORMAL) { fprintf(pf, "

%s

\n", GetStyle(CLASS_BLUNDER, hecss), _("Cannot analyse beaver nor raccoons!")); break; } HTMLPrintCubeAnalysisTable(pf, pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, pmr->fPlayer, &pmr->CubeDecPtr->esDouble, &ci, TRUE, -1, pmr->stCube, SKILL_NONE, hecss); break; case MOVE_TAKE: case MOVE_DROP: /* cube analysis from double, {take, drop, beaver} */ if (dt != DT_NORMAL) { dt = DT_NORMAL; fprintf(pf, "

%s

\n", GetStyle(CLASS_BLUNDER, hecss), _("Cannot analyse beaver nor raccoons!")); break; } HTMLPrintCubeAnalysisTable(pf, pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, pmr->fPlayer, &pmr->CubeDecPtr->esDouble, &ci, TRUE, pmr->mt == MOVE_TAKE, SKILL_NONE, /* FIXME: skill from prev. cube */ pmr->stCube, hecss); break; default: g_assert_not_reached(); } return; } /* * Print move analysis * * Input: * pf: output file * pms: match state * pmr: current move record * szImageDir: URI to images * szExtension: extension of images * */ static void HTMLPrintMoveAnalysis(FILE * pf, matchstate * pms, moverecord * pmr, const char *UNUSED(szImageDir), const char *UNUSED(szExtension), const htmlexporttype UNUSED(het), const htmlexportcss hecss) { char sz[FORMATEDMOVESIZE]; cubeinfo ci; GetMatchStateCubeInfo(&ci, pms); /* check if move should be printed */ if (!exsExport.afMovesDisplay[pmr->n.stMove]) return; fputs("\n\n\n", pf); /* print alerts */ if (badSkill(pmr->n.stMove) && pmr->n.iMove < pmr->ml.cMoves) { /* blunder or error */ fprintf(pf, "

", GetStyle(CLASS_BLUNDER, hecss)); fprintf(pf, _("Alert: %s move"), gettext(aszSkillType[pmr->n.stMove])); if (!pms->nMatchTo || !fOutputMWC) fprintf(pf, " (%+*.*f)

\n", fOutputDigits + 4, fOutputDigits, pmr->ml.amMoves[pmr->n.iMove].rScore - pmr->ml.amMoves[0].rScore); else fprintf(pf, " (%+*.*f%%)

\n", fOutputDigits + 3, fOutputDigits, 100.0f * eq2mwc(pmr->ml.amMoves[pmr->n.iMove].rScore, &ci) - 100.0f * eq2mwc(pmr->ml.amMoves[0].rScore, &ci)); } if (pmr->lt != LUCK_NONE) { /* joker */ fprintf(pf, "

", GetStyle(CLASS_JOKER, hecss)); fprintf(pf, _("Alert: %s roll!"), gettext(aszLuckType[pmr->lt])); if (!pms->nMatchTo || !fOutputMWC) fprintf(pf, " (%+*.*f)

\n", fOutputDigits + 4, fOutputDigits, pmr->rLuck); else fprintf(pf, " (%+*.*f%%)

\n", fOutputDigits + 3, fOutputDigits, 100.0f * eq2mwc(pmr->rLuck, &ci) - 100.0f * eq2mwc(0.0f, &ci)); } /* table header */ fprintf(pf, "\n" "\n", GetStyle(CLASS_MOVETABLE, hecss)); fprintf(pf, "\n", GetStyleGeneral((int)hecss, CLASS_MOVEHEADER, CLASS_MOVENUMBER, -1), _("#")); fprintf(pf, "\n", GetStyleGeneral((int)hecss, CLASS_MOVEHEADER, CLASS_MOVEPLY, -1), _("Ply")); fprintf(pf, "\n", GetStyleGeneral((int)hecss, CLASS_MOVEHEADER, CLASS_MOVEMOVE, -1), _("Move")); fprintf(pf, "\n" "\n", GetStyleGeneral((int)hecss, CLASS_MOVEHEADER, CLASS_MOVEEQUITY, -1), (!pms->nMatchTo || !fOutputMWC) ? _("Equity") : _("MWC")); if (pmr->ml.cMoves) { for (unsigned int i = 0; i < pmr->ml.cMoves; i++) { float rEq, rEqTop; if (i >= exsExport.nMoves && i != pmr->n.iMove) continue; if (i == pmr->n.iMove) fprintf(pf, "\n", GetStyle(CLASS_MOVETHEMOVE, hecss)); else if (i % 2) fprintf(pf, "\n", GetStyle(CLASS_MOVEODD, hecss)); else fputs("\n", pf); /* selected move or not */ fprintf(pf, "\n", GetStyle(CLASS_MOVENUMBER, hecss), (i == pmr->n.iMove) ? bullet : " "); /* move no */ if (i != pmr->n.iMove || i != pmr->ml.cMoves - 1 || pmr->ml.cMoves == 1 || i < exsExport.nMoves) fprintf(pf, "\n", GetStyle(CLASS_MOVENUMBER, hecss), i + 1); else fprintf(pf, "\n", GetStyle(CLASS_MOVENUMBER, hecss)); /* ply */ switch (pmr->ml.amMoves[i].esMove.et) { case EVAL_NONE: fprintf(pf, "\n", GetStyle(CLASS_MOVEPLY, hecss)); break; case EVAL_EVAL: fprintf(pf, "\n", GetStyle(CLASS_MOVEPLY, hecss), pmr->ml.amMoves[i].esMove.ec.nPlies); break; case EVAL_ROLLOUT: fprintf(pf, "\n", GetStyle(CLASS_MOVEPLY, hecss)); break; } /* move */ fprintf(pf, "\n", GetStyle(CLASS_MOVEMOVE, hecss), FormatMove(sz, (ConstTanBoard) pms->anBoard, pmr->ml.amMoves[i].anMove)); /* equity */ rEq = pmr->ml.amMoves[i].rScore; rEqTop = pmr->ml.amMoves[0].rScore; if (i) fprintf(pf, "\n", GetStyle(CLASS_MOVEEQUITY, hecss), OutputEquity(rEq, &ci, TRUE), OutputEquityDiff(rEq, rEqTop, &ci)); else fprintf(pf, "\n", GetStyle(CLASS_MOVEEQUITY, hecss), OutputEquity(rEq, &ci, TRUE)); /* end row */ fprintf(pf, "\n"); /* * print row with detailed probabilities */ if (exsExport.fMovesDetailProb) { float *ar = pmr->ml.amMoves[i].arEvalMove; /* percentages */ if (i == pmr->n.iMove) fprintf(pf, "\n", GetStyle(CLASS_MOVETHEMOVE, hecss)); else if (i % 2) fprintf(pf, "\n", GetStyle(CLASS_MOVEODD, hecss)); else fputs("\n", pf); fputs("\n", pf); fputs("\n", pf); fputs("\n", pf); fputs("\n", pf); } /* * Write row with move parameters */ if (exsExport.afMovesParameters[pmr->ml.amMoves[i].esMove.et - 1]) { evalsetup *pes = &pmr->ml.amMoves[i].esMove; switch (pes->et) { case EVAL_EVAL: if (i == pmr->n.iMove) fprintf(pf, "\n", GetStyle(CLASS_MOVETHEMOVE, hecss)); else if (i % 2) fprintf(pf, "\n", GetStyle(CLASS_MOVEODD, hecss)); else fputs("\n", pf); fprintf(pf, "\n"); fputs("\n", pf); fputs("\n", pf); fputs("\n", pf); break; case EVAL_ROLLOUT: { char *szrc = g_strdup(OutputRolloutContext(NULL, &pes->rc)); char *pcS = szrc, *pcE; while ((pcE = strstr(pcS, "\n"))) { *pcE = 0; if (i == pmr->n.iMove) fprintf(pf, "\n", GetStyle(CLASS_MOVETHEMOVE, hecss)); else if (i % 2) fprintf(pf, "\n", GetStyle(CLASS_MOVEODD, hecss)); else fputs("\n", pf); fprintf(pf, "\n"); fputs("\n", pf); fputs("\n", pf); fputs("\n", pf); pcS = pcE + 1; } g_free(szrc); break; } default: break; } } } } else { if (pmr->n.anMove[0] >= 0) /* no movelist saved */ fprintf(pf, "" "\n", GetStyle(CLASS_MOVETHEMOVE, hecss), FormatMove(sz, (ConstTanBoard) pms->anBoard, pmr->n.anMove)); else /* no legal moves */ /* FIXME: output equity?? */ fprintf(pf, "" "\n", GetStyle(CLASS_MOVETHEMOVE, hecss), _("Cannot move")); } fprintf(pf, "
%s%s%s%s
%s%u\?\?n/a%uR%s%s (%s)%s
", pf); switch (pmr->ml.amMoves[i].esMove.et) { case EVAL_EVAL: fputs(OutputPercents(ar, TRUE), pf); break; case EVAL_ROLLOUT: printRolloutTable(pf, NULL, (float (*)[NUM_ROLLOUT_OUTPUTS]) pmr->ml.amMoves[i].arEvalMove, (float (*)[NUM_ROLLOUT_OUTPUTS]) pmr->ml.amMoves[i].arEvalStdDev, &ci, 1, pmr->ml.amMoves[i].esMove.rc.fCubeful, FALSE, hecss); break; default: break; } fputs("
", pf); fputs(OutputEvalContext(&pes->ec, TRUE), pf); fputs("
", pf); fputs(pcS, pf); fputs("
%s
%s
\n"); fputs("\n\n\n", pf); return; } /* * Print cube analysis * * Input: * pf: output file * pms: match state * pmr: current move record * szImageDir: URI to images * szExtension: extension of images * */ static void HTMLAnalysis(FILE * pf, matchstate * pms, moverecord * pmr, const char *szImageDir, const char *szExtension, const htmlexporttype het, const htmlexportcss hecss) { switch (pmr->mt) { case MOVE_NORMAL: fprintf(pf, "

"); if (het == HTML_EXPORT_TYPE_FIBS2HTML) printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, het); if (pmr->n.anMove[0] >= 0) { char sz[FORMATEDMOVESIZE]; fprintf(pf, _("%s%s moves %s"), bullet, ap[pmr->fPlayer].szName, FormatMove(sz, (ConstTanBoard) pms->anBoard, pmr->n.anMove)); } else if (!pmr->ml.cMoves) fprintf(pf, _("%s%s cannot move"), bullet, ap[pmr->fPlayer].szName); fputs("

\n", pf); /* HTMLRollAlert ( pf, pms, pmr, szImageDir, szExtension ); */ if (exsExport.fIncludeAnalysis) { HTMLPrintCubeAnalysis(pf, pms, pmr, szImageDir, szExtension, het, hecss); HTMLPrintMoveAnalysis(pf, pms, pmr, szImageDir, szExtension, het, hecss); } break; case MOVE_TAKE: case MOVE_DROP: case MOVE_DOUBLE: fprintf(pf, "

"); if (het == HTML_EXPORT_TYPE_FIBS2HTML) printImage(pf, szImageDir, "b-indent", szExtension, "", hecss, het); if (pmr->mt == MOVE_DOUBLE) fprintf(pf, "%s%s %s

\n", bullet, ap[pmr->fPlayer].szName, _("doubles")); else fprintf(pf, "%s%s %s

\n", bullet, ap[pmr->fPlayer].szName, (pmr->mt == MOVE_TAKE) ? _("accepts") : _("rejects")); if (exsExport.fIncludeAnalysis) HTMLPrintCubeAnalysis(pf, pms, pmr, szImageDir, szExtension, het, hecss); break; default: break; } } /* * Dump statcontext * * Input: * pf: output file * statcontext to dump * */ static void HTMLDumpStatcontext(FILE * pf, const statcontext * psc, int nMatchTo, const int iGame, const htmlexportcss hecss, const gchar * header) { fprintf(pf, "\n\n\n", (iGame >= 0) ? "Game" : "Match"); fprintf(pf, "\n", GetStyle(CLASS_STATTABLE, hecss)); printStatTableHeader(pf, hecss, header); fprintf(pf, "\n" "\n" "\n", GetStyle(CLASS_STATTABLEHEADER, hecss), _("Player"), ap[0].szName, ap[1].szName); /* checker play */ if (psc->fMoves) { GList *list = formatGS(psc, nMatchTo, FORMATGS_CHEQUER); GList *pl; printStatTableHeader(pf, hecss, _("Chequer play statistics")); for (pl = g_list_first(list); pl; pl = g_list_next(pl)) { char **aasz = pl->data; printStatTableRow(pf, aasz[0], "%s", aasz[1], aasz[2]); } freeGS(list); } /* dice stat */ if (psc->fDice) { GList *list = formatGS(psc, nMatchTo, FORMATGS_LUCK); GList *pl; printStatTableHeader(pf, hecss, _("Luck statistics")); for (pl = g_list_first(list); pl; pl = g_list_next(pl)) { char **aasz = pl->data; printStatTableRow(pf, aasz[0], "%s", aasz[1], aasz[2]); } freeGS(list); } /* cube statistics */ if (psc->fCube) { GList *list = formatGS(psc, nMatchTo, FORMATGS_CUBE); GList *pl; printStatTableHeader(pf, hecss, _("Cube statistics")); for (pl = g_list_first(list); pl; pl = g_list_next(pl)) { char **aasz = pl->data; printStatTableRow(pf, aasz[0], "%s", aasz[1], aasz[2]); } freeGS(list); } /* overall rating */ { GList *list = formatGS(psc, nMatchTo, FORMATGS_OVERALL); GList *pl; printStatTableHeader(pf, hecss, _("Overall statistics")); for (pl = g_list_first(list); pl; pl = g_list_next(pl)) { char **aasz = pl->data; printStatTableRow(pf, aasz[0], "%s", aasz[1], aasz[2]); } freeGS(list); } fprintf(pf, "
%s%s%s
\n"); fprintf(pf, "\n\n\n", (iGame >= 0) ? "Game" : "Match"); } static void HTMLPrintComment(FILE * pf, const moverecord * pmr, const htmlexportcss hecss) { char *sz = pmr->sz; if (sz) { fputs("\n\n", pf); fprintf(pf, "

\n" "
", GetStyle(CLASS_COMMENTHEADER, hecss)); fputs(_("Annotation"), pf); fputs("
\n", pf); fprintf(pf, "
", GetStyle(CLASS_COMMENT, hecss)); while (*sz) { if (*sz == '\n') fputs("
\n", pf); else fputc(*sz, pf); sz++; } fputs("
\n\n", pf); fputs("\n\n", pf); } } static void HTMLPrintMI(FILE * pf, const char *szTitle, const char *sz) { gchar **ppch; gchar *pchToken; int i; if (!sz || !*sz) return; fprintf(pf, "" "%s", szTitle); ppch = g_strsplit(sz, "\n", -1); for (i = 0; (pchToken = ppch[i]); ++i) { if (i) fputs("
\n", pf); fputs(pchToken, pf); } g_strfreev(ppch); fputs("\n", pf); } static void HTMLMatchInfo(FILE * pf, const matchinfo * pmi, const htmlexportcss UNUSED(hecss)) { /* the fields below are not used here but are read * inside strftime(), so initialise them. */ struct tm tmx = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_isdst=-1 }; if (!pmi->nYear && !pmi->pchRating[0] && !pmi->pchRating[1] && !pmi->pchEvent && !pmi->pchRound && !pmi->pchPlace && !pmi->pchAnnotator && !pmi->pchComment) /* no match information -- don't print anything */ return; fputs("\n\n\n", pf); fputs("
", pf); fprintf(pf, "

%s

\n", _("Match Information")); fputs("\n", pf); /* ratings */ for (int i = 0; i < 2; ++i) if (pmi->pchRating[i]) { gchar *pch = g_strdup_printf(_("%s's rating"), ap[i].szName); HTMLPrintMI(pf, pch, pmi->pchRating[i] ? pmi->pchRating[i] : _("n/a")); g_free(pch); } /* date */ if (pmi->nYear) { char sz[80]; tmx.tm_year = pmi->nYear - 1900; tmx.tm_mon = pmi->nMonth - 1; tmx.tm_mday = pmi->nDay; strftime(sz, sizeof(sz), "%x", &tmx); HTMLPrintMI(pf, _("Date"), sz); } /* event, round, place and annotator */ HTMLPrintMI(pf, _("Event"), pmi->pchEvent); HTMLPrintMI(pf, _("Round"), pmi->pchRound); HTMLPrintMI(pf, _("Place"), pmi->pchPlace); HTMLPrintMI(pf, _("Annotator"), pmi->pchAnnotator); HTMLPrintMI(pf, _("Comment"), pmi->pchComment); fputs("
\n", pf); fputs("\n\n\n", pf); } /* * Export a game in HTML * * Input: * pf: output file * plGame: list of moverecords for the current game * */ static void ExportGameHTML(FILE * pf, listOLD * plGame, const char *szImageDir, const char *szExtension, const htmlexporttype het, const htmlexportcss hecss, const int iGame, const int fLastGame, char *aszLinks[4]) { listOLD *pl; moverecord *pmr; matchstate msExport; matchstate msOrig; int iMove = 0; statcontext *psc = NULL; static statcontext scTotal; xmovegameinfo *pmgi = NULL; listOLD *pl_hint = NULL; statcontext *psc_rel = NULL; msOrig.nMatchTo = 0; if (!iGame) IniStatcontext(&scTotal); updateStatisticsGame(plGame); if (game_is_last(plGame)) pl_hint = game_add_pmr_hint(plGame); for (pl = plGame->plNext; pl != plGame; pl = pl->plNext) { pmr = pl->p; FixMatchState(&msExport, pmr); switch (pmr->mt) { case MOVE_GAMEINFO: ApplyMoveRecord(&msExport, plGame, pmr); HTMLPrologue(pf, &msExport, iGame, aszLinks, het, hecss); if (exsExport.fIncludeMatchInfo) HTMLMatchInfo(pf, &mi, hecss); msOrig = msExport; pmgi = &pmr->g; psc = &pmr->g.sc; AddStatcontext(psc, &scTotal); /* FIXME: game introduction */ break; case MOVE_NORMAL: if (pmr->fPlayer != msExport.fMove) SwapSides(msExport.anBoard); msExport.fTurn = msExport.fMove = pmr->fPlayer; msExport.anDice[0] = pmr->anDice[0]; msExport.anDice[1] = pmr->anDice[1]; HTMLBoardHeader(pf, &msExport, het, hecss, iGame, iMove, TRUE); printHTMLBoard(pf, &msExport, msExport.fTurn, szImageDir, szExtension, het, hecss); HTMLAnalysis(pf, &msExport, pmr, szImageDir, szExtension, het, hecss); iMove++; break; case MOVE_DOUBLE: case MOVE_TAKE: case MOVE_DROP: HTMLBoardHeader(pf, &msExport, het, hecss, iGame, iMove, TRUE); printHTMLBoard(pf, &msExport, msExport.fTurn, szImageDir, szExtension, het, hecss); HTMLAnalysis(pf, &msExport, pmr, szImageDir, szExtension, het, hecss); iMove++; break; default: break; } if (exsExport.fIncludeAnnotation) HTMLPrintComment(pf, pmr, hecss); ApplyMoveRecord(&msExport, plGame, pmr); } if (pl_hint) game_remove_pmr_hint(pl_hint); if (pmgi && pmgi->fWinner != -1) { /* print game result */ fprintf(pf, ngettext("

%s wins %d point

\n", "

%s wins %d points

\n", pmgi->nPoints), GetStyle(CLASS_RESULT, hecss), ap[pmgi->fWinner].szName, pmgi->nPoints); } if (psc) { char *header = g_strdup_printf(_("Game statistics for game %d"), iGame + 1); HTMLDumpStatcontext(pf, psc, msOrig.nMatchTo, iGame, hecss, header); g_free(header); } if (fLastGame) { const gchar *header; /* match statistics */ header = ms.nMatchTo ? _("Match statistics") : _("Session statistics"); fprintf(pf, "
\n"); HTMLDumpStatcontext(pf, &scTotal, msOrig.nMatchTo, -1, hecss, header); psc_rel = relational_player_stats_get(ap[0].szName, ap[1].szName); if (psc_rel) { HTMLDumpStatcontext(pf, psc_rel, 0, -1, hecss, _("Statistics from database")); g_free(psc_rel); } } HTMLEpilogue(pf, &msExport, aszLinks, hecss); } /* * Open file gnubg.css with same path as requested html file * * If the gnubg.css file already exists NULL is returned * (and gnubg.css is NOT overwritten) * */ static FILE * OpenCSS(const char *sz) { gchar *pch = g_strdup(sz); gchar *pchBase = g_path_get_dirname(pch); gchar *pchCSS; FILE *pf; pchCSS = g_build_filename(pchBase, "gnubg.css", NULL); if (g_file_test(pchCSS, G_FILE_TEST_EXISTS)) { outputf(_("gnubg.css is not written since it already exist in \"%s\"\n"), pchBase); pf = NULL; } else if (!(pf = g_fopen(pchCSS, "w"))) { outputerr(pchCSS); } g_free(pch); g_free(pchBase); g_free(pchCSS); return pf; } static void check_for_html_images(gchar * path) { char *url = exsExport.szHTMLPictureURL; if (url && g_path_is_absolute(url)) { if (!g_file_test(url, G_FILE_TEST_EXISTS)) CommandExportHTMLImages(url); } else { gchar *folder = g_path_get_dirname(path); gchar *img = g_build_filename(folder, url, NULL); if (!g_file_test(img, G_FILE_TEST_EXISTS)) CommandExportHTMLImages(img); g_free(img); g_free(folder); } } extern void CommandExportGameHtml(char *sz) { FILE *pf; int fDontClose = FALSE; sz = NextToken(&sz); if (!plGame) { outputl(_("No game in progress (type `new game' to start one).")); return; } if (!sz || !*sz) { outputl(_("You must specify a file to export to (see `help export " "game html').")); return; } if (!confirmOverwrite(sz, fConfirmSave)) return; if (!strcmp(sz, "-")) { pf = stdout; fDontClose = TRUE; } else if (!(pf = g_fopen(sz, "w"))) { outputerr(sz); return; } if (exsExport.het == HTML_EXPORT_TYPE_GNU) check_for_html_images(sz); ExportGameHTML(pf, plGame, exsExport.szHTMLPictureURL, exsExport.szHTMLExtension, exsExport.het, exsExport.hecss, getGameNumber(plGame), FALSE, NULL); if (!fDontClose) fclose(pf); setDefaultFileName(sz); /* external stylesheet */ if (exsExport.hecss == HTML_EXPORT_CSS_EXTERNAL) if ((pf = OpenCSS(sz))) { WriteStyleSheet(pf, exsExport.hecss); fclose(pf); } } extern void CommandExportMatchHtml(char *sz) { FILE *pf; listOLD *pl; int nGames; char *aszLinks[4], *filenames[4]; int i, j; sz = NextToken(&sz); if (!sz || !*sz) { outputl(_("You must specify a file to export to (see `help export " "match html').")); return; } if (exsExport.het == HTML_EXPORT_TYPE_GNU) check_for_html_images(sz); /* Find number of games in match */ for (pl = lMatch.plNext, nGames = 0; pl != &lMatch; pl = pl->plNext, nGames++); for (pl = lMatch.plNext, i = 0; pl != &lMatch; pl = pl->plNext, i++) { char *szCurrent = filename_from_iGame(sz, i); filenames[0] = filename_from_iGame(sz, 0); aszLinks[0] = g_path_get_basename(filenames[0]); filenames[1] = aszLinks[1] = NULL; if (i > 0) { filenames[1] = filename_from_iGame(sz, i - 1); aszLinks[1] = g_path_get_basename(filenames[1]); } filenames[2] = aszLinks[2] = NULL; if (i < nGames - 1) { filenames[2] = filename_from_iGame(sz, i + 1); aszLinks[2] = g_path_get_basename(filenames[2]); } filenames[3] = filename_from_iGame(sz, nGames - 1); aszLinks[3] = g_path_get_basename(filenames[3]); if (!i) { if (!confirmOverwrite(sz, fConfirmSave)) { for (j = 0; j < 4; j++) { g_free(aszLinks[j]); g_free(filenames[j]); } g_free(szCurrent); return; } setDefaultFileName(sz); } if (!strcmp(szCurrent, "-")) pf = stdout; else if (!(pf = g_fopen(szCurrent, "w"))) { outputerr(szCurrent); for (j = 0; j < 4; j++) { g_free(aszLinks[j]); g_free(filenames[j]); } g_free(szCurrent); return; } ExportGameHTML(pf, pl->p, exsExport.szHTMLPictureURL, exsExport.szHTMLExtension, exsExport.het, exsExport.hecss, i, i == nGames - 1, aszLinks); for (j = 0; j < 4; j++) { g_free(aszLinks[j]); g_free(filenames[j]); } g_free(szCurrent); if (pf != stdout) fclose(pf); } /* external stylesheet */ if (exsExport.hecss == HTML_EXPORT_CSS_EXTERNAL) if ((pf = OpenCSS(sz))) { WriteStyleSheet(pf, exsExport.hecss); fclose(pf); } } extern void CommandExportPositionHtml(char *sz) { FILE *pf; int fHistory; moverecord *pmr; int iMove; sz = NextToken(&sz); if (ms.gs == GAME_NONE) { outputl(_("No game in progress (type `new game' to start one).")); return; } if (!sz || !*sz) { outputl(_("You must specify a file to export to (see `help export " "position html').")); return; } pmr = get_current_moverecord(&fHistory); if (!confirmOverwrite(sz, fConfirmSave)) return; if (!strcmp(sz, "-")) pf = stdout; else if (!(pf = g_fopen(sz, "w"))) { outputerr(sz); return; } if (exsExport.het == HTML_EXPORT_TYPE_GNU) check_for_html_images(sz); HTMLPrologue(pf, &ms, getGameNumber(plGame), NULL, exsExport.het, exsExport.hecss); if (exsExport.fIncludeMatchInfo) HTMLMatchInfo(pf, &mi, exsExport.hecss); if (fHistory) iMove = getMoveNumber(plGame, pmr) - 1; else if (plLastMove) iMove = getMoveNumber(plGame, plLastMove->p); else iMove = -1; HTMLBoardHeader(pf, &ms, exsExport.het, exsExport.hecss, getGameNumber(plGame), iMove, TRUE); printHTMLBoard(pf, &ms, ms.fTurn, exsExport.szHTMLPictureURL, exsExport.szHTMLExtension, exsExport.het, exsExport.hecss); if (pmr) { HTMLAnalysis(pf, &ms, pmr, exsExport.szHTMLPictureURL, exsExport.szHTMLExtension, exsExport.het, exsExport.hecss); if (exsExport.fIncludeAnnotation) HTMLPrintComment(pf, pmr, exsExport.hecss); } HTMLEpilogue(pf, &ms, NULL, exsExport.hecss); if (pf != stdout) fclose(pf); setDefaultFileName(sz); /* external stylesheet */ if (exsExport.hecss == HTML_EXPORT_CSS_EXTERNAL) if ((pf = OpenCSS(sz))) { WriteStyleSheet(pf, exsExport.hecss); fclose(pf); } } static void ExportPositionGammOnLine(FILE * pf) { moverecord *pmr = get_current_moverecord(NULL); if (!pmr) { outputerrf(_("Unable to export this position")); return; } fputs("\n\n\n", pf); fputs("", pf); if (ms.nMatchTo > 1) fprintf(pf, /* ngettext for complex Plural-Forms language */ ngettext("Match to %d point - %s %d, %s %d%s", "Match to %d points - %s %d, %s %d%s", ms.nMatchTo), ms.nMatchTo, ap[0].szName, ms.anScore[0], ap[1].szName, ms.anScore[1], ms.fCrawford ? _(", Crawford game") : (ms.fPostCrawford ? _(", post-Crawford play") : "")); else if (ms.nMatchTo == 1) fprintf(pf, _("Match to 1 point")); else fprintf(pf, gettext("Unlimited game, %s"), ms.fJacoby ? _("Jacoby rule") : _("without Jacoby rule")); fputs("\n", pf); fputs("\n\n\n", pf); printHTMLBoard(pf, &ms, ms.fTurn, "../Images/", "gif", HTML_EXPORT_TYPE_BBS, HTML_EXPORT_CSS_INLINE); HTMLAnalysis(pf, &ms, pmr, "../Images/", "gif", HTML_EXPORT_TYPE_BBS, HTML_EXPORT_CSS_INLINE); HTMLPrintComment(pf, pmr, HTML_EXPORT_CSS_INLINE); HTMLEpilogueComment(pf); } extern void CommandExportPositionGammOnLine(char *sz) { FILE *pf; int fDontClose = FALSE; sz = NextToken(&sz); if (ms.gs == GAME_NONE) { outputl(_("No game in progress (type `new game' to start one).")); return; } if (!sz || !*sz) { outputl(_("You must specify a file to export to (see `help export " "position html').")); return; } if (!confirmOverwrite(sz, fConfirmSave)) return; if (!strcmp(sz, "-")) { pf = stdout; fDontClose = TRUE; } else if (!(pf = g_fopen(sz, "w"))) { outputerr(sz); return; } ExportPositionGammOnLine(pf); if (!fDontClose) fclose(pf); } extern void CommandExportPositionGOL2Clipboard(char *UNUSED(sz)) { char *szClipboard; long l; size_t s; FILE *pf; char *tmpFile; if (ms.gs == GAME_NONE) { outputl(_("No game in progress (type `new game' to start one).")); return; } /* get tmp file */ pf = GetTemporaryFile(NULL, &tmpFile); if (pf == NULL) { outputerr(_("Error creating temporary file")); return; } /* generate file */ ExportPositionGammOnLine(pf); /* find size of file */ if (fseek(pf, 0L, SEEK_END)) { outputerr(_("Error reading temporary file")); return; } l = ftell(pf); if (l < 0 || fseek(pf, 0L, SEEK_SET)) { outputerr(_("Error reading temporary file")); return; } /* copy file to clipboard */ s = (size_t) l; szClipboard = (char *) g_malloc(s + 1); if (fread(szClipboard, 1, s, pf) != s) { outputerr(_("Error reading temporary file")); } else { szClipboard[s] = 0; TextToClipboard(szClipboard); } g_free(szClipboard); fclose(pf); g_unlink(tmpFile); g_free(tmpFile); }