/*
* gnubg.c
*
* by Gary Wong
, 1998, 1999, 2000, 2001, 2002, 2003.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 3 or later of the GNU General Public License as
* published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: gnubg.c,v 1.886 2011/02/14 21:06:24 plm Exp $
*/
#include "config.h"
#include "gnubgmodule.h"
#include
#include
#if HAVE_UNISTD_H
#include
#endif
#include
#include
#include
#include
#include
#include
#ifdef WIN32
#include
#endif
#if HAVE_LIBREADLINE
static char *gnubg_histfile;
#include
#include
static int fReadingOther;
static char szCommandSeparators[] = " \t\n\r\v\f";
#endif
#define STRINGIZEAUX(num) #num
#define STRINGIZE(num) STRINGIZEAUX(num)
#include "analysis.h"
#include "backgammon.h"
#include "dice.h"
#include "drawboard.h"
#include "eval.h"
#include "sgf.h"
#include "export.h"
#include "import.h"
#include
#include
#include "matchequity.h"
#include "matchid.h"
#include "positionid.h"
#include "render.h"
#include "renderprefs.h"
#include "rollout.h"
#include "sound.h"
#include "progress.h"
#include "osr.h"
#include "format.h"
#include "relational.h"
#include "credits.h"
#include "external.h"
#include "neuralnet.h"
#include "util.h"
#if HAVE_SOCKETS
#if HAVE_SYS_SOCKET_H
#include
#include
#include
#include
#include
#endif /* #if HAVE_SYS_SOCKET_H */
#ifndef WIN32
#include
#else /* #ifndef WIN32 */
#include
#include
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS
#define EALREADY WSAEALREADY
#define ENOTSOCK WSAENOTSOCK
#define EDESTADDRREQ WSAEDESTADDRREQ
#define EMSGSIZE WSAEMSGSIZE
#define EPROTOTYPE WSAEPROTOTYPE
#define ENOPROTOOPT WSAENOPROTOOPT
#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
#define EOPNOTSUPP WSAEOPNOTSUPP
#define EPFNOSUPPORT WSAEPFNOSUPPORT
#define EAFNOSUPPORT WSAEAFNOSUPPORT
#define EADDRINUSE WSAEADDRINUSE
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define ENETDOWN WSAENETDOWN
#define ENETUNREACH WSAENETUNREACH
#define ENETRESET WSAENETRESET
#define ECONNABORTED WSAECONNABORTED
#define ECONNRESET WSAECONNRESET
#define ENOBUFS WSAENOBUFS
#define EISCONN WSAEISCONN
#define ENOTCONN WSAENOTCONN
#define ESHUTDOWN WSAESHUTDOWN
#define ETOOMANYREFS WSAETOOMANYREFS
#define ETIMEDOUT WSAETIMEDOUT
#define ECONNREFUSED WSAECONNREFUSED
#define ELOOP WSAELOOP
#define EHOSTDOWN WSAEHOSTDOWN
#define EHOSTUNREACH WSAEHOSTUNREACH
#define EPROCLIM WSAEPROCLIM
#define EUSERS WSAEUSERS
#define EDQUOT WSAEDQUOT
#define ESTALE WSAESTALE
#define EREMOTE WSAEREMOTE
#define inet_aton(ip,addr) (addr)->s_addr = inet_addr(ip), 1
#define inet_pton(fam,ip,addr) (addr)->s_addr = inet_addr(ip), 1
#endif /* #ifndef WIN32 */
#endif
#if USE_GTK
#include
#include "gtkboard.h"
#include "gtkgame.h"
#include "gtkprefs.h"
#include "gtksplash.h"
#include "gtkchequer.h"
#include "gtkwindows.h"
#endif
#if USE_BOARD3D
#include "fun3d.h"
#endif
#include "multithread.h"
#include "openurl.h"
#if defined(MSDOS) || defined(__MSDOS__) || defined(WIN32)
#define NO_BACKSLASH_ESCAPES 1
#endif
#if USE_GTK
int fX = FALSE; /* use X display */
unsigned int nDelay = 300;
int fNeedPrompt = FALSE;
#if HAVE_LIBREADLINE
int fReadingCommand;
#endif
#endif
static char *autosave = NULL;
static int loading_rc = FALSE;
static int foutput_on = TRUE;
const char *intro_string =
N_("This program comes with ABSOLUTELY NO WARRANTY; for details type `show warranty'.\n"
"This is free software, and you are welcome to redistribute it under certain conditions; type `show copying' for details.\n");
char *szLang=NULL;
const char szDefaultPrompt[] = "(\\p) ",
*szPrompt = szDefaultPrompt;
static int fInteractive, cOutputDisabled, cOutputPostponed;
matchstate ms = {
{{0}, {0}}, /* anBoard */
{0}, /* anDice */
-1, /* fTurn */
0, /* fResigned */
0, /* fResignationDeclined */
FALSE, /* fDoubled */
0, /* cGames */
-1, /* fMove */
-1, /* fCubeOwner */
FALSE, /* fCrawford */
FALSE, /* fPostCrawford */
0, /* nMatchTo */
{ 0, 0 }, /* anScore */
1, /* nCube */
0, /* cBeavers */
VARIATION_STANDARD, /*bgv */
TRUE, /* fCubeUse */
TRUE, /* fJacoby */
GAME_NONE /* gs */
};
ConstTanBoard msBoard(void) {return (ConstTanBoard)ms.anBoard;}
matchinfo mi;
float rRatingOffset = 2050;
int fAnalyseCube = TRUE;
int fAnalyseDice = TRUE;
int fAnalyseMove = TRUE;
int fAutoBearoff = FALSE;
int fAutoCrawford = 1;
int fAutoGame = TRUE;
int fAutoMove = FALSE;
int fAutoRoll = TRUE;
int fCheat = FALSE;
int fConfirmNew = TRUE;
int fConfirmSave = TRUE;
int nAutoSaveTime = 15;
int fAutoSaveRollout = FALSE;
int fAutoSaveAnalysis = FALSE;
int fAutoSaveConfirmDelete = TRUE;
int fCubeEqualChequer = TRUE;
int fCubeUse = TRUE;
int fDisplay = TRUE;
int fFullScreen = FALSE;
int fGotoFirstGame = FALSE;
int fInvertMET = FALSE;
int fJacoby = TRUE;
int fOutputRawboard = FALSE;
int fPlayersAreSame = TRUE;
int fRecord = TRUE;
int fShowProgress;
int fStyledGamelist = TRUE;
int fTruncEqualPlayer0 =TRUE;
int fTutorChequer = TRUE;
int fTutorCube = TRUE;
int fTutor = FALSE;
int fEvalSameAsAnalysis = TRUE;
int nConfirmDefault = -1;
int nThreadPriority = 0;
int nToolbarStyle = 2;
unsigned int afCheatRoll[ 2 ] = { 0, 0 };
unsigned int cAutoDoubles = 0;
unsigned int nBeavers = 3;
unsigned int nDefaultLength = 7;
#if USE_BOARD3D
int fSync = -1; /* Not set */
int fResetSync = FALSE; /* May need to wait for main window */
#endif
skilltype TutorSkill = SKILL_DOUBTFUL;
int nTutorSkillCurrent = 0;
char *szCurrentFileName = NULL;
char *szCurrentFolder = NULL;
int fNextTurn = FALSE, fComputing = FALSE;
float rEvalsPerSec = -1.0f;
float arLuckLevel[] = {
0.6f, /* LUCK_VERYBAD */
0.3f, /* LUCK_BAD */
0, /* LUCK_NONE */
0.3f, /* LUCK_GOOD */
0.6f /* LUCK_VERYGOOD */
}, arSkillLevel[] = {
0.16f, /* SKILL_VERYBAD */
0.08f, /* SKILL_BAD */
0.04f, /* SKILL_DOUBTFUL */
0, /* SKILL_NONE */
};
evalcontext ecTD = { FALSE, 0, FALSE, TRUE, 0.0 };
/* this is the "normal" movefilter*/
#define MOVEFILTER \
{ { { 0, 8, 0.16f }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } } , \
{ { 0, 8, 0.16f }, { -1, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } } , \
{ { 0, 8, 0.16f }, { -1, 0, 0 }, { 0, 2, 0.04f }, { 0, 0, 0 } }, \
{ { 0, 8, 0.16f }, { -1, 0, 0 }, { 0, 2, 0.04f }, { -1, 0, 0 } } , \
}
rngcontext *rngctxRollout = NULL;
rolloutcontext rcRollout =
{
{
/* player 0/1 cube decision */
{ TRUE, 0, TRUE, TRUE, 0.0 },
{ TRUE, 0, TRUE, TRUE, 0.0 }
},
{
/* player 0/1 chequerplay */
{ TRUE, 0, TRUE, TRUE, 0.0 },
{ TRUE, 0, TRUE, TRUE, 0.0 }
},
{
/* player 0/1 late cube decision */
{ TRUE, 0, TRUE, TRUE, 0.0 },
{ TRUE, 0, TRUE, TRUE, 0.0 }
},
{
/* player 0/1 late chequerplay */
{ TRUE, 0, TRUE, TRUE, 0.0 },
{ TRUE, 0, TRUE, TRUE, 0.0 }
},
/* truncation point cube and chequerplay */
{ TRUE, 0, TRUE, TRUE, 0.0 },
{ TRUE, 0, TRUE, TRUE, 0.0 },
/* move filters */
{ MOVEFILTER, MOVEFILTER },
{ MOVEFILTER, MOVEFILTER },
TRUE, /* cubeful */
TRUE, /* variance reduction */
FALSE, /* initial position */
TRUE, /* rotate */
TRUE, /* truncate at BEAROFF2 for cubeless rollouts */
TRUE, /* truncate at BEAROFF2_OS for cubeless rollouts */
FALSE, /* late evaluations */
FALSE, /* Truncation enabled */
FALSE, /* no stop on STD */
FALSE, /* no stop on JSD */
FALSE, /* no move stop on JSD */
10, /* truncation */
1296, /* number of trials */
5, /* late evals start here */
RNG_MERSENNE, /* RNG */
0, /* seed */
144, /* minimum games */
0.01, /* stop when std's are lower than 0.01 */
144, /* minimum games */
1.96, /* stop when best has j.s.d. for 95% confidence */
0, /* nGamesDone */
0, /* nSkip */
};
/* parameters for `eval' and `hint' */
#define EVALSETUP { \
/* evaltype */ \
EVAL_EVAL, \
/* evalcontext */ \
{ TRUE, 0, FALSE, TRUE, 0.0 }, \
/* rolloutcontext */ \
{ \
{ \
{ FALSE, 0, TRUE, TRUE, 0.0 }, /* player 0 cube decision */ \
{ FALSE, 0, TRUE, TRUE, 0.0 } /* player 1 cube decision */ \
}, \
{ \
{ FALSE, 0, TRUE, TRUE, 0.0 }, /* player 0 chequerplay */ \
{ FALSE, 0, TRUE, TRUE, 0.0 } /* player 1 chequerplay */ \
}, \
{ \
{ FALSE, 0, TRUE, TRUE, 0.0 }, /* p 0 late cube decision */ \
{ FALSE, 0, TRUE, TRUE, 0.0 } /* p 1 late cube decision */ \
}, \
{ \
{ FALSE, 0, TRUE, TRUE, 0.0 }, /* p 0 late chequerplay */ \
{ FALSE, 0, TRUE, TRUE, 0.0 } /* p 1 late chequerplay */ \
}, \
{ FALSE, 0, TRUE, TRUE, 0.0 }, /* truncate cube decision */ \
{ FALSE, 0, TRUE, TRUE, 0.0 }, /* truncate chequerplay */ \
{ MOVEFILTER, MOVEFILTER }, \
{ MOVEFILTER, MOVEFILTER }, \
FALSE, /* cubeful */ \
TRUE, /* variance reduction */ \
FALSE, /* initial position */ \
TRUE, /* rotate */ \
TRUE, /* truncate at BEAROFF2 for cubeless rollouts */ \
TRUE, /* truncate at BEAROFF2_OS for cubeless rollouts */ \
FALSE, /* late evaluations */ \
TRUE, /* Truncation enabled */ \
FALSE, /* no stop on STD */ \
FALSE, /* no stop on JSD */ \
FALSE, /* no move stop on JSD */ \
10, /* truncation */ \
1296, /* number of trials */ \
5, /* late evals start here */ \
RNG_MERSENNE, /* RNG */ \
0, /* seed */ \
144, /* minimum games */ \
0.01, /* stop when std's are lower than 0.01 */ \
144, /* minimum games */ \
1.96, /* stop when best has j.s.d. for 95% confidence */ \
0, \
0 \
} \
}
evalsetup esEvalChequer = EVALSETUP;
evalsetup esEvalCube = EVALSETUP;
evalsetup esAnalysisChequer = EVALSETUP;
evalsetup esAnalysisCube = EVALSETUP;
movefilter aamfEval[ MAX_FILTER_PLIES ][ MAX_FILTER_PLIES ] = MOVEFILTER;
movefilter aamfAnalysis[ MAX_FILTER_PLIES ][ MAX_FILTER_PLIES ] = MOVEFILTER;
extern evalsetup *GetEvalChequer(void)
{
return fEvalSameAsAnalysis ? &esAnalysisChequer : &esEvalChequer;
}
extern evalsetup *GetEvalCube(void)
{
return fEvalSameAsAnalysis ? &esAnalysisCube : &esEvalCube;
}
extern TmoveFilter *GetEvalMoveFilter(void)
{
return fEvalSameAsAnalysis ? &aamfAnalysis : &aamfEval;
}
exportsetup exsExport = {
TRUE, /* include annotations */
TRUE, /* include analysis */
TRUE, /* include statistics */
TRUE, /* include match information */
1, /* display board for all moves */
-1, /* both players */
5, /* display max 5 moves */
TRUE, /* show detailed probabilities */
/* do not show move parameters */
{ FALSE, TRUE },
/* display all moves */
{ TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE },
TRUE, /* show detailed prob. for cube decisions */
{ FALSE, TRUE }, /* do not show move parameters */
/* display all cube decisions */
{ TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE },
NULL, /* HTML url to pictures */
HTML_EXPORT_TYPE_GNU,
NULL, /* HTML extension */
HTML_EXPORT_CSS_HEAD, /* write CSS stylesheet in */
4, /* PNG size */
4 /* Html size */
};
#define DEFAULT_NET_SIZE 128
player ap[ 2 ] = {
{ "gnubg", PLAYER_GNU, EVALSETUP, EVALSETUP, MOVEFILTER, 0, NULL },
{ "user", PLAYER_HUMAN, EVALSETUP, EVALSETUP, MOVEFILTER, 0, NULL }
};
char default_names[2][31] = {"gnubg", "user"};
/* Usage strings */
static char szDICE[] = N_(" "),
szCOMMAND[] = N_(""),
szCOMMENT[] = N_(""),
szER[] = N_("evaluation|rollout"),
szFILENAME[] = N_(""),
szKEYVALUE[] = N_("[= ...]"),
szLENGTH[] = N_(""),
szLIMIT[] = N_(""),
szMILLISECONDS[] = N_(""),
szMOVE[] = N_(" ..."),
szFILTER[] = N_( " "
"[ ]"),
szNAME[] = N_(""),
szQUIET[] = N_("[quiet]"),
szLANG[] = N_("system|"),
szONOFF[] = N_("on|off"),
szOPTCOMMAND[] = N_("[command]"),
szOPTDATE[] = N_("[yyyy-mm-dd]"),
szOPTDEPTH[] = N_("[depth]"),
szOPTFILENAME[] = N_("[filename]"),
szOPTLENGTH[] = N_("[length]"),
szOPTMODULUSOPTSEED[] = N_("[modulus |factors ] "
"[seed]"),
szOPTNAME[] = N_("[name]"),
szOPTPOSITION[] = N_("[position]"),
szOPTSEED[] = N_("[seed]"),
szOPTVALUE[] = N_("[value]"),
szPLAYER[] = N_(""),
szPLAYEROPTRATING[] = N_(" [rating]"),
szPLIES[] = N_(""),
szPOSITION[] = N_(""),
szPRIORITY[] = N_(""),
szPROMPT[] = N_(""),
szSCORE[] = N_(""),
szSIZE[] = N_(""),
szSTEP[] = N_("[game|roll|rolled|marked] "),
szTRIALS[] = N_(""),
szVALUE[] = N_(""),
szMATCHID[] = N_(""),
szGNUBGID[] = N_(""),
szXGID[] = N_(""),
szURL[] = N_(""),
szMAXERR[] = N_(""),
szMINGAMES[] = N_(""),
szFOLDER[] = N_(""),
#if USE_GTK
szWARN[] = N_("[]"),
szWARNYN[] = N_(" on|off"),
#endif
szJSDS[] = N_(""),
szSTDDEV[] = N_("");
/* Command defines moved into separate file */
#include "commands.inc"
static int iProgressMax, iProgressValue, fInProgress;
static char *pcProgress;
static psighandler shInterruptOld;
char *default_import_folder = NULL;
char *default_export_folder = NULL;
char *default_sgf_folder = NULL;
const char *szHomeDirectory;
char const *aszBuildInfo[] = {
#if USE_PYTHON
N_("Python supported."),
#endif
#if USE_SQLITE
N_("SQLite database supported."),
#endif
#if USE_GTK
N_("GTK graphical interface supported."),
#endif
#if HAVE_SOCKETS
N_("External players supported."),
#endif
#if HAVE_LIBGMP
N_("Long RNG seeds supported."),
#endif
#if USE_BOARD3D
N_("3d Boards supported."),
#endif
#if HAVE_SOCKETS
N_("External commands supported."),
#endif
#if defined(WIN32)
N_("Windows sound system supported."),
#elif defined(__APPLE__) && !defined(__LP64__)
N_("Apple QuickTime sound system supported."),
#elif HAVE_CANBERRA
N_("libcanberra sound system supported."),
#endif
#if USE_MULTITHREAD
N_("Multiple threads supported."),
#endif
#if USE_SSE_VECTORIZE
#if USE_SSE2
N_("SSE/SSE2 supported."),
#else
N_("SSE supported."),
#endif
#endif
NULL
};
extern const char *GetBuildInfoString(void)
{
static const char **ppch = aszBuildInfo;
if (!*ppch)
{
ppch = aszBuildInfo;
return NULL;
}
return *ppch++;
}
/*
* general token extraction
input: ppch pointer to pointer to command
szToekns - string of token separators
output: NULL if no token found
ptr to extracted token if found. Token is in original location
in input string, but null terminated if not quoted, token
will have been moved forward over quote character when quoted
ie:
input: ' abcd efgh'
output ' abcd\0efgh'
return value points to abcd, ppch points to efgh
input ' "jklm" nopq'
output ; jklm\0 nopq'
return value points to jklm, ppch points to space before
the 'n'
ppch points past null terminator
ignores leading whitespace, advances ppch over token and trailing
whitespace
matching single or double quotes are allowed, any character outside
of quotes or in doubly quoated strings can be escaped with a
backslash and will be taken as literal. Backslashes within single
quoted strings are taken literally. Multiple quoted strins can be
concatentated.
For example: input ' abc\"d"e f\"g h i"jk'l m n \" o p q'rst uvwzyz'
with the terminator list ' \t\r\n\v\f'
The returned token will be the string
ppch will point to the 'u'
The \" between c and d is not in a single quoted string, so is reduced to
a double quote and is *not* the start of a quoted string.
The " before the 'd' begins a double quoted string, so spaces and tabs are
not terminators. The \" between f and g is reduced to a double quote and
does not teminate the quoted string. which ends with the double quote
between i and j. The \" between n and o is taken as a pair of literal
characters because they are within the single quoted string beginning
before l and ending after q.
It is not possible to put a single quote within a single quoted string.
You can have single quotes unescaped withing double quoted strings and
double quotes unescaped within single quoted strings.
*/
extern char *
NextTokenGeneral( char **ppch, const char *szTokens ) {
char *pch, *pchSave, chQuote = 0;
int fEnd = FALSE;
#ifndef NDEBUG
char *pchEnd;
#endif
if( !*ppch )
return NULL;
#ifndef NDEBUG
pchEnd = strchr( *ppch, 0 );
#endif
/* skip leading whitespace */
while( isspace( **ppch ) )
( *ppch )++;
if( !*( pch = pchSave = *ppch ) )
/* nothing left */
return NULL;
while( !fEnd ) {
if ( **ppch && strchr( szTokens, **ppch ) ) {
/* this character ends token */
if( !chQuote ) {
fEnd = TRUE;
(*ppch)++; /* step over token */
}
else
*pchSave++ = **ppch;
}
else {
switch( **ppch ) {
case '\'':
case '"':
/* quote mark */
if( !chQuote )
/* start quoting */
chQuote = **ppch;
else if( chQuote == **ppch )
/* end quoting */
chQuote = 0;
else
/* literal */
*pchSave++ = **ppch;
break;
#ifdef NO_BACKSLASH_ESCAPES
case '%':
#else
case '\\':
#endif
/* backslash */
if( chQuote == '\'' )
/* literal */
*pchSave++ = **ppch;
else {
( *ppch )++;
if( **ppch )
/* next character is literal */
*pchSave++ = **ppch;
else {
/* end of string -- the backlash doesn't quote anything */
#ifdef NO_BACKSLASH_ESCAPES
*pchSave++ = '%';
#else
*pchSave++ = '\\';
#endif
fEnd = TRUE;
}
}
break;
case 0:
/* end of string -- always ends token */
fEnd = TRUE;
break;
default:
*pchSave++ = **ppch;
}
}
if( !fEnd )
( *ppch )++;
}
while( isspace( **ppch ) )
( *ppch )++;
*pchSave = 0;
#ifndef NDEBUG
g_assert( pchSave <= pchEnd );
g_assert( *ppch <= pchEnd );
g_assert( pch <= pchEnd );
#endif
return pch;
}
/* extrace a token from a string. Tokens are terminated by tab, newline,
carriage return, vertical tab or form feed.
Input:
ppch = pointer to pointer to input string. This will be updated
to point past any token found. If the string is exhausetd,
the pointer at ppch will point to the terminating NULL, so it is
safe to call this function repeatedly after failure
Output:
null terminated token if found or NULL if no tokens present.
*/
extern char *NextToken( char **ppch )
{
return NextTokenGeneral( ppch, " \t\n\r\v\f" );
}
/* return a count of the number of separate runs of one or more
non-whitespace characters. This is the number of tokens that
NextToken() will return. It may not be the number of tokens
NextTokenGeneral() will return, as it does not count quoted strings
containing whitespace as single tokens
*/
static int CountTokens( char *pch ) {
int c = 0;
do {
while( isspace( *pch ) )
pch++;
if( *pch ) {
c++;
while( *pch && !isspace( *pch ) )
pch++;
}
} while( *pch );
return c;
}
/* extract a token and convert to double. On error or no token, return
ERR_VAL (a very large negative double.
*/
extern double ParseReal( char **ppch )
{
char *pch, *pchOrig;
double r;
if( !ppch || !( pchOrig = NextToken( ppch ) ) )
return ERR_VAL;
r = g_ascii_strtod( pchOrig, &pch );
return *pch ? ERR_VAL : r;
}
/* get the next token from the input and convert as an
integer. Returns INT_MIN on empty input or non-numerics found. Does
handle negative integers. On failure, one token (if any were available
will have been consumed, it is not pushed back into the input.
*/
extern int ParseNumber( char **ppch )
{
char *pch, *pchOrig;
if( !ppch || !( pchOrig = NextToken( ppch ) ) )
return INT_MIN;
for( pch = pchOrig; *pch; pch++ )
if( !isdigit( *pch ) && *pch != '-' )
return INT_MIN;
return atoi( pchOrig );
}
/* get a player either by name or as player 0 or 1 (indicated by the single
character '0' or '1'. Returns -1 on no input, 2 if not a recoginsed name
Note - this is not a token extracting routine, it expects to be handed
an already extracted token
*/
extern int ParsePlayer( char *sz )
{
int i;
if( !sz )
return -1;
if( ( *sz == '0' || *sz == '1' ) && !sz[ 1 ] )
return *sz - '0';
for( i = 0; i < 2; i++ )
if( !CompareNames( sz, ap[ i ].szName ) )
return i;
if( !StrNCaseCmp( sz, "both", strlen( sz ) ) )
return 2;
return -1;
}
/* Convert a string to a board array. Currently allows the string to
be a position ID, "=n" notation, or empty (in which case the current
board is used).
The input string should be specified in *ppch; this string must be
modifiable, and the pointer will be updated to point to the token
following a board specification if possible (see NextToken()). The
board will be returned in an, and if pchDesc is non-NULL, then
descriptive text (the position ID, formatted move, or "Current
position", depending on the input) will be stored there.
Returns -1 on failure, 0 on success, or 1 on success if the position
specified has the opponent on roll (e.g. because it used "=n" notation). */
extern int ParsePosition( TanBoard an, char **ppch, char *pchDesc )
{
int i;
char *pch;
/* FIXME allow more formats, e.g. FIBS "boardstyle 3" */
if( !ppch || !( pch = NextToken( ppch ) ) ) {
memcpy( an, msBoard(), sizeof(TanBoard) );
if( pchDesc )
strcpy( pchDesc, _("Current position") );
return 0;
}
if ( ! strcmp ( pch, "simple" ) ) {
/* board given as 26 integers.
* integer 1 : # of my chequers on the bar
* integer 2-25: number of chequers on point 1-24
* positive ints: my chequers
* negative ints: opp chequers
* integer 26 : # of opp chequers on the bar
*/
int n;
for ( i = 0; i < 26; i++ ) {
if ( ( n = ParseNumber ( ppch ) ) == INT_MIN ) {
outputf (_("`simple' must be followed by 26 integers; "
"found only %d\n"), i );
return -1;
}
if ( i == 0 ) {
/* my chequers on the bar */
an[ 1 ][ 24 ] = abs(n);
}
else if ( i == 25 ) {
/* opp chequers on the bar */
an[ 0 ][ 24 ] = abs(n);
} else {
an[ 1 ][ i - 1 ] = 0;
an[ 0 ][ 24 - i ] = 0;
if ( n < 0 )
an[ 0 ][ 24 - i ] = -n;
else if ( n > 0 )
an[ 1 ][ i - 1 ] = n;
}
}
if( pchDesc )
strcpy( pchDesc, *ppch );
*ppch = NULL;
return CheckPosition((ConstTanBoard)an) ? 0 : -1;
}
if( !PositionFromID( an, pch ) ) {
outputl( _("Illegal position.") );
return -1;
}
if( pchDesc )
strcpy( pchDesc, pch );
return 0;
}
/* Parse "key=value" pairs on a command line. PPCH takes a pointer to
a command line on input, and returns a pointer to the next parameter.
The key is returned in apch[ 0 ], and the value in apch[ 1 ].
The function return value is the number of parts successfully read
(0 = no key was found, 1 = key only, 2 = both key and value). */
extern int ParseKeyValue( char **ppch, char *apch[ 2 ] )
{
if( !ppch || !( apch[ 0 ] = NextToken( ppch ) ) )
return 0;
if( !( apch[ 1 ] = strchr( apch[ 0 ], '=' ) ) )
return 1;
*apch[ 1 ] = 0;
apch[ 1 ]++;
return 2;
}
/* Compare player names. Performed case insensitively, and with all
whitespace characters and underscore considered identical. */
extern int CompareNames( char *sz0, char *sz1 )
{
static char ach[] = " \t\r\n\f\v_";
for( ; *sz0 || *sz1; sz0++, sz1++ )
if( toupper( *sz0 ) != toupper( *sz1 ) &&
( !strchr( ach, *sz0 ) || !strchr( ach, *sz1 ) ) )
return toupper( *sz0 ) - toupper( *sz1 );
return 0;
}
extern void UpdateSetting( void *p )
{
#if USE_GTK
if( fX )
GTKSet( p );
#endif
}
extern void UpdateSettings( void )
{
UpdateSetting( &ms.nCube );
UpdateSetting( &ms.fCubeOwner );
UpdateSetting( &ms.fTurn );
UpdateSetting( &ms.nMatchTo );
UpdateSetting( &ms.fCrawford );
UpdateSetting( &ms.gs );
ShowBoard();
#if USE_BOARD3D
RestrictiveRedraw();
#endif
}
/* handle turning a setting on / off
inputs: szName - the setting being adjusted
pf = pointer to current on/off state (will be updated)
sz = pointer to command line - a token will be extracted,
but furhter calls to NextToken will return only the on/off
value, so you can't have commands in the form
set something on
szOn - text to output when turning setting on
szOff - text to output when turning setting off
output: -1 on error
0 setting is now off
1 setting is now on
acceptable tokens are on/off yes/no true/false
*/
extern int SetToggle( const char *szName, int *pf, char *sz, const char *szOn, const char *szOff )
{
char *pch = NextToken( &sz );
int cch;
if( !pch ) {
outputf( _("You must specify whether to set '%s' on or off.\n"), szName );
return -1;
}
cch = strlen( pch );
if( !StrCaseCmp( "on", pch ) || !StrNCaseCmp( "yes", pch, cch ) ||
!StrNCaseCmp( "true", pch, cch ) ) {
outputl( szOn );
if( *pf != TRUE ) {
*pf = TRUE;
UpdateSetting( pf );
}
return TRUE;
}
if( !StrCaseCmp( "off", pch ) || !StrNCaseCmp( "no", pch, cch ) ||
!StrNCaseCmp( "false", pch, cch ) ) {
outputl( szOff );
if( *pf != FALSE ) {
*pf = FALSE;
UpdateSetting( pf );
}
return FALSE;
}
outputf( _("Illegal keyword `%s'.\n"), pch );
return -1;
}
extern void PortableSignal( int nSignal, void (*p)(int),
psighandler *pOld, int fRestart ) {
#if HAVE_SIGACTION
struct sigaction sa;
sa.sa_handler = p;
sigemptyset( &sa.sa_mask );
sa.sa_flags =
#if SA_RESTART
( fRestart ? SA_RESTART : 0 ) |
#endif
#if SA_NOCLDSTOP
SA_NOCLDSTOP |
#endif
0;
sigaction( nSignal, p ? &sa : NULL, pOld );
#elif HAVE_SIGVEC
struct sigvec sv;
sv.sv_handler = p;
sigemptyset( &sv.sv_mask );
sv.sv_flags = nSignal == SIGINT || nSignal == SIGIO ? SV_INTERRUPT : 0;
sigvec( nSignal, p ? &sv : NULL, pOld );
#else
if( pOld )
*pOld = signal( nSignal, p );
else if( p )
signal( nSignal, p );
#endif
}
extern void PortableSignalRestore( int nSignal, psighandler *p )
{
#if HAVE_SIGACTION
sigaction( nSignal, p, NULL );
#elif HAVE_SIGVEC
sigvec( nSignal, p, NULL );
#else
signal( nSignal, *p );
#endif
}
/* Reset the SIGINT handler, on return to the main command loop. Notify
the user if processing had been interrupted. */
extern void ResetInterrupt( void )
{
if( fInterrupt ) {
{
outputf("(%s)", _("Interrupted") );
outputx();
}
fInterrupt = FALSE;
#if USE_GTK
if( nNextTurn ) {
g_source_remove( nNextTurn );
nNextTurn = 0;
}
#endif
}
}
extern void HandleCommand( char *sz, command *ac )
{
command *pc;
char *pch;
int cch;
if( ac == acTop ) {
outputnew();
if( *sz == '#' ) /* Comment */
return;
else if( *sz == ':' ) {
return;
}
else if ( *sz == '>' ) {
while ( *sz == '>' )
++sz;
/* leading white space confuses Python :-) */
while ( isspace( *sz ) )
++sz;
#if USE_PYTHON
PythonRun(sz);
#else
outputl( _("This installation of GNU Backgammon was compiled without Python support.") );
outputx();
#endif
return;
}
}
if( !( pch = NextToken( &sz ) ) ) {
if( ac != acTop )
outputl( _("Incomplete command.") );
outputx();
return;
}
cch = strlen( pch );
if( ac == acTop && ( isdigit( *pch ) ||
!StrNCaseCmp( pch, "bar/", cch > 4 ? 4 : cch ) ) ) {
if( pch + cch < sz )
pch[ cch ] = ' ';
CommandMove( pch );
outputx();
return;
}
for( pc = ac; pc->sz; pc++ )
if( !StrNCaseCmp( pch, pc->sz, cch ) )
break;
if( !pc->sz) {
if (!loading_rc)
outputerrf( _("Unknown keyword `%s'.\n"), pch );
return;
}
if( pc->pf ) {
pc->pf( sz );
outputx();
} else
HandleCommand( sz, pc->pc );
}
extern void InitBoard( TanBoard anBoard, const bgvariation bgv )
{
unsigned int i;
unsigned int j;
for( i = 0; i < 25; i++ )
anBoard[ 0 ][ i ] = anBoard[ 1 ][ i ] = 0;
switch( bgv ) {
case VARIATION_STANDARD:
case VARIATION_NACKGAMMON:
anBoard[ 0 ][ 5 ] = anBoard[ 1 ][ 5 ] =
anBoard[ 0 ][ 12 ] = anBoard[ 1 ][ 12 ] =
( bgv == VARIATION_NACKGAMMON ) ? 4 : 5;
anBoard[ 0 ][ 7 ] = anBoard[ 1 ][ 7 ] = 3;
anBoard[ 0 ][ 23 ] = anBoard[ 1 ][ 23 ] = 2;
if( bgv == VARIATION_NACKGAMMON )
anBoard[ 0 ][ 22 ] = anBoard[ 1 ][ 22 ] = 2;
break;
case VARIATION_HYPERGAMMON_1:
case VARIATION_HYPERGAMMON_2:
case VARIATION_HYPERGAMMON_3:
for ( i = 0; i < 2; ++i )
for ( j = 0; j < (unsigned int)( bgv - VARIATION_HYPERGAMMON_1 + 1 ); ++j )
anBoard[ i ][ 23 - j ] = 1;
break;
default:
g_assert ( FALSE );
break;
}
}
extern void GetMatchStateCubeInfo( cubeinfo* pci, const matchstate* pms )
{
SetCubeInfo( pci, pms->nCube, pms->fCubeOwner, pms->fMove,
pms->nMatchTo, pms->anScore, pms->fCrawford,
pms->fJacoby, nBeavers, pms->bgv );
}
static void
DisplayCubeAnalysis( float aarOutput[ 2 ][ NUM_ROLLOUT_OUTPUTS ],
float aarStdDev[ 2 ][ NUM_ROLLOUT_OUTPUTS ],
const evalsetup* pes ) {
cubeinfo ci;
if( pes->et == EVAL_NONE )
return;
GetMatchStateCubeInfo( &ci, &ms );
outputl( OutputCubeAnalysis( aarOutput, aarStdDev, pes, &ci ) );
}
extern char *GetLuckAnalysis( const matchstate *pms, float rLuck )
{
static char sz[ 16 ];
cubeinfo ci;
if( fOutputMWC && pms->nMatchTo ) {
GetMatchStateCubeInfo( &ci, pms );
sprintf( sz, "%+0.3f%%", 100.0f * ( eq2mwc( rLuck, &ci ) -
eq2mwc( 0.0f, &ci ) ) );
} else
sprintf( sz, "%+0.3f", rLuck );
return sz;
}
static void DisplayAnalysis( moverecord *pmr ) {
unsigned int i;
char szBuf[ 1024 ];
switch( pmr->mt ) {
case MOVE_NORMAL:
DisplayCubeAnalysis( pmr->CubeDecPtr->aarOutput,
pmr->CubeDecPtr->aarStdDev,
&pmr->CubeDecPtr->esDouble );
outputf( "%s %d%d", _("Rolled"), pmr->anDice[ 0 ], pmr->anDice[ 1 ] );
if( pmr->rLuck != ERR_VAL )
outputf( " (%s):\n", GetLuckAnalysis( &ms, pmr->rLuck ) );
else
outputl( ":" );
for( i = 0; i < pmr->ml.cMoves; i++ ) {
if( i >= 10 /* FIXME allow user to choose limit */ &&
i != pmr->n.iMove )
continue;
outputc( i == pmr->n.iMove ? '*' : ' ' );
output( FormatMoveHint( szBuf, &ms, &pmr->ml, i,
i != pmr->n.iMove ||
i != pmr->ml.cMoves - 1 ||
pmr->ml.cMoves == 1 ||
i < exsExport.nMoves, TRUE, TRUE ) );
}
break;
case MOVE_DOUBLE:
DisplayCubeAnalysis( pmr->CubeDecPtr->aarOutput,
pmr->CubeDecPtr->aarStdDev,
&pmr->CubeDecPtr->esDouble );
break;
case MOVE_TAKE:
case MOVE_DROP:
/* FIXME */
break;
case MOVE_SETDICE:
if( pmr->rLuck != ERR_VAL )
outputf( "%s %d%d (%s):\n", _("Rolled"),
pmr->anDice[ 0 ], pmr->anDice[ 1 ],
GetLuckAnalysis( &ms, pmr->rLuck ) );
break;
default:
break;
}
}
extern void ShowBoard( void )
{
char szBoard[ 2048 ];
char sz[ 50 ], szCube[ 50 ], szPlayer0[ MAX_NAME_LEN + 3 ], szPlayer1[ MAX_NAME_LEN + 3 ],
szScore0[ 50 ], szScore1[ 50 ], szMatch[ 50 ];
char *apch[ 7 ] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
moverecord *pmr;
TanBoard an;
if( cOutputDisabled || !foutput_on)
return;
if( ms.gs == GAME_NONE ) {
#if USE_GTK
if( fX ) {
TanBoard anBoardTemp;
InitBoard( anBoardTemp, ms.bgv );
game_set( BOARD( pwBoard ), anBoardTemp, 0, ap[ 1 ].szName,
ap[ 0 ].szName, ms.nMatchTo, ms.anScore[ 1 ],
ms.anScore[ 0 ], 0, 0, FALSE, anChequers[ ms.bgv ] );
} else
#endif
outputl( _("No game in progress.") );
return;
}
memcpy( an, msBoard(), sizeof(TanBoard) );
if( !ms.fMove )
SwapSides( an );
#if USE_GTK
if( !fX ) {
#endif
if( fOutputRawboard )
{
outputl( FIBSBoard( szBoard, an, ms.fMove, ap[ 1 ].szName,
ap[ 0 ].szName, ms.nMatchTo, ms.anScore[ 1 ],
ms.anScore[ 0 ], ms.anDice[ 0 ],
ms.anDice[ 1 ], ms.nCube,
ms.fCubeOwner, ms.fDoubled, ms.fTurn,
ms.fCrawford, anChequers[ ms.bgv ] ) );
return;
}
apch[ 0 ] = szPlayer0;
apch[ 6 ] = szPlayer1;
sprintf( apch[ 1 ] = szScore0,
ngettext( "%d point", "%d points", ms.anScore[ 0 ] ),
ms.anScore[ 0 ] );
sprintf( apch[ 5 ] = szScore1,
ngettext( "%d point", "%d points", ms.anScore[ 1 ] ),
ms.anScore[ 1 ] );
if( ms.fDoubled ) {
apch[ ms.fTurn ? 4 : 2 ] = szCube;
sprintf( szPlayer0, "O: %s", ap[ 0 ].szName );
sprintf( szPlayer1, "X: %s", ap[ 1 ].szName );
sprintf( szCube, _("Cube offered at %d"), ms.nCube << 1 );
} else {
sprintf( szPlayer0, "O: %s", ap[ 0 ].szName );
sprintf( szPlayer1, "X: %s", ap[ 1 ].szName );
apch[ ms.fMove ? 4 : 2 ] = sz;
if( ms.anDice[ 0 ] )
sprintf( sz,
_("%s %d%d"), _("Rolled"), ms.anDice[ 0 ], ms.anDice[ 1 ] );
else if( !GameStatus( msBoard(), ms.bgv ) )
strcpy( sz, _("On roll") );
else
sz[ 0 ] = 0;
if( ms.fCubeOwner < 0 ) {
apch[ 3 ] = szCube;
if( ms.nMatchTo )
sprintf( szCube,
_("%d point match (Cube: %d)"), ms.nMatchTo,
ms.nCube );
else
sprintf (szCube, "(%s: %d)", _("Cube"), ms.nCube);
} else {
int cch = strlen( ap[ ms.fCubeOwner ].szName );
if( cch > 20 )
cch = 20;
sprintf( szCube, "%c: %*s (%s: %d)", ms.fCubeOwner ? 'X' : 'O',
cch, ap[ ms.fCubeOwner ].szName, _("Cube"), ms.nCube );
apch[ ms.fCubeOwner ? 6 : 0 ] = szCube;
if( ms.nMatchTo )
sprintf( apch[ 3 ] = szMatch, _("%d point match"),
ms.nMatchTo );
}
}
if( ms.fResigned > 0 )
/* FIXME it's not necessarily the player on roll that resigned */
sprintf( strchr( sz, 0 ), ", %s %s", _("resigns"),
gettext ( aszGameResult[ ms.fResigned - 1 ] ) );
outputl( DrawBoard( szBoard, (ConstTanBoard)an, ms.fMove, apch,
MatchIDFromMatchState ( &ms ),
anChequers[ ms.bgv ] ) );
if (
#if USE_GTK
PanelShowing(WINDOW_ANALYSIS) &&
#endif
plLastMove && ( pmr = plLastMove->plNext->p ) ) {
DisplayAnalysis( pmr );
if( pmr->sz )
outputl( pmr->sz ); /* FIXME word wrap */
}
#if USE_GTK
}
else
{
game_set( BOARD( pwBoard ), an, ms.fMove, ap[ 1 ].szName,
ap[ 0 ].szName, ms.nMatchTo, ms.anScore[ 1 ],
ms.anScore[ 0 ], ms.anDice[ 0 ], ms.anDice[ 1 ],
ap[ ms.fTurn ].pt != PLAYER_HUMAN && !fComputing &&
!nNextTurn, anChequers[ ms.bgv ] );
}
#endif
#ifdef UNDEF
{
char *pc;
printf ( "%s: %s\n", _("MatchID"), pc = MatchIDFromMatchState ( &ms ) );
MatchStateFromID ( &ms, pc );
}
#endif
}
extern char *FormatPrompt( void )
{
static char sz[ 128 ]; /* FIXME check for overflow in rest of function */
const char *pch = szPrompt;
char *pchDest = sz;
unsigned int anPips[ 2 ];
while( *pch )
if( *pch == '\\' ) {
pch++;
switch( *pch ) {
case 0:
goto done;
case 'c':
case 'C':
/* Pip count */
if( ms.gs == GAME_NONE )
strcpy( pchDest, _("No game") );
else {
PipCount( msBoard(), anPips );
sprintf( pchDest, "%d:%d", anPips[ 1 ], anPips[ 0 ] );
}
break;
case 'p':
case 'P':
/* Player on roll */
switch( ms.gs ) {
case GAME_NONE:
strcpy( pchDest, _("No game") );
break;
case GAME_PLAYING:
strcpy( pchDest, ap[ ms.fTurn ].szName );
break;
case GAME_OVER:
case GAME_RESIGNED:
case GAME_DROP:
strcpy( pchDest, _("Game over") );
break;
}
break;
case 's':
case 'S':
/* Match score */
sprintf( pchDest, "%d:%d", ms.anScore[ 0 ], ms.anScore[ 1 ] );
break;
case 'v':
case 'V':
/* Version */
strcpy( pchDest, VERSION );
break;
default:
*pchDest++ = *pch;
*pchDest = 0;
}
pchDest = strchr( pchDest, 0 );
pch++;
} else
*pchDest++ = *pch++;
done:
*pchDest = 0;
return sz;
}
extern void CommandEval( char *sz )
{
char szOutput[ 4096 ];
int n;
TanBoard an;
cubeinfo ci;
decisionData dd;
if( !*sz && ms.gs == GAME_NONE ) {
outputl( _("No position specified and no game in progress.") );
return;
}
if( ( n = ParsePosition( an, &sz, NULL ) ) < 0 )
return;
if( n && ms.fMove )
/* =n notation used; the opponent is on roll in the position given. */
SwapSides( an );
if( ms.gs == GAME_NONE )
memcpy( &ci, &ciCubeless, sizeof( ci ) );
else
SetCubeInfo( &ci, ms.nCube, ms.fCubeOwner, n ? !ms.fMove : ms.fMove,
ms.nMatchTo, ms.anScore, ms.fCrawford, ms.fJacoby,
nBeavers, ms.bgv );
/* Consider cube action */
dd.pboard = (ConstTanBoard)an;
dd.pec = &GetEvalCube()->ec;
dd.pci = &ci;
dd.szOutput = szOutput;
dd.n = n;
if (RunAsyncProcess((AsyncFun)asyncDumpDecision, &dd, _("Evaluating position...")) == 0)
{
#if USE_GTK
if( fX )
GTKEval( szOutput );
else
#endif
outputl( szOutput );
}
}
extern command *FindHelpCommand( command *pcBase, char *sz,
char *pchCommand, char *pchUsage ) {
command *pc;
const char *pch;
int cch;
if( !( pch = NextToken( &sz ) ) )
return pcBase;
cch = strlen( pch );
for( pc = pcBase->pc; pc && pc->sz; pc++ )
if( !StrNCaseCmp( pch, pc->sz, cch ) )
break;
if( !pc || !pc->sz )
return NULL;
pch = pc->sz;
while( *pch )
*pchCommand++ = *pchUsage++ = *pch++;
*pchCommand++ = ' '; *pchCommand = 0;
*pchUsage++ = ' '; *pchUsage = 0;
if( pc->szUsage ) {
pch = gettext ( pc->szUsage );
while( *pch )
*pchUsage++ = *pch++;
*pchUsage++ = ' '; *pchUsage = 0;
}
if( pc->pc )
/* subcommand */
return FindHelpCommand( pc, sz, pchCommand, pchUsage );
else
/* terminal command */
return pc;
}
extern char* CheckCommand(char *sz, command *ac)
{
command *pc;
int cch;
char *pch = NextToken(&sz);
if (!pch)
return 0;
cch = strlen( pch );
for (pc = ac; pc->sz; pc++)
if (!StrNCaseCmp(pch, pc->sz, cch))
break;
if (!pc->sz)
return pch;
if (pc->pf)
{
return 0;
}
else
{
return CheckCommand(sz, pc->pc);
}
}
extern void CommandHelp( char *sz )
{
command *pc, *pcFull;
char szCommand[ 128 ], szUsage[ 128 ], *szHelp;
#if USE_GTK
if( fX ){
GTKHelp( sz );
return;
}
#endif
if( !( pc = FindHelpCommand( &cTop, sz, szCommand, szUsage ) ) ) {
outputf( _("No help available for topic `%s'"), sz );
output("\n");
return;
}
if( pc->szHelp )
/* the command has its own help text */
szHelp = gettext ( pc->szHelp );
else if( pc == &cTop )
/* top-level help isn't for any command */
szHelp = NULL;
else {
/* perhaps the command is an abbreviation; search for the full
version */
szHelp = NULL;
for( pcFull = acTop; pcFull->sz; pcFull++ )
if( pcFull->pf == pc->pf && pcFull->szHelp ) {
szHelp = gettext ( pcFull->szHelp );
break;
}
}
if( szHelp ) {
outputf( "%s- %s\n\n%s: %s", szCommand, szHelp, _("Usage"), szUsage);
if( pc->pc && pc->pc->sz )
outputf( "<%s>\n", _("subcommand") );
else
outputc( '\n' );
}
if( pc->pc && pc->pc->sz ) {
outputl( pc == &cTop ? _("Available commands:") :
_("Available subcommands:") );
pc = pc->pc;
for( ; pc->sz; pc++ )
if( pc->szHelp )
outputf( "%-15s\t%s\n", pc->sz, gettext ( pc->szHelp ) );
}
}
extern char *FormatMoveHint( char *sz, const matchstate *pms, movelist *pml,
int i, int fRankKnown,
int fDetailProb, int fShowParameters ) {
cubeinfo ci;
char szTemp[ 2048 ], szMove[ 32 ];
char *pc;
float *ar, *arStdDev;
float rEq, rEqTop;
GetMatchStateCubeInfo( &ci, pms );
strcpy ( sz, "" );
/* number */
if ( i && ! fRankKnown )
strcat( sz, " ?? " );
else
sprintf ( pc = strchr ( sz, 0 ),
" %4i. ", i + 1 );
/* eval */
sprintf ( pc = strchr ( sz, 0 ),
"%-14s %-28s %s: ",
FormatEval ( szTemp, &pml->amMoves[ i ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ i ].anMove ),
( !pms->nMatchTo || ( pms->nMatchTo && ! fOutputMWC ) ) ?
_("Eq.") : _("MWC") );
/* equity or mwc for move */
ar = pml->amMoves[ i ].arEvalMove;
arStdDev = pml->amMoves[ i ].arEvalStdDev;
rEq = pml->amMoves[ i ].rScore;
rEqTop = pml->amMoves[ 0 ].rScore;
strcat ( sz, OutputEquity ( rEq, &ci, TRUE ) );
/* difference */
if ( i )
sprintf ( pc = strchr ( sz, 0 ),
" (%s)\n",
OutputEquityDiff ( rEq, rEqTop, &ci ) );
else
strcat ( sz, "\n" );
/* percentages */
if ( fDetailProb ) {
switch ( pml->amMoves[ i ].esMove.et ) {
case EVAL_EVAL:
/* FIXME: add cubeless and cubeful equities */
strcat ( sz, " " );
strcat ( sz, OutputPercents ( ar, TRUE ) );
strcat ( sz, "\n" );
break;
case EVAL_ROLLOUT:
strcat ( sz,
OutputRolloutResult ( " ",
NULL,
( float (*)[NUM_ROLLOUT_OUTPUTS] )
ar,
( float (*)[NUM_ROLLOUT_OUTPUTS] )
arStdDev,
&ci,
0,
1,
pml->amMoves[ i ].esMove.rc.fCubeful ) );
break;
default:
break;
}
}
/* eval parameters */
if ( fShowParameters ) {
switch ( pml->amMoves[ i ].esMove.et ) {
case EVAL_EVAL:
strcat ( sz, " " );
strcat ( sz,
OutputEvalContext ( &pml->amMoves[ i ].esMove.ec, TRUE ) );
strcat ( sz, "\n" );
break;
case EVAL_ROLLOUT:
strcat ( sz,
OutputRolloutContext ( " ",
&pml->amMoves[ i ].esMove.rc ) );
break;
default:
break;
}
}
return sz;
#if 0
if ( !pms->nMatchTo || ( pms->nMatchTo && ! fOutputMWC ) ) {
/* output in equity */
float *ar, rEq, rEqTop;
ar = pml->amMoves[ 0 ].arEvalMove;
rEqTop = pml->amMoves[ 0 ].rScore;
if( !i ) {
if( fOutputWinPC )
sprintf( sz, " %4i. %-14s %-28s Eq.: %+6.3f\n"
" %5.1f%% %5.1f%% %5.1f%% -"
" %5.1f%% %5.1f%% %5.1f%%\n",
1, FormatEval ( szTemp, &pml->amMoves[ 0 ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ 0 ].anMove ),
rEqTop,
100.0 * ar[ 0 ], 100.0 * ar[ 1 ], 100.0 * ar[ 2 ],
100.0 * ( 1.0 - ar[ 0 ] ) , 100.0 * ar[ 3 ],
100.0 * ar[ 4 ] );
else
sprintf( sz, " %4i. %-14s %-28s Eq.: %+6.3f\n"
" %5.3f %5.3f %5.3f -"
" %5.3f %5.3f %5.3f\n",
1, FormatEval ( szTemp, &pml->amMoves[ 0 ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ 0 ].anMove ),
rEqTop,
ar[ 0 ], ar[ 1 ], ar[ 2 ],
( 1.0 - ar[ 0 ] ) , ar[ 3 ],
ar[ 4 ] );
} else {
ar = pml->amMoves[ i ].arEvalMove;
rEq = pml->amMoves[ i ].rScore;
if( fRankKnown )
sprintf( sz, " %4i.", i + 1 );
else
strcpy( sz, " ?? " );
if( fOutputWinPC )
sprintf( sz + 6, " %-14s %-28s Eq.: %+6.3f (%+6.3f)\n"
" %5.1f%% %5.1f%% %5.1f%% -"
" %5.1f%% %5.1f%% %5.1f%%\n",
FormatEval ( szTemp, &pml->amMoves[ i ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ i ].anMove ),
rEq, rEq - rEqTop,
100.0 * ar[ 0 ], 100.0 * ar[ 1 ], 100.0 * ar[ 2 ],
100.0 * ( 1.0 - ar[ 0 ] ) , 100.0 * ar[ 3 ],
100.0 * ar[ 4 ] );
else
sprintf( sz + 6, " %-14s %-28s Eq.: %+6.3f (%+6.3f)\n"
" %5.3f %5.3f %5.3f -"
" %5.3f %5.3f %5.3f\n",
FormatEval ( szTemp, &pml->amMoves[ i ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ i ].anMove ),
rEq, rEq - rEqTop,
ar[ 0 ], ar[ 1 ], ar[ 2 ],
( 1.0 - ar[ 0 ] ) , ar[ 3 ],
ar[ 4 ] );
}
} else {
/* output in mwc */
float *ar, rMWC, rMWCTop;
ar = pml->amMoves[ 0 ].arEvalMove;
rMWCTop = 100.0 * eq2mwc ( pml->amMoves[ 0 ].rScore, &ci );
if( !i ) {
if( fOutputWinPC )
sprintf( sz, " %4i. %-14s %-28s MWC: %7.3f%%\n"
" %5.1f%% %5.1f%% %5.1f%% -"
" %5.1f%% %5.1f%% %5.1f%%\n",
1, FormatEval ( szTemp, &pml->amMoves[ 0 ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ 0 ].anMove ),
rMWCTop,
100.0 * ar[ 0 ], 100.0 * ar[ 1 ], 100.0 * ar[ 2 ],
100.0 * ( 1.0 - ar[ 0 ] ) , 100.0 * ar[ 3 ],
100.0 * ar[ 4 ] );
else
sprintf( sz, " %4i. %-14s %-28s MWC: %7.3f%%\n"
" %5.3f %5.3f %5.3f -"
" %5.3f %5.3f %5.3f\n",
1, FormatEval ( szTemp, &pml->amMoves[ 0 ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ 0 ].anMove ),
rMWCTop,
ar[ 0 ], ar[ 1 ], ar[ 2 ],
( 1.0 - ar[ 0 ] ) , ar[ 3 ],
ar[ 4 ] );
} else {
ar = pml->amMoves[ i ].arEvalMove;
rMWC = 100.0 * eq2mwc ( pml->amMoves[ i ].rScore, &ci );
if( fRankKnown )
sprintf( sz, " %4i.", i + 1 );
else
strcpy( sz, " ?? " );
if( fOutputWinPC )
sprintf( sz + 6, " %-14s %-28s MWC: %7.3f%% (%+7.3f%%)\n"
" %5.1f%% %5.1f%% %5.1f%% -"
" %5.1f%% %5.1f%% %5.1f%%\n",
FormatEval ( szTemp, &pml->amMoves[ i ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ i ].anMove ),
rMWC, rMWC - rMWCTop,
100.0 * ar[ 0 ], 100.0 * ar[ 1 ], 100.0 * ar[ 2 ],
100.0 * ( 1.0 - ar[ 0 ] ) , 100.0 * ar[ 3 ],
100.0 * ar[ 4 ] );
else
sprintf( sz + 6, " %-14s %-28s MWC: %7.3f%% (%+7.3f%%)\n"
" %5.3f %5.3f %5.3f -"
" %5.3f %5.3f %5.3f\n",
FormatEval ( szTemp, &pml->amMoves[ i ].esMove ),
FormatMove( szMove, pms->anBoard,
pml->amMoves[ i ].anMove ),
rMWC, rMWC - rMWCTop,
ar[ 0 ], ar[ 1 ], ar[ 2 ],
( 1.0 - ar[ 0 ] ) , ar[ 3 ],
ar[ 4 ] );
}
}
return sz;
#endif
}
static void HintResigned( void )
{
float rEqBefore, rEqAfter;
static cubeinfo ci;
static decisionData dd;
GetMatchStateCubeInfo( &ci, &ms );
#if USE_BOARD3D
if (fX)
{ /* Stop waving flag, otherwise hangs */
BoardData* bd = BOARD(pwBoard)->board_data;
StopIdle3d(bd, bd->bd3d);
}
#endif
/* evaluate current position */
dd.pboard = msBoard();
dd.pci = &ci;
dd.pec = &GetEvalCube()->ec;
if (RunAsyncProcess((AsyncFun)asyncMoveDecisionE, &dd, _("Considering resignation...")) != 0)
return;
getResignEquities ( dd.aarOutput[0], &ci, ms.fResigned,
&rEqBefore, &rEqAfter );
#if USE_GTK
if ( fX ) {
GTKResignHint ( dd.aarOutput[0], rEqBefore, rEqAfter, &ci,
ms.nMatchTo && fOutputMWC );
return;
}
#endif
if ( ! ms.nMatchTo || ( ms.nMatchTo && ! fOutputMWC ) ) {
outputf ( "%s : %+6.3f\n", _("Equity before resignation"),
- rEqBefore );
outputf ( "%s : %+6.3f (%+6.3f)\n\n", _("Equity after resignation"),
- rEqAfter, rEqBefore - rEqAfter );
outputf ( "%s : %s\n\n", _("Correct resign decision"),
( rEqBefore - rEqAfter >= 0 ) ?
_("Accept") : _("Reject") );
}
else {
rEqBefore = eq2mwc ( - rEqBefore, &ci );
rEqAfter = eq2mwc ( - rEqAfter, &ci );
outputf ( "%s : %6.2f%%\n", _("Equity before resignation"),
rEqBefore * 100.0f );
outputf ( "%s : %6.2f%% (%6.2f%%)\n\n", _("Equity after resignation"),
rEqAfter * 100.0f,
100.0f * ( rEqAfter - rEqBefore ) );
outputf ( "%s : %s\n\n", _("Correct resign decision"),
( rEqAfter - rEqBefore >= 0 ) ?
_("Accept") : _("Reject") );
}
}
static int hint_cube(moverecord *pmr, cubeinfo *pci)
{
static decisionData dd;
if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE) {
/* no analysis performed yet */
dd.pboard = msBoard();
dd.pci = pci;
dd.pes = GetEvalCube();
if (RunAsyncProcess
((AsyncFun) asyncCubeDecision, &dd,
_("Considering cube action...")) != 0)
return -1;
pmr_cubedata_set(pmr, dd.pes, dd.aarOutput, dd.aarStdDev);
}
return 0;
}
static skilltype no_double_skill(moverecord *pmr, cubeinfo *pci)
{
float arDouble[4];
float eq = 0.0f;
cubedecision cd;
if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
return SKILL_NONE;
cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
switch (cd) {
case DOUBLE_TAKE:
case DOUBLE_BEAVER:
case REDOUBLE_TAKE:
eq = arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_TAKE];
break;
case DOUBLE_PASS:
case REDOUBLE_PASS:
eq = arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_DROP];
break;
default:
break;
}
return Skill(eq);
}
static skilltype double_skill(moverecord *pmr, cubeinfo *pci)
{
float arDouble[4];
float eq = 0.0f;
cubedecision cd;
if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
return SKILL_NONE;
cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
switch (cd) {
case NODOUBLE_TAKE:
case NODOUBLE_BEAVER:
case NO_REDOUBLE_TAKE:
case NO_REDOUBLE_BEAVER:
case TOOGOOD_TAKE:
case TOOGOODRE_TAKE:
eq = arDouble[OUTPUT_TAKE] - arDouble[OUTPUT_NODOUBLE];
break;
case TOOGOOD_PASS:
case TOOGOODRE_PASS:
eq = arDouble[OUTPUT_DROP] - arDouble[OUTPUT_NODOUBLE];
break;
default:
break;
}
return Skill(eq);
}
static skilltype drop_skill(moverecord *pmr, cubeinfo *pci)
{
float arDouble[4];
float eq = 0.0f;
cubedecision cd;
if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
return SKILL_NONE;
cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
switch (cd) {
case DOUBLE_TAKE:
case DOUBLE_BEAVER:
case REDOUBLE_TAKE:
case NODOUBLE_TAKE:
case NODOUBLE_BEAVER:
case NO_REDOUBLE_TAKE:
case NO_REDOUBLE_BEAVER:
case TOOGOOD_TAKE:
case TOOGOODRE_TAKE:
case OPTIONAL_DOUBLE_BEAVER:
case OPTIONAL_DOUBLE_TAKE:
case OPTIONAL_REDOUBLE_TAKE:
/* equity is for doubling player, invert for response */
eq = arDouble[OUTPUT_TAKE] - arDouble[OUTPUT_DROP];
break;
default:
break;
}
return Skill(eq);
}
static skilltype take_skill(moverecord *pmr, cubeinfo *pci)
{
float arDouble[4];
float eq = 0.0f;
cubedecision cd;
if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
return SKILL_NONE;
cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
switch (cd) {
case DOUBLE_PASS:
case REDOUBLE_PASS:
case TOOGOOD_PASS:
case TOOGOODRE_PASS:
case OPTIONAL_DOUBLE_PASS:
case OPTIONAL_REDOUBLE_PASS:
/* equity is for doubling player, invert for response */
eq = arDouble[OUTPUT_DROP] - arDouble[OUTPUT_TAKE];
break;
default:
break;
}
return Skill(eq);
}
static skilltype move_skill(moverecord *pmr)
{
move *move_i;
move *move_0;
if (pmr->n.iMove >= pmr->ml.cMoves || !pmr->ml.amMoves)
return SKILL_NONE;
move_i = &pmr->ml.amMoves[pmr->n.iMove];
move_0 = &pmr->ml.amMoves[0];
if (move_i->esMove.et == EVAL_NONE || move_0->esMove.et == EVAL_NONE)
return SKILL_NONE;
else
return Skill(move_i->rScore - move_0->rScore);
}
extern void find_skills(moverecord *pmr, const matchstate *pms, int did_double,
int did_take)
{
cubeinfo ci;
doubletype dt = DoubleType(pms->fDoubled, pms->fMove, pms->fTurn);
taketype tt = (taketype) dt;
GetMatchStateCubeInfo(&ci, pms);
if (pmr->mt != MOVE_NORMAL && pmr->mt != MOVE_DOUBLE
&& pmr->mt != MOVE_TAKE && pmr->mt != MOVE_DROP) {
pmr->n.stMove = SKILL_NONE;
pmr->stCube = SKILL_NONE;
return;
}
if (pmr->mt == MOVE_DOUBLE && dt != DT_NORMAL) {
pmr->stCube = SKILL_NONE;
return;
}
if (pmr->mt == MOVE_TAKE && tt > TT_NORMAL) {
pmr->stCube = SKILL_NONE;
return;
}
if (did_double == FALSE)
pmr->stCube = no_double_skill(pmr, &ci);
else if (did_double == TRUE)
pmr->stCube = double_skill(pmr, &ci);
else if (did_take == FALSE)
pmr->stCube = drop_skill(pmr, &ci);
else if (did_take == TRUE)
pmr->stCube = take_skill(pmr, &ci);
else
pmr->stCube = SKILL_NONE;
if (pmr->mt == MOVE_NORMAL && pmr->ml.cMoves > 0
&& pmr->n.iMove < pmr->ml.cMoves)
pmr->n.stMove = move_skill(pmr);
}
extern void hint_double(int show, int did_double)
{
static cubeinfo ci;
moverecord *pmr;
int hist;
doubletype dt = DoubleType ( ms.fDoubled, ms.fMove, ms.fTurn );
if (dt != DT_NORMAL)
{
if (show)
outputerrf( _("This decision is part of beaver/raccoon sequence and cannot be hinted"));
return;
}
GetMatchStateCubeInfo(&ci, &ms);
if (!GetDPEq(NULL, NULL, &ci)) {
outputerrf(_("You cannot double."));
return;
}
pmr = get_current_moverecord(&hist);
if (!pmr)
return;
if (hint_cube(pmr, &ci) < 0)
return;
if (hist && did_double == -1)
did_double = (pmr->mt == MOVE_DOUBLE) ? TRUE : FALSE;
find_skills(pmr, &ms, did_double, -1);
#if USE_GTK
if (fX) {
if (hist && show)
ChangeGame(NULL);
if (show)
GTKCubeHint(pmr, &ms, did_double, -1, hist);
return;
}
#endif
outputl(OutputCubeAnalysis(pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble, &ci));
}
extern void hint_take(int show, int did_take)
{
static cubeinfo ci;
moverecord *pmr;
int hist;
taketype tt = (taketype) DoubleType ( ms.fDoubled, ms.fMove, ms.fTurn );
if ( tt > TT_NORMAL )
{
if (show)
outputerrf( _("This decision is part of beaver/raccoon sequence and cannot be hinted"));
return;
}
GetMatchStateCubeInfo(&ci, &ms);
pmr = get_current_moverecord(&hist);
if (!pmr)
return;
if (hint_cube(pmr, &ci) < 0)
return;
if (hist && did_take == -1)
did_take = (pmr->mt == MOVE_TAKE) ? TRUE : FALSE;
find_skills(pmr, &ms, -1, did_take);
#if USE_GTK
if (fX) {
if (hist && show)
ChangeGame(NULL);
if (show)
GTKCubeHint(pmr, &ms, -1, did_take, hist);
return;
}
#endif
outputl(OutputCubeAnalysis(pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble, &ci));
}
extern void hint_move(char *sz, gboolean show)
{
unsigned int i;
char szBuf[1024];
int parse_n = ParseNumber(&sz);
unsigned int n = (parse_n <= 0) ? 10 : parse_n;
moverecord *pmr;
cubeinfo ci;
int hist;
GetMatchStateCubeInfo(&ci, &ms);
pmr = get_current_moverecord(&hist);
if(!pmr)
return;
if (pmr->esChequer.et == EVAL_NONE) {
movelist ml;
findData fd;
fd.pml = &ml;
fd.pboard = msBoard();
fd.auchMove = NULL;
fd.rThr = arSkillLevel[SKILL_DOUBTFUL];
fd.pci = &ci;
fd.pec = &GetEvalChequer()->ec;
fd.aamf = *GetEvalMoveFilter();
if ((RunAsyncProcess
((AsyncFun) asyncFindMove, &fd,
_("Considering move...")) != 0) || fInterrupt)
return;
pmr_movelist_set(pmr, GetEvalChequer(), &ml);
}
#if USE_GTK
if (!hist && fX)
GTKGetMove(pmr->n.anMove);
#endif
if (pmr->n.anMove[0] == -1)
{
pmr->n.iMove = UINT_MAX;
pmr->n.stMove = SKILL_NONE;
}
else if (pmr->n.anMove[0] != -1)
{
pmr->n.iMove = locateMove(msBoard(), pmr->n.anMove, &pmr->ml);
find_skills(pmr, &ms, FALSE, -1);
}
#if USE_GTK
if (fX) {
if (hist && show)
ChangeGame(NULL);
if (show)
GTKHint(pmr, hist);
return;
} else
#endif
if (!show)
return;
if (!pmr->ml.cMoves) {
outputl(_("There are no legal moves."));
return;
}
n = MIN(pmr->ml.cMoves, n);
for (i = 0; i < n; i++)
output(FormatMoveHint
(szBuf, &ms, &pmr->ml, i, TRUE, TRUE, TRUE));
}
extern void CommandHint( char *sz )
{
if( ms.gs != GAME_PLAYING ) {
outputl( _("You must set up a board first.") );
return;
}
/* hint on cube decision */
if( !ms.anDice[ 0 ] && !ms.fDoubled && ! ms.fResigned ) {
hint_double(TRUE, -1);
return;
}
/* Give hint on resignation */
if ( ms.fResigned ) {
HintResigned();
return;
}
/* Give hint on take decision */
if ( ms.fDoubled ) {
hint_take(TRUE, -1);
return;
}
/* Give hint on chequer play decision */
if ( ms.anDice[ 0 ] ) {
hint_move( sz, TRUE );
return;
}
}
static void
Shutdown( void ) {
RenderFinalise();
free_rngctx(rngctxCurrent);
free_rngctx(rngctxRollout);
FreeMatch();
ClearMatch();
#if USE_GTK
MoveListDestroy();
#endif
#if USE_MULTITHREAD
MT_Close();
#endif
EvalShutdown();
#if USE_PYTHON
PythonShutdown();
#endif
#if HAVE_SOCKETS
#ifdef WIN32
WSACleanup();
#endif
#endif
SoundWait();
}
/* Called on various exit commands -- e.g. EOF on stdin, "quit" command,
etc. If stdin is not a TTY, this should always exit immediately (to
avoid enless loops on EOF). If stdin is a TTY, and fConfirmNew is set,
and a game is in progress, then we ask the user if they're sure. */
extern void PromptForExit( void )
{
static int fExiting = FALSE;
#if USE_GTK
BoardData* bd = NULL;
if (fX)
bd = BOARD(pwBoard)->board_data;
#endif
if (fExiting)
return;
fExiting = TRUE;
if( fInteractive) {
fInterrupt = FALSE;
if (!get_input_discard())
{
fInterrupt = FALSE;
fExiting = FALSE;
return;
}
}
#if USE_BOARD3D
if (fX && (display_is_3d(bd->rd)))
{ /* Stop any 3d animations */
StopIdle3d(bd, bd->bd3d);
}
#endif
#if HAVE_SOCKETS
/* Close any open connections */
if( ap[0].pt == PLAYER_EXTERNAL )
closesocket( ap[0].h );
if( ap[1].pt == PLAYER_EXTERNAL )
closesocket( ap[1].h );
#endif
playSound ( SOUND_EXIT );
#if USE_BOARD3D
if (fX && display_is_3d(bd->rd) && bd->rd->closeBoardOnExit && bd->rd->fHinges3d)
CloseBoard3d(bd, bd->bd3d, bd->rd);
#endif
ProcessEvents();
SoundWait(); /* Wait for sound to finish before final close */
if( fInteractive )
PortableSignalRestore( SIGINT, &shInterruptOld );
#if USE_GTK
if (fX)
{
stop_board_expose(bd);
board_free_pixmaps(bd);
}
#if USE_BOARD3D
if (fX && gtk_gl_init_success)
Tidy3dObjects(bd->bd3d, bd->rd);
#endif
#endif
#if HAVE_LIBREADLINE
write_history( gnubg_histfile );
#endif /* HAVE_READLINE */
#if USE_GTK
if (gtk_main_level() == 1)
gtk_main_quit();
else
#endif
{
Shutdown();
exit( EXIT_SUCCESS );
}
}
extern void CommandNotImplemented( char *sz )
{
outputl( _("That command is not yet implemented.") );
}
extern void CommandQuit( char *sz )
{
PromptForExit();
}
extern void CommandRollout(char *sz)
{
float arOutput [ NUM_ROLLOUT_OUTPUTS ];
float arStdDev [ NUM_ROLLOUT_OUTPUTS ];
rolloutstat arsStatistics[ 2 ];
TanBoard anBoard;
cubeinfo ci;
char asz[1][40];
void *p;
if (CountTokens(sz) > 0) {
outputerrf("%s", _("The rollout command takes no arguments and only rollouts the current position"));
return;
}
if (ms.gs != GAME_PLAYING) {
outputerrf("%s", _("No position specified and no game in progress."));
return;
}
#if USE_GTK
if (fX)
GTKShowWarning(WARN_ROLLOUT, NULL);
#endif
sprintf(asz[0], _("Current Position"));
memcpy(anBoard, msBoard(), sizeof(TanBoard));
SetCubeInfo(&ci, ms.nCube, ms.fCubeOwner, ms.fMove, ms.nMatchTo, ms.anScore, ms.fCrawford, ms.fJacoby, nBeavers, ms.bgv);
RolloutProgressStart(&ci, 1, NULL, &rcRollout, asz, FALSE, &p);
GeneralEvaluationR(arOutput, arStdDev, arsStatistics, (ConstTanBoard)anBoard, &ci, &rcRollout, RolloutProgress, p);
RolloutProgressEnd(&p, FALSE);
}
static void LoadCommands(FILE * pf, char *szFile)
{
char sz[2048], *pch;
outputpostpone();
/* FIXME shouldn't restart sys calls on signals during this fgets */
while (fgets(sz, sizeof(sz), pf) != NULL) {
if ((pch = strchr(sz, '\n')))
*pch = 0;
if ((pch = strchr(sz, '\r')))
*pch = 0;
if (fInterrupt) {
outputresume();
return;
}
if (*sz == '#') /* Comment */
continue;
#if USE_PYTHON
if (!strcmp(sz, ">")) {
/* Python escape. */
/* Ideally we should be able to handle both > print 1+1 sys.exit() and > print 1+1
currently we only handle the latter... */
outputerrf("%s", _("Only Python commands supported, not multiline code"));
continue;
}
#endif /* USE_PYTHON */
HandleCommand(sz, acTop);
/* FIXME handle NextTurn events? */
}
if (ferror(pf)) {
outputerr(szFile);
}
outputresume();
}
extern void CommandLoadPython(char *sz)
{
sz = NextToken(&sz);
if (sz && *sz)
#if USE_PYTHON
LoadPythonFile(sz);
#else
outputl(_("This build of GNU Backgammon does not support Python"));
#endif
else
outputl(_("You must specify a file to load from."));
}
extern void CommandLoadCommands( char *sz )
{
FILE *pf;
sz = NextToken( &sz );
if( !sz || !*sz ) {
outputl( _("You must specify a file to load from.") );
return;
}
if( ( pf = g_fopen( sz, "r" ) ) ) {
LoadCommands( pf, sz );
fclose( pf );
} else
outputerr( sz );
}
extern void CommandCopy (char *sz)
{
char *aps[ 7 ] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
char szOut[2048];
char szCube[32], szPlayer0[MAX_NAME_LEN + 3], szPlayer1[MAX_NAME_LEN + 3],
szScore0[35], szScore1[35], szMatch[35];
char szRolled[ 32 ];
TanBoard anBoardTemp;
aps[0] = szPlayer0;
aps[6] = szPlayer1;
sprintf (aps[1] = szScore0,
ngettext("%d point", "%d points", ms.anScore[0]),
ms.anScore[0]);
sprintf (aps[5] = szScore1,
ngettext("%d point", "%d points", ms.anScore[1]),
ms.anScore[1]);
if (ms.fDoubled)
{
aps[ms.fTurn ? 4 : 2] = szCube;
sprintf (szPlayer0, "O: %s", ap[0].szName);
sprintf (szPlayer1, "X: %s", ap[1].szName);
sprintf (szCube, _("Cube offered at %d"), ms.nCube << 1);
}
else
{
sprintf (szPlayer0, "O: %s", ap[0].szName);
sprintf (szPlayer1, "X: %s", ap[1].szName);
aps[ms.fMove ? 4 : 2] = szRolled;
if (ms.anDice[0])
sprintf (szRolled, "%s %d%d", _("Rolled"), ms.anDice[0], ms.anDice[1]);
else if (!GameStatus (msBoard(), ms.bgv))
strcpy (szRolled, _("On roll"));
else
szRolled[0] = 0;
if (ms.fCubeOwner < 0)
{
aps[3] = szCube;
if (ms.nMatchTo)
sprintf (szCube, _("%d point match (Cube: %d)"), ms.nMatchTo,
ms.nCube);
else
sprintf (szCube, "(%s: %d)", _("Cube"), ms.nCube);
}
else
{
int cch = strlen (ap[ms.fCubeOwner].szName);
if (cch > 20)
cch = 20;
sprintf (szCube, "%c: %*s (%s: %d)", ms.fCubeOwner ? 'X' :
'O', cch, ap[ms.fCubeOwner].szName, _("Cube"), ms.nCube);
aps[ms.fCubeOwner ? 6 : 0] = szCube;
if (ms.nMatchTo)
sprintf (aps[3] = szMatch, _("%d point match"), ms.nMatchTo);
}
}
memcpy ( anBoardTemp, msBoard(), sizeof(TanBoard) );
if ( ! ms.fMove )
SwapSides ( anBoardTemp );
DrawBoard (szOut, (ConstTanBoard)anBoardTemp, ms.fMove, aps, MatchIDFromMatchState (&ms),
anChequers[ ms.bgv ] );
strcat (szOut, "\n");
TextToClipboard (szOut);
}
static void LoadRCFiles(void)
{
char *sz, *szz;
loading_rc = TRUE;
outputoff();
sz = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
szz = g_strdup_printf("'%s'", sz);
if (g_file_test(sz, G_FILE_TEST_EXISTS))
CommandLoadCommands(szz);
g_free(sz);
g_free(szz);
sz = g_build_filename(szHomeDirectory, "gnubgrc", NULL);
szz = g_strdup_printf("'%s'", sz);
if (g_file_test(sz, G_FILE_TEST_EXISTS))
CommandLoadCommands(szz);
g_free(sz);
g_free(szz);
outputon();
loading_rc = FALSE;
}
static void
SaveRNGSettings ( FILE *pf, const char *sz, rng rngCurrent, rngcontext *rngctx ) {
switch( rngCurrent ) {
case RNG_ANSI:
fprintf( pf, "%s rng ansi\n", sz );
break;
case RNG_BBS:
fprintf( pf, "%s rng bbs\n", sz ); /* FIXME save modulus */
break;
case RNG_BSD:
fprintf( pf, "%s rng bsd\n", sz );
break;
case RNG_ISAAC:
fprintf( pf, "%s rng isaac\n", sz );
break;
case RNG_MANUAL:
fprintf( pf, "%s rng manual\n", sz );
break;
case RNG_MD5:
fprintf( pf, "%s rng md5\n", sz );
break;
case RNG_MERSENNE:
fprintf( pf, "%s rng mersenne\n", sz );
break;
case RNG_RANDOM_DOT_ORG:
fprintf( pf, "%s rng random.org\n", sz );
break;
case RNG_FILE:
fprintf( pf, "%s rng file \"%s\"\n", sz, GetDiceFileName( rngctx ) );
break;
default:
break;
}
}
static void
SaveMoveFilterSettings ( FILE *pf,
const char *sz,
movefilter aamf [ MAX_FILTER_PLIES ][ MAX_FILTER_PLIES ] ) {
int i, j;
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
for (i = 0; i < MAX_FILTER_PLIES; ++i)
for (j = 0; j <= i; ++j) {
fprintf (pf, "%s %d %d %d %d %s\n",
sz,
i+1, j,
aamf[i][j].Accept,
aamf[i][j].Extra,
g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE,
"%0.3g", aamf[i][j].Threshold));
}
}
static void
SaveEvalSettings( FILE *pf, const char *sz, evalcontext *pec ) {
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gchar *szNoise = g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", pec->rNoise);
fprintf( pf, "%s plies %d\n"
"%s prune %s\n"
"%s cubeful %s\n"
"%s noise %s\n"
"%s deterministic %s\n",
sz, pec->nPlies,
sz, pec->fUsePrune ? "on" : "off",
sz, pec->fCubeful ? "on" : "off",
sz, szNoise,
sz, pec->fDeterministic ? "on" : "off" );
}
extern void
SaveRolloutSettings ( FILE *pf, const char *sz, rolloutcontext *prc ) {
char *pch;
int i; /* flags and stuff */
gchar szTemp1[G_ASCII_DTOSTR_BUF_SIZE];
gchar szTemp2[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_formatd(szTemp1, G_ASCII_DTOSTR_BUF_SIZE, "%05.4f", prc->rStdLimit);
g_ascii_formatd(szTemp2, G_ASCII_DTOSTR_BUF_SIZE, "%05.4f", prc->rJsdLimit);
fprintf ( pf,
"%s cubeful %s\n"
"%s varredn %s\n"
"%s quasirandom %s\n"
"%s initial %s\n"
"%s truncation enable %s\n"
"%s truncation plies %d\n"
"%s bearofftruncation exact %s\n"
"%s bearofftruncation onesided %s\n"
"%s later enable %s\n"
"%s later plies %d\n"
"%s trials %d\n"
"%s cube-equal-chequer %s\n"
"%s players-are-same %s\n"
"%s truncate-equal-player0 %s\n"
"%s limit enable %s\n"
"%s limit minimumgames %d\n"
"%s limit maxerror %s\n"
"%s jsd stop %s\n"
"%s jsd minimumgames %d\n"
"%s jsd limit %s\n",
sz, prc->fCubeful ? "on" : "off",
sz, prc->fVarRedn ? "on" : "off",
sz, prc->fRotate ? "on" : "off",
sz, prc->fInitial ? "on" : "off",
sz, prc->fDoTruncate ? "on" : "off",
sz, prc->nTruncate,
sz, prc->fTruncBearoff2 ? "on" : "off",
sz, prc->fTruncBearoffOS ? "on" : "off",
sz, prc->fLateEvals ? "on" : "off",
sz, prc->nLate,
sz, prc->nTrials,
sz, fCubeEqualChequer ? "on" : "off",
sz, fPlayersAreSame ? "on" : "off",
sz, fTruncEqualPlayer0 ? "on" : "off",
sz, prc->fStopOnSTD ? "on" : "off",
sz, prc->nMinimumGames,
sz, szTemp1,
sz, prc->fStopOnJsd ? "on" : "off",
sz, prc->nMinimumJsdGames,
sz, szTemp2
);
SaveRNGSettings ( pf, sz, prc->rngRollout, rngctxRollout );
/* chequer play and cube decision evalcontexts */
pch = malloc ( strlen ( sz ) + 50 );
strcpy ( pch, sz );
for ( i = 0; i < 2; i++ ) {
sprintf ( pch, "%s player %i chequerplay", sz, i );
SaveEvalSettings ( pf, pch, &prc->aecChequer[ i ] );
sprintf ( pch, "%s player %i cubedecision", sz, i );
SaveEvalSettings ( pf, pch, &prc->aecCube[ i ] );
sprintf ( pch, "%s player %i movefilter", sz, i );
SaveMoveFilterSettings ( pf, pch, prc->aaamfChequer[ i ] );
}
for ( i = 0; i < 2; i++ ) {
sprintf ( pch, "%s later player %i chequerplay", sz, i );
SaveEvalSettings ( pf, pch, &prc->aecChequerLate[ i ] );
sprintf ( pch, "%s later player %i cubedecision", sz, i );
SaveEvalSettings ( pf, pch, &prc->aecCubeLate[ i ] );
sprintf ( pch, "%s later player %i movefilter", sz, i );
SaveMoveFilterSettings ( pf, pch, prc->aaamfLate[ i ] );
}
sprintf (pch, "%s truncation cubedecision", sz);
SaveEvalSettings ( pf, pch, &prc->aecCubeTrunc );
sprintf (pch, "%s truncation chequerplay", sz );
SaveEvalSettings ( pf, pch, &prc->aecChequerTrunc);
free ( pch );
}
static void SaveEvalSetupSettings(FILE * pf, const char *sz, evalsetup *pes)
{
char szTemp[1024];
switch (pes->et) {
case EVAL_EVAL:
fprintf(pf, "%s type evaluation\n", sz);
break;
case EVAL_ROLLOUT:
fprintf(pf, "%s type rollout\n", sz);
break;
default:
break;
}
strcpy(szTemp, sz);
SaveEvalSettings(pf, strcat(szTemp, " evaluation"), &pes->ec);
strcpy(szTemp, sz);
SaveRolloutSettings(pf, strcat(szTemp, " rollout"), &pes->rc);
}
static void SaveAnalysisSettings(FILE * pf)
{
gchar aszThr[7][G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_formatd(aszThr[0], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_BAD]);
g_ascii_formatd(aszThr[1], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_DOUBTFUL]);
g_ascii_formatd(aszThr[2], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_GOOD]);
g_ascii_formatd(aszThr[3], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_BAD]);
g_ascii_formatd(aszThr[4], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_VERYBAD]);
g_ascii_formatd(aszThr[5], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_VERYGOOD]);
g_ascii_formatd(aszThr[6], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_VERYBAD]);
SaveEvalSetupSettings(pf, "set analysis chequerplay", &esAnalysisChequer);
SaveEvalSetupSettings(pf, "set analysis cubedecision", &esAnalysisCube);
SaveMoveFilterSettings(pf, "set analysis movefilter", aamfAnalysis);
SaveEvalSettings(pf, "set analysis luckanalysis", &ecLuck);
fprintf(pf, "set analysis threshold bad %s\n", aszThr[0]);
fprintf(pf, "set analysis threshold doubtful %s\n", aszThr[1]);
fprintf(pf, "set analysis threshold lucky %s\n", aszThr[2]);
fprintf(pf, "set analysis threshold unlucky %s\n", aszThr[3]);
fprintf(pf, "set analysis threshold verybad %s\n", aszThr[4]);
fprintf(pf, "set analysis threshold verylucky %s\n", aszThr[5]);
fprintf(pf, "set analysis threshold veryunlucky %s\n", aszThr[6]);
fprintf(pf, "set analysis cube %s\n", fAnalyseCube ? "on" : "off");
fprintf(pf, "set analysis luck %s\n", fAnalyseDice ? "on" : "off");
fprintf(pf, "set analysis moves %s\n", fAnalyseMove ? "on" : "off");
fprintf(pf, "set analysis player 0 analyse %s\n", afAnalysePlayers[0] ? "yes" : "no");
fprintf(pf, "set analysis player 1 analyse %s\n", afAnalysePlayers[1] ? "yes" : "no");
}
static void SaveImportExportSettings(FILE * pf)
{
int i;
if (default_import_folder && *default_import_folder)
fprintf(pf, "set import folder \"%s\"\n", default_import_folder);
if (default_export_folder && *default_export_folder)
fprintf(pf, "set export folder \"%s\"\n", default_export_folder);
if (default_sgf_folder && *default_sgf_folder)
fprintf(pf, "set sgf folder \"%s\"\n", default_sgf_folder);
fprintf(pf, "set export include annotations %s\n",
exsExport.fIncludeAnnotation ? "yes" : "no");
fprintf(pf, "set export include analysis %s\n",
exsExport.fIncludeAnalysis ? "yes" : "no");
fprintf(pf, "set export include statistics %s\n",
exsExport.fIncludeStatistics ? "yes" : "no");
fprintf(pf, "set export include matchinfo %s\n",
exsExport.fIncludeMatchInfo ? "yes" : "no");
fprintf(pf, "set export show board %d\n", exsExport.fDisplayBoard);
if (exsExport.fSide == 3)
fprintf(pf, "set export show player both\n");
else if (exsExport.fSide)
fprintf(pf, "set export show player %d\n", exsExport.fSide - 1);
fprintf(pf, "set export move number %d\n", exsExport.nMoves);
fprintf(pf, "set export moves parameters evaluation %s\n",
exsExport.afMovesParameters[0] ? "yes" : "no");
fprintf(pf, "set export moves parameters rollout %s\n",
exsExport.afMovesParameters[1] ? "yes" : "no");
fprintf(pf, "set export moves probabilities %s\n",
exsExport.fMovesDetailProb ? "yes" : "no");
for (i = 0; i < N_SKILLS; i++) {
if (i == SKILL_NONE)
fprintf(pf, "set export moves display unmarked %s\n",
exsExport.afMovesDisplay[i] ? "yes" : "no");
else
fprintf(pf, "set export moves display %s %s\n",
aszSkillTypeCommand[i], exsExport.afMovesDisplay[i] ? "yes" : "no");
}
fprintf(pf, "set export cube parameters evaluation %s\n",
exsExport.afCubeParameters[0] ? "yes" : "no");
fprintf(pf, "set export cube parameters rollout %s\n",
exsExport.afCubeParameters[1] ? "yes" : "no");
fprintf(pf, "set export cube probabilities %s\n", exsExport.fCubeDetailProb ? "yes" : "no");
for (i = 0; i < N_SKILLS; i++) {
if (i == SKILL_NONE)
fprintf(pf, "set export cube display unmarked %s\n",
exsExport.afCubeDisplay[i] ? "yes" : "no");
else
fprintf(pf, "set export cube display %s %s\n",
aszSkillTypeCommand[i], exsExport.afCubeDisplay[i] ? "yes" : "no");
}
fprintf(pf, "set export cube display actual %s\n",
exsExport.afCubeDisplay[EXPORT_CUBE_ACTUAL] ? "yes" : "no");
fprintf(pf, "set export cube display missed %s\n",
exsExport.afCubeDisplay[EXPORT_CUBE_MISSED] ? "yes" : "no");
fprintf(pf, "set export cube display close %s\n",
exsExport.afCubeDisplay[EXPORT_CUBE_CLOSE] ? "yes" : "no");
fprintf(pf, "set export html pictureurl \"%s\"\n", exsExport.szHTMLPictureURL);
fprintf(pf, "set export html type \"%s\"\n", aszHTMLExportType[exsExport.het]);
fprintf(pf, "set export html css %s\n", aszHTMLExportCSSCommand[exsExport.hecss]);
fprintf(pf, "set export png size %d\n", exsExport.nPNGSize);
fprintf(pf, "set export html size %d\n", exsExport.nHtmlSize);
}
#if USE_GTK
static void SaveGUISettings(FILE * pf)
{
const char *aszAnimation[] = { "none", "blink", "slide" };
const char *aszShowPips[N_GUI_SHOW_PIPS] = { "none", "pips", "epc", "wastage" };
int dummy;
SaveWindowSettings(pf);
WriteWarnings(pf);
if (fX)
GTKSaveSettings();
fprintf(pf, "set fullscreen %s\n", fFullScreen ? "on" : "off");
if (fFullScreen)
{
GetFullscreenWindowSettings(&dummy, &fShowIDs, &dummy);
fprintf(pf, "set gui showids %s\n", fShowIDs ? "on" : "off");
fShowIDs = FALSE;
}
else
fprintf(pf, "set gui showids %s\n", fShowIDs ? "on" : "off");
fprintf(pf, "set gui animation %s\n", aszAnimation[animGUI]);
fprintf(pf, "set gui animation speed %d\n", nGUIAnimSpeed);
fprintf(pf, "set gui beep %s\n", fGUIBeep ? "on" : "off");
fprintf(pf, "set gui dicearea %s\n", GetMainAppearance()->fDiceArea ? "on" : "off");
fprintf(pf, "set gui highdiefirst %s\n", fGUIHighDieFirst ? "on" : "off");
fprintf(pf, "set gui illegal %s\n", fGUIIllegal ? "on" : "off");
fprintf(pf, "set gui showpips %s\n", aszShowPips[gui_show_pips]);
fprintf(pf, "set gui dragtargethelp %s\n", fGUIDragTargetHelp ? "on" : "off");
fprintf(pf, "set gui usestatspanel %s\n", fGUIUseStatsPanel ? "on" : "off");
fprintf(pf, "set gui movelistdetail %s\n", showMoveListDetail ? "on" : "off");
fprintf(pf, "set gui grayedit %s\n", fGUIGrayEdit ? "on" : "off");
fprintf(pf, "set gui windowpositions %s\n", fGUISetWindowPos ? "on" : "off");
fprintf(pf, "set styledgamelist %s\n", fStyledGamelist ? "on" : "off");
fprintf(pf, "set delay %d\n", nDelay);
fprintf(pf, "set toolbar %d\n", nToolbarStyle);
if (!fToolbarShowing)
fputs("set toolbar off\n", pf);
#if USE_BOARD3D
if (fSync != -1)
fprintf(pf, "set vsync3d %s\n", fSync ? "yes" : "no");
#endif
}
#endif
static void SaveSoundSettings(FILE * pf)
{
int i;
fprintf(pf, "set sound enable %s\n", fSound ? "yes" : "no");
fprintf(pf, "set sound system command %s\n", sound_get_command());
for (i = 0; i < NUM_SOUNDS; ++i) {
char *file = GetSoundFile(i);
fprintf(pf, "set sound sound %s \"%s\"\n", sound_command[i], file);
g_free(file);
}
}
static void SavePlayerSettings(FILE * pf)
{
int i;
char szTemp[4096];
fprintf(pf, "set defaultnames \"%s\" \"%s\"\n", default_names[0], default_names[1]);
for (i = 0; i < 2; i++) {
fprintf(pf, "set player %d name %s\n", i, ap[i].szName);
switch (ap[i].pt) {
case PLAYER_GNU:
fprintf(pf, "set player %d gnubg\n", i);
sprintf(szTemp, "set player %d chequerplay", i);
SaveEvalSetupSettings(pf, szTemp, &ap[i].esChequer);
sprintf(szTemp, "set player %d cubedecision", i);
SaveEvalSetupSettings(pf, szTemp, &ap[i].esCube);
sprintf(szTemp, "set player %d movefilter", i);
SaveMoveFilterSettings(pf, szTemp, ap[i].aamf);
break;
case PLAYER_HUMAN:
fprintf(pf, "set player %d human\n", i);
break;
case PLAYER_EXTERNAL:
/* don't save external players */
break;
}
}
fprintf(pf, "set cheat enable %s\n", fCheat ? "on" : "off");
for (i = 0; i < 2; ++i)
fprintf(pf, "set cheat player %d roll %d\n", i, afCheatRoll[i] + 1);
}
static void SavePlayingSettings(FILE * pf)
{
fprintf(pf, "set automatic bearoff %s\n", fAutoBearoff ? "on" : "off");
fprintf(pf, "set automatic crawford %s\n", fAutoCrawford ? "on" : "off");
fprintf(pf, "set automatic game %s\n", fAutoGame ? "on" : "off");
fprintf(pf, "set automatic move %s\n", fAutoMove ? "on" : "off");
fprintf(pf, "set automatic roll %s\n", fAutoRoll ? "on" : "off");
}
static void SaveRuleSettings(FILE * pf)
{
fprintf(pf, "set automatic doubles %d\n", cAutoDoubles);
fprintf(pf, "set beavers %d\n", nBeavers);
fprintf(pf, "set jacoby %s\n", fJacoby ? "on" : "off");
fprintf(pf, "set matchlength %d\n", nDefaultLength);
fprintf(pf, "set variation %s\n", aszVariationCommands[bgvDefault]);
}
static void SaveEvaluationSettings(FILE * pf)
{
fprintf(pf, "set eval sameasanalysis %s\n", fEvalSameAsAnalysis ? "on" : "off");
SaveEvalSetupSettings(pf, "set evaluation chequerplay", &esEvalChequer);
SaveEvalSetupSettings(pf, "set evaluation cubedecision", &esEvalCube);
SaveMoveFilterSettings(pf, "set evaluation movefilter", aamfEval);
fprintf(pf, "set cache %d\n", GetEvalCacheEntries());
fprintf(pf, "set matchequitytable \"%s\"\n", miCurrent.szFileName);
fprintf(pf, "set invert matchequitytable %s\n", fInvertMET ? "on" : "off");
#if USE_MULTITHREAD
fprintf(pf, "set threads %d\n", MT_GetNumThreads());
#endif
}
static void SaveAutoSaveSettings(FILE * pf)
{
fprintf(pf, "set autosave time %d\n", nAutoSaveTime);
fprintf(pf, "set autosave rollout %s\n", fAutoSaveRollout ? "on" : "off");
fprintf(pf, "set autosave analysis %s\n", fAutoSaveAnalysis ? "on" : "off");
fprintf(pf, "set autosave confirm %s\n", fAutoSaveConfirmDelete ? "on" : "off");
}
static void SaveMiscSettings(FILE * pf)
{
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
fprintf(pf, "set tutor mode %s\n", fTutor ? "on" : "off");
fprintf(pf, "set tutor cube %s\n", fTutorCube ? "on" : "off");
fprintf(pf, "set tutor chequer %s\n", fTutorChequer ? "on" : "off");
fprintf(pf, "set tutor skill ");
if (TutorSkill == SKILL_VERYBAD)
fprintf(pf, "very bad\n");
else if (TutorSkill == SKILL_BAD)
fprintf(pf, "bad\n");
else
fprintf(pf, "doubtful\n");
fprintf(pf, "set clockwise %s\n", fClockwise ? "on" : "off");
fprintf(pf, "set confirm new %s\n", fConfirmNew ? "on" : "off");
fprintf(pf, "set confirm save %s\n", fConfirmSave ? "on" : "off");
fprintf(pf, "set cube use %s\n", fCubeUse ? "on" : "off");
fprintf(pf, "set display %s\n", fDisplay ? "on" : "off");
fprintf(pf, "set confirm default ");
if (nConfirmDefault == 1)
fprintf(pf, "yes\n");
else if (nConfirmDefault == 0)
fprintf(pf, "no\n");
else
fprintf(pf, "ask\n");
fprintf(pf, "set gotofirstgame %s\n", fGotoFirstGame ? "on" : "off");
fprintf(pf, "set output matchpc %s\n", fOutputMatchPC ? "on" : "off");
fprintf(pf, "set output mwc %s\n", fOutputMWC ? "on" : "off");
fprintf(pf, "set output rawboard %s\n", fOutputRawboard ? "on" : "off");
fprintf(pf, "set output winpc %s\n", fOutputWinPC ? "on" : "off");
fprintf(pf, "set output digits %d\n", fOutputDigits);
fprintf(pf, "set output errorratefactor %s\n",
g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%f", rErrorRateFactor));
fprintf(pf, "set prompt %s\n", szPrompt);
fprintf(pf, "set browser \"%s\"\n", get_web_browser());
fprintf(pf, "set priority nice %d\n", nThreadPriority);
fprintf(pf, "set ratingoffset %s\n",
g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%f", rRatingOffset));
}
extern void CommandSaveSettings(char *szParam)
{
FILE *pf;
char *szFile;
szParam = NextToken(&szParam);
if (!szParam || !*szParam) {
/* no filename parameter given -- save to default location */
szFile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
} else
szFile = g_strdup(szParam);
if (!strcmp(szFile, "-"))
pf = stdout;
else
pf = g_fopen(szFile, "w");
if (!pf) {
outputerr(szFile);
g_free(szFile);
return;
}
#if USE_GTK
/* the following may set errno because of a gtk bug so we do it
* first and reset errno afterwards */
if (fX)
RefreshGeometries();
#endif
errno = 0;
fprintf(pf,
"#\n"
"# GNU Backgammon command file\n"
"# generated by %s\n"
"#\n"
"# WARNING: The file `.gnubgautorc' is automatically "
"generated by the\n"
"# `save settings' command, and will be overwritten the next "
"time settings\n"
"# are saved. If you want to add startup commands manually, "
"you should\n" "# use `.gnubgrc' instead.\n" "\n", VERSION_STRING);
/* language preference */
fprintf(pf, "set lang %s\n", szLang);
#if USE_GTK
SaveGUISettings(pf);
#endif
SaveAnalysisSettings(pf);
SaveRenderingSettings(pf);
SavePlayingSettings(pf);
SaveRuleSettings(pf);
SaveEvaluationSettings(pf);
SavePlayerSettings(pf);
SaveRNGSettings(pf, "set", rngCurrent, rngctxCurrent);
SaveRolloutSettings(pf, "set rollout", &rcRollout);
SaveImportExportSettings(pf);
SaveSoundSettings(pf);
RelationalSaveSettings(pf);
SaveMiscSettings(pf);
SaveAutoSaveSettings(pf);
if (pf != stdout)
fclose(pf);
if (errno)
outputerr(szFile);
else {
outputf(_("Settings saved to %s."),
(!strcmp(szFile, "-")) ? _("standard output stream") : szFile);
output("\n");
}
g_free(szFile);
}
#if HAVE_LIBREADLINE
static command *pcCompleteContext;
static char *NullGenerator( const char *sz, int nState ) {
return NULL;
}
static char *GenerateKeywords( const char *sz, int nState ) {
static int cch;
static command *pc;
char *szDup;
if( !nState ) {
cch = strlen( sz );
pc = pcCompleteContext;
}
while( pc && pc->sz ) {
if( !StrNCaseCmp( sz, pc->sz, cch ) && pc->szHelp ) {
if( !( szDup = malloc( strlen( pc->sz ) + 1 ) ) )
return NULL;
strcpy( szDup, pc->sz );
pc++;
return szDup;
}
pc++;
}
return NULL;
}
static char *ERCompletion( const char *sz, int nState ) {
static int i, cch;
const char *pch;
char *szDup;
if( !nState ) {
cch = strlen( sz );
i = 0;
}
while( i < 2 ) {
pch = i++ ? "rollout" : "evaluation";
if( !StrNCaseCmp( sz, pch, cch ) ) {
if( !( szDup = malloc( strlen( pch ) + 1 ) ) )
return NULL;
return strcpy( szDup, pch );
}
++i;
}
return NULL;
}
static char *OnOffCompletion( const char *sz, int nState ) {
static unsigned int i;
static int cch;
static const char *asz[] = { "false", "no", "off", "on", "true", "yes" };
const char *pch;
char *szDup;
if( !nState ) {
cch = strlen( sz );
i = 0;
}
while( i < sizeof(asz)/sizeof(asz[0]) ) {
pch = asz[ i++ ];
if( !StrNCaseCmp( sz, pch, cch ) ) {
if( !( szDup = malloc( strlen( pch ) + 1 ) ) )
return NULL;
return strcpy( szDup, pch );
}
}
return NULL;
}
static char *PlayerCompletionGen( const char *sz, int nState, int fBoth ) {
static int i, cch;
const char *pch;
char *szDup;
if( !nState ) {
cch = strlen( sz );
i = 0;
}
while( i < ( fBoth ? 5 : 4 ) ) {
switch( i ) {
case 0:
pch = "0";
break;
case 1:
pch = "1";
break;
case 2:
pch = ap[ 0 ].szName;
break;
case 3:
pch = ap[ 1 ].szName;
break;
case 4:
pch = "both";
break;
default:
abort();
}
i++;
if( !StrNCaseCmp( sz, pch, cch ) ) {
if( !( szDup = malloc( strlen( pch ) + 1 ) ) )
return NULL;
return strcpy( szDup, pch );
}
}
return NULL;
}
static char *PlayerCompletion( const char *sz, int nState ) {
return PlayerCompletionGen( sz, nState, FALSE );
}
static char *PlayerCompletionBoth( const char *sz, int nState ) {
return PlayerCompletionGen( sz, nState, TRUE );
}
static command *FindContext( command *pc, char *szOrig, int ich ) {
char *sz = (char*) g_alloca(strlen( szOrig ) * sizeof(char) + 1);
char *pch, *pchCurrent;
command *pcResume = NULL;
pch = strcpy( sz, szOrig );
pch[ ich ] = 0;
do {
if( !( pchCurrent = NextToken( &pch ) ) )
/* no command */
return pc;
if( !pch )
/* incomplete command */
return pc;
if( pcResume ) {
pc = pcResume;
pcResume = NULL;
continue;
}
while( pc && pc->sz ) {
if( !StrNCaseCmp( pchCurrent, pc->sz, strlen( pchCurrent ) ) ) {
pc = pc->pc;
if( pc == acSetPlayer || pc == acSetRolloutPlayer ||
pc == acSetRolloutLatePlayer ||
pc == acSetAnalysisPlayer || pc == acSetCheatPlayer ) {
pcResume = pc;
pc = &cPlayerBoth;
}
break;
}
pc++;
}
} while( pcResume || ( pc && pc->sz ) );
if( pc && pc->pc ) {
/* dummy command for parameter completion */
if( !NextToken( &pch ) )
return pc;
}
/* the command is already complete */
return NULL;
}
static char **CompleteKeyword( const char *szText, int iStart, int iEnd)
{
if( fReadingOther )
return rl_completion_matches( szText, NullGenerator );
pcCompleteContext = FindContext( acTop, rl_line_buffer, iStart );
if( !pcCompleteContext )
return NULL;
if( pcCompleteContext == &cER )
return rl_completion_matches( szText, ERCompletion );
else if( pcCompleteContext == &cFilename ) {
rl_filename_completion_desired = TRUE;
return rl_completion_matches( szText,
rl_filename_completion_function );
} else if( pcCompleteContext == &cOnOff )
return rl_completion_matches( szText, OnOffCompletion );
else if( pcCompleteContext == &cPlayer )
return rl_completion_matches( szText, PlayerCompletion );
else if( pcCompleteContext == &cPlayerBoth )
return rl_completion_matches( szText, PlayerCompletionBoth );
else
return rl_completion_matches( szText, GenerateKeywords );
}
#endif
extern void Prompt( void )
{
#if HAVE_LIBREADLINE
if( !fInteractive || !isatty( STDIN_FILENO ) )
return;
#endif
g_print("%s", FormatPrompt() );
fflush( stdout );
}
#if USE_GTK
#if HAVE_LIBREADLINE
extern void ProcessInput(char *sz)
{
char *pchExpanded;
rl_callback_handler_remove();
fReadingCommand = FALSE;
if( !sz ) {
outputc( '\n' );
PromptForExit();
}
else
{
fInterrupt = FALSE;
/* expand history */
history_expand( sz, &pchExpanded );
if( *pchExpanded )
add_history( pchExpanded );
if( fX )
GTKDisallowStdin();
HandleCommand( pchExpanded, acTop );
free( pchExpanded );
}
if( fX )
GTKAllowStdin();
ResetInterrupt();
/* Recalculate prompt -- if we call nothing, then readline will
redisplay the old prompt. This isn't what we want: we either
want no prompt at all, yet (if NextTurn is going to be called),
or if we do want to prompt immediately, we recalculate it in
case the information in the old one is out of date. */
if( nNextTurn )
fNeedPrompt = TRUE;
else {
char *sz = locale_from_utf8(FormatPrompt());
rl_callback_handler_install( sz, ProcessInput );
g_free(sz);
fReadingCommand = TRUE;
}
}
#endif
/* Handle a command as if it had been typed by the user. */
extern void UserCommand(const char *szCommand)
{
char *sz;
g_return_if_fail(szCommand);
g_return_if_fail(*szCommand);
/* Unfortunately we need to copy the command, because it might be in
read-only storage and HandleCommand might want to modify it. */
sz = g_strdup(szCommand);
#ifndef WIN32
if (!fTTY || !fInteractive)
#endif
{
fInterrupt = FALSE;
HandleCommand(sz, acTop);
g_free(sz);
ResetInterrupt();
return;
}
/* Note that the command is always echoed to stdout; the output*()
functions are bypassed. */
#if HAVE_LIBREADLINE
rl_end = 0; /* crashes without this line */
rl_redisplay();
g_print("%s\n", sz);
ProcessInput(sz);
g_free(sz);
return;
#elif !defined(WIN32)
g_print("\n");
Prompt();
g_print("%s\n", sz);
fInterrupt = FALSE;
HandleCommand(sz, acTop);
g_free(sz);
ResetInterrupt();
if (nNextTurn)
Prompt();
else
fNeedPrompt = TRUE;
return;
#endif
}
extern gint NextTurnNotify( gpointer p )
{
NextTurn( TRUE );
ResetInterrupt();
if( fNeedPrompt )
{
#if HAVE_LIBREADLINE
if( fInteractive ) {
char *sz = locale_from_utf8(FormatPrompt());
rl_callback_handler_install( sz, ProcessInput );
g_free(sz);
fReadingCommand = TRUE;
} else
#endif
Prompt();
fNeedPrompt = FALSE;
}
return FALSE; /* remove idle handler, if GTK */
}
#endif
/* Read a line from stdin, and handle X and readline input if
* appropriate. This function blocks until a line is ready, and does
* not call HandleEvents(), and because fBusy will be set some X
* commands will not work. Therefore, it should not be used for
* reading top level commands. The line it returns has been allocated
* with malloc (as with readline()). */
extern char *GetInput( char *szPrompt )
{
char *sz;
char *pch;
char *pchConverted;
#if USE_GTK
g_assert( fTTY && !fX );
#endif
#if HAVE_LIBREADLINE
if( fInteractive ) {
char *prompt;
/* Using readline, but not X. */
if( fInterrupt )
return NULL;
fReadingOther = TRUE;
prompt = locale_from_utf8(szPrompt);
while( !( sz = readline( prompt ) ) ) {
outputc( '\n' );
PromptForExit();
}
g_free(prompt);
fReadingOther = FALSE;
if( fInterrupt )
return NULL;
pchConverted = locale_to_utf8(sz);
free( sz );
return pchConverted;
}
#endif
/* Not using readline or X. */
if( fInterrupt )
return NULL;
g_print("%s", szPrompt );
fflush( stdout );
sz = malloc( 256 ); /* FIXME it would be nice to handle longer strings */
clearerr( stdin );
pch = fgets( sz, 256, stdin );
if( fInterrupt ) {
free( sz );
return NULL;
}
if( !pch ) {
if( !isatty( STDIN_FILENO ) )
exit( EXIT_SUCCESS );
outputc( '\n' );
PromptForExit();
}
if( ( pch = strchr( sz, '\n' ) ) ) *pch = 0;
if( ( pch = strchr( sz, '\r' ) ) ) *pch = 0;
pchConverted = locale_to_utf8(sz);
free( sz );
return pchConverted;
}
/* Ask a yes/no question. Interrupting the question is considered a "no"
answer. */
extern int GetInputYN( char *szPrompt )
{
char *pch;
if (nConfirmDefault != -1)
{
outputf("%s %s\n", szPrompt, nConfirmDefault ? _("yes") : _("no"));
return nConfirmDefault;
}
if (!fInteractive)
return TRUE;
#if USE_GTK
if( fX )
return GTKGetInputYN( szPrompt );
#endif
if( fInterrupt )
return FALSE;
while( (pch = GetInput( szPrompt )) != 0 )
{
if( pch )
switch( *pch ) {
case 'y':
case 'Y':
g_free( pch );
return TRUE;
case 'n':
case 'N':
g_free( pch );
return FALSE;
default:
g_free( pch );
}
outputl( _("Please answer `y' or `n'.") );
}
return FALSE;
}
/* Like strncpy, except it does the right thing */
extern char *strcpyn( char *szDest, const char *szSrc, int cch )
{
char *pchDest = szDest;
const char *pchSrc = szSrc;
if( cch-- < 1 )
return szDest;
while( cch-- )
if( !( *pchDest++ = *pchSrc++ ) )
return szDest;
*pchDest = 0;
return szDest;
}
/* Write a string to stdout/status bar/popup window */
extern void output( const char *sz )
{
if( cOutputDisabled || !foutput_on)
return;
#if USE_GTK
if( fX ) {
GTKOutput( sz );
return;
}
#endif
fprintf(stdout, "%s", sz);
if( !isatty( STDOUT_FILENO ) )
fflush( stdout );
}
/* Write a string to stdout/status bar/popup window, and append \n */
extern void outputl( const char *sz )
{
if( cOutputDisabled || !foutput_on)
return;
#if USE_GTK
if( fX ) {
char *szOut = g_strdup_printf("%s\n", sz);
GTKOutput( szOut );
g_free(szOut);
return;
}
#endif
g_print("%s\n", sz);
if( !isatty( STDOUT_FILENO ) )
fflush( stdout );
}
/* Write a character to stdout/status bar/popup window */
extern void outputc( const char ch )
{
char sz[2];
sz[0] = ch;
sz[1] = '\0';
output( sz );
}
/* Write a string to stdout/status bar/popup window, printf style */
extern void outputf( const char *sz, ... )
{
va_list val;
va_start( val, sz );
outputv( sz, val );
va_end( val );
}
/* Write a string to stdout/status bar/popup window, vprintf style */
extern void outputv( const char *sz, va_list val )
{
char *szFormatted;
if( cOutputDisabled || !foutput_on)
return;
szFormatted = g_strdup_vprintf( sz, val );
output( szFormatted );
g_free( szFormatted );
}
/* Write an error message, perror() style */
extern void outputerr( const char *sz )
{
/* FIXME we probably shouldn't convert the charset of strerror() - yuck! */
outputerrf( "%s: %s", sz, strerror( errno ) );
}
/* Write an error message, fprintf() style */
extern void outputerrf( const char *sz, ... )
{
va_list val;
va_start( val, sz );
outputerrv( sz, val );
va_end( val );
}
/* Write an error message, vfprintf() style */
extern void outputerrv(const char *sz, va_list val)
{
char *szFormatted;
szFormatted = g_strdup_vprintf(sz, val);
#if USE_GTK
if (fX)
GTKOutputErr(szFormatted);
#endif
fprintf(stderr, "%s", szFormatted);
if (!isatty(STDOUT_FILENO))
fflush(stdout);
putc('\n', stderr);
g_free(szFormatted);
}
/* Signifies that all output for the current command is complete */
extern void outputx( void )
{
if( cOutputDisabled || cOutputPostponed || !foutput_on)
return;
#if USE_GTK
if( fX )
GTKOutputX();
#endif
}
/* Signifies that subsequent output is for a new command */
extern void outputnew( void )
{
if( cOutputDisabled || !foutput_on)
return;
#if USE_GTK
if( fX )
GTKOutputNew();
#endif
}
/* Disable output */
extern void outputoff( void )
{
cOutputDisabled++;
}
/* Enable output */
extern void outputon( void )
{
g_assert( cOutputDisabled );
cOutputDisabled--;
}
extern void CommandSetOutputOutput(char *sz)
{
SetToggle("output", &foutput_on, sz, _("output will be shown"), _("output will not be shown"));
return;
}
/* Temporarily disable outputx() calls */
extern void outputpostpone( void )
{
cOutputPostponed++;
}
/* Re-enable outputx() calls */
extern void outputresume( void )
{
g_assert( cOutputPostponed );
if( !--cOutputPostponed )
{
outputx();
}
}
static GTimeVal tvProgress;
static int ProgressThrottle( void ) {
GTimeVal tv, tvDiff;
g_get_current_time(&tv);
tvDiff.tv_sec = tv.tv_sec - tvProgress.tv_sec;
if( ( tvDiff.tv_usec = tv.tv_usec + 1000000 - tvProgress.tv_usec ) >=
1000000 )
tvDiff.tv_usec -= 1000000;
else
tvDiff.tv_sec--;
if( tvDiff.tv_sec || tvDiff.tv_usec >= 100000 ) {
/* sufficient time elapsed; record current time */
tvProgress.tv_sec = tv.tv_sec;
tvProgress.tv_usec = tv.tv_usec;
return 0;
}
/* insufficient time elapsed */
return -1;
}
extern void ProgressStart( const char *sz )
{
if( !fShowProgress )
return;
fInProgress = TRUE;
#if USE_GTK
if( fX ) {
GTKProgressStart( sz );
return;
}
#endif
if( sz ) {
fputs( sz, stdout );
fflush( stdout );
}
}
extern void
ProgressStartValue ( char *sz, int iMax ) {
if ( !fShowProgress )
return;
iProgressMax = iMax;
iProgressValue = 0;
pcProgress = sz;
fInProgress = TRUE;
#if USE_GTK
if( fX ) {
GTKProgressStartValue( sz, iMax );
return;
}
#endif
if( sz ) {
fputs( sz, stdout );
fflush( stdout );
}
}
extern void
ProgressValue ( int iValue ) {
if ( !fShowProgress || iProgressValue == iValue )
return;
iProgressValue = iValue;
if( ProgressThrottle() )
return;
#if USE_GTK
if( fX ) {
GTKProgressValue( iValue, iProgressMax );
return;
}
#endif
outputf ( "\r%s %d/%d\r", pcProgress, iProgressValue, iProgressMax );
fflush ( stdout );
}
extern void
ProgressValueAdd ( int iValue ) {
ProgressValue ( iProgressValue + iValue );
}
static gboolean Progress( gpointer unused )
{
static int i = 0;
static char ach[ 5 ] = "/-\\|";
if( !fShowProgress )
return FALSE;
if( ProgressThrottle() )
return TRUE;
#if USE_GTK
if( fX ) {
GTKProgress();
return TRUE;
}
#endif
putchar( ach[ i++ ] );
i &= 0x03;
putchar( '\b' );
fflush( stdout );
return TRUE;
}
#if !USE_MULTITHREAD
extern void CallbackProgress( void )
{
#if USE_GTK
if( fX )
{
GTKDisallowStdin();
if (fInProgress)
GTKSuspendInput();
while( gtk_events_pending() )
gtk_main_iteration();
if (fInProgress)
GTKResumeInput();
GTKAllowStdin();
}
#endif
if( fInProgress && !iProgressMax )
Progress(NULL);
}
#endif
extern void ProgressEnd( void )
{
int i;
if( !fShowProgress )
return;
fInProgress = FALSE;
iProgressMax = 0;
iProgressValue = 0;
pcProgress = NULL;
#if USE_GTK
if( fX ) {
GTKProgressEnd();
return;
}
#endif
putchar( '\r' );
for( i = 0; i < 79; i++ )
putchar( ' ' );
putchar( '\r' );
fflush( stdout );
}
static void
move_rc_files (void)
{
/* Move files to the new location. Remove this part when all users have had
* their files moved.*/
char *olddir, *oldfile, *newfile;
#ifdef WIN32
olddir = g_strdup (getDataDir());
#else
olddir = g_build_filename (szHomeDirectory, "..", NULL);
#endif
newfile = g_build_filename (szHomeDirectory, "gnubgautorc", NULL);
oldfile = g_build_filename (olddir, ".gnubgautorc", NULL);
if (g_file_test(oldfile, G_FILE_TEST_IS_REGULAR) && !g_file_test(newfile, G_FILE_TEST_EXISTS))
g_rename (oldfile, newfile);
g_free (oldfile);
g_free (newfile);
newfile = g_build_filename (szHomeDirectory, "gnubgrc", NULL);
oldfile = g_build_filename (olddir, ".gnubgrc", NULL);
if (g_file_test(oldfile, G_FILE_TEST_IS_REGULAR) && !g_file_test(newfile, G_FILE_TEST_EXISTS))
g_rename (oldfile, newfile);
g_free (oldfile);
g_free (newfile);
g_free(olddir);
}
/*
* Create $HOME/.gnubg if not existing
*
*/
static int
CreateGnubgDirectory (void)
{
char *newfile;
if (!g_file_test (szHomeDirectory, G_FILE_TEST_IS_DIR))
{
if (g_mkdir (szHomeDirectory, 0700) < 0)
{
outputerr (szHomeDirectory);
return -1;
}
}
newfile = g_build_filename (szHomeDirectory, "gnubgautorc", NULL);
if (!g_file_test (newfile, G_FILE_TEST_IS_REGULAR))
move_rc_files ();
g_free (newfile);
return 0;
}
extern void HandleInterrupt( int idSignal )
{
/* NB: It is safe to write to fInterrupt even if it cannot be read
atomically, because it is only used to hold a binary value. */
fInterrupt = TRUE;
}
static void BearoffProgress( unsigned int i )
{
#if USE_GTK
if( fX ) {
GTKBearoffProgress( i );
return;
}
#endif
putchar( "\\|/-"[ ( i / 1000 ) % 4 ] );
putchar( '\r' );
fflush( stdout );
}
static void VersionMessage(void)
{
g_print("%s\n%s\n",_(VERSION_STRING), _(aszCOPYRIGHT));
g_print("%s", _(intro_string));
}
#if HAVE_LIBREADLINE
static char *get_readline(void)
{
char *pchExpanded;
char *szInput;
char *sz;
char *prompt = locale_from_utf8(FormatPrompt());
while (!(szInput = readline(prompt))) {
outputc('\n');
PromptForExit();
}
g_free(prompt);
sz = locale_to_utf8(szInput);
free(szInput);
fInterrupt = FALSE;
history_expand(sz, &pchExpanded);
g_free(sz);
if (*pchExpanded)
add_history(pchExpanded);
return pchExpanded;
}
#endif
static char *get_stdin_line(void)
{
char sz[2048], *pch;
sz[0] = 0;
Prompt();
clearerr(stdin);
/* FIXME shouldn't restart sys calls on signals during this
fgets */
if (fgets(sz, sizeof(sz), stdin) == NULL)
{
if (ferror(stdin))
{
outputerr("stdin");
exit(EXIT_FAILURE);
}
else
{
Shutdown();
exit(EXIT_SUCCESS);
}
}
if ((pch = strchr(sz, '\n')))
*pch = 0;
if (feof(stdin)) {
if (!isatty(STDIN_FILENO)) {
Shutdown();
exit(EXIT_SUCCESS);
}
outputc('\n');
if (!sz[0])
PromptForExit();
return NULL;
}
fInterrupt = FALSE;
return g_strdup(sz);
}
static void run_cl(void)
{
char *line;
for (;;) {
#if HAVE_LIBREADLINE
if (fInteractive) {
line = get_readline();
HandleCommand(line, acTop);
free(line);
} else
#endif
{
line = get_stdin_line();
HandleCommand(line, acTop);
g_free(line);
}
while (fNextTurn)
NextTurn(TRUE);
ResetInterrupt();
}
}
static void init_language(char **lang)
{
char *szFile, szTemp[4096];
char *pch;
FILE *pf;
outputoff();
szFile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
if (!*lang && (pf = g_fopen(szFile, "r"))) {
while (fgets(szTemp, sizeof(szTemp), pf) != NULL)
{
if ((pch = strchr(szTemp, '\n'))) *pch = 0;
if ((pch = strchr(szTemp, '\r'))) *pch = 0;
if (!strncmp("set lang", szTemp, 8)) {
*lang = g_strdup(szTemp+9);
break;
}
}
if (ferror(pf))
outputerr(szFile);
fclose(pf);
}
g_free(szFile);
CommandSetLang(*lang);
g_free(*lang);
outputon();
}
static void setup_readline(void)
{
#if HAVE_LIBREADLINE
char *pch;
int i;
gnubg_histfile = g_build_filename(szHomeDirectory, "history", NULL);
rl_readline_name = "gnubg";
rl_basic_word_break_characters =
rl_filename_quote_characters = szCommandSeparators;
rl_completer_quote_characters = "\"'";
/* assume readline 4.2 or later */
rl_completion_entry_function = NullGenerator;
rl_attempted_completion_function = CompleteKeyword;
/* setup history */
read_history(gnubg_histfile);
using_history();
if ((pch = getenv("HISTSIZE")) && ((i = atoi(pch)) > 0))
stifle_history(i);
#endif
}
#if !USE_GTK
static void PushSplash(char *unused, char *heading, char *message)
{
}
#endif
static void init_nets(int nNewWeights, int fNoBearoff)
{
char *gnubg_weights = BuildFilename("gnubg.weights");
char *gnubg_weights_binary = BuildFilename("gnubg.wd");
EvalInitialise(gnubg_weights, gnubg_weights_binary, fNoBearoff,
fShowProgress ? BearoffProgress : NULL);
g_free(gnubg_weights);
g_free(gnubg_weights_binary);
}
extern int GetManualDice(unsigned int anDice[2])
{
char *pz;
char *sz = NULL;
int i;
#if USE_GTK
if (fX) {
if (GTKGetManualDice(anDice)) {
fInterrupt = 1;
return -1;
} else
return 0;
}
#endif
for (;;) {
TryAgain:
if (fInterrupt) {
anDice[0] = anDice[1] = 0;
return -1;
}
sz = GetInput(_("Enter dice: "));
if (fInterrupt) {
g_free(sz);
anDice[0] = anDice[1] = 0;
return -1;
}
/* parse input and read a couple of dice */
/* any string with two numbers is allowed */
pz = sz;
for (i = 0; i < 2; i++) {
while (*pz && ((*pz < '1') || (*pz > '6')))
pz++;
if (!*pz) {
outputl(_
("You must enter two numbers between 1 and 6."));
goto TryAgain;
}
anDice[i] = (int) (*pz - '0');
pz++;
}
g_free(sz);
return 0;
}
}
/*
* Fetch random numbers from www.random.org
*
*/
#if HAVE_SOCKETS
extern int getDiceRandomDotOrg(void)
{
#define RANDOMORGSITEPORT 80
#define RANDOMORGSITE "www.random.org"
#define BUFLENGTH 500
#define BUFLENGTH_STR STRINGIZE(BUFLENGTH)
#define RANDOMORGSITEPORT_STR STRINGIZE(RANDOMORGSITEPORT)
static int nCurrent = -1;
static int anBuf[BUFLENGTH];
static int nRead;
int h;
int cb;
int nBytesRead, i;
struct sockaddr *psa;
char szHostname[80];
char szHTTP[] =
"GET http://" RANDOMORGSITE "/integers/?num=" BUFLENGTH_STR "&min=0&max=5&col=1&base=10&format=plain&rnd=new HTTP/1.0\n" \
"User-Agent: GNUBG/" VERSION " (email: " PACKAGE_BUGREPORT ")\n\n";
char acBuf[4096];
/*
* Suggestions for improvements:
* - use proxy
*/
/*
* Return random number
*/
if ((nCurrent >= 0) && (nCurrent < nRead))
return anBuf[nCurrent++];
else {
outputf(_("Fetching %d random numbers from <%s>\n"), BUFLENGTH, RANDOMORGSITE);
outputx();
/* fetch new numbers */
/* open socket */
strcpy(szHostname, RANDOMORGSITE ":" RANDOMORGSITEPORT_STR);
if ((h = ExternalSocket(&psa, &cb, szHostname)) < 0) {
SockErr(szHostname);
return -1;
}
/* connect */
#ifdef WIN32
if (connect((SOCKET) h, (const struct sockaddr *) psa, cb)
< 0) {
#else
if ((connect(h, psa, cb)) < 0) {
#endif /* WIN32 */
SockErr(szHostname);
return -1;
}
/* read next set of numbers */
if (ExternalWrite(h, szHTTP, strlen(szHTTP) + 1) < 0) {
SockErr(szHTTP);
closesocket(h);
return -1;
}
/* read data from web-server */
#ifdef WIN32
/* reading from sockets doesn't work on Windows
use recv instead */
if (!
(nBytesRead =
recv((SOCKET) h, acBuf, sizeof(acBuf), 0))) {
#else
if (!(nBytesRead = read(h, acBuf, sizeof(acBuf)))) {
#endif
SockErr("reading data");
closesocket(h);
return -1;
}
/* close socket */
closesocket(h);
/* parse string */
outputl(_("Done."));
outputx();
i = 0;
nRead = 0;
for (i = 0; i < nBytesRead; i++) {
if ((acBuf[i] >= '0') && (acBuf[i] <= '5')) {
anBuf[nRead] = 1 + (int) (acBuf[i] - '0');
nRead++;
}
}
nCurrent = 1;
return anBuf[0];
}
}
#else
static int (*getRandomDiceDotOrg)(void) = NULL;
#endif /* HAVE_SOCKETS */
static void init_rng(void)
{
if (!(rngctxCurrent = InitRNG(NULL, NULL, TRUE, rngCurrent))) {
printf("%s\n", _("Failure setting up RNG"));
exit(EXIT_FAILURE);
}
if (!(rngctxRollout = InitRNG(&rcRollout.nSeed, NULL,
TRUE, rcRollout.rngRollout))) {
printf("%s\n", _("Failure setting up RNG for rollout."));
exit(EXIT_FAILURE);
}
/* we don't want rollouts to use the same seed as normal dice (which
could happen if InitRNG had to use the current time as a seed) -- mix
it up a little bit */
rcRollout.nSeed ^= 0x792A584B;
}
#if defined(WIN32) && HAVE_SOCKETS
static void init_winsock()
{
short wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(1, 1);
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
outputerr("Windows sockets initialisation");
}
}
#endif
static char *matchfile_from_argv(char *sz)
{
char *pchMatch;
#ifdef WIN32
if (g_path_is_absolute(sz))
pchMatch = g_strdup_printf("'%s'", sz);
else {
char *tmp =
g_build_filename(g_get_current_dir(), sz, NULL);
pchMatch = g_strdup_printf("'%s'", tmp);
g_free(tmp);
}
#else
pchMatch = g_strdup_printf("'%s'", sz);
#endif
return pchMatch;
}
static void init_defaults(void)
{
/* init some html export options */
exsExport.szHTMLPictureURL = g_strdup("html-images/");
exsExport.szHTMLExtension = g_strdup("png");
SetMatchDate(&mi);
strcpy(ap[1].szName, g_get_user_name());
strcpy(default_names[1], g_get_user_name());
ListCreate(&lMatch);
IniStatcontext(&scMatch);
szHomeDirectory =
g_build_filename(g_get_home_dir(), ".gnubg", NULL);
}
static void init_autosave(void)
{
char *backupdir;
backupdir = g_build_filename(szHomeDirectory, "backup", NULL);
g_mkdir(backupdir, 0700);
g_free(backupdir);
}
static void null_debug (const gchar* dom, GLogLevelFlags logflags, const gchar* message, gpointer unused)
{
}
int main(int argc, char *argv[])
{
#if USE_GTK
GtkWidget *pwSplash = NULL;
#else
char *pwSplash = NULL;
#endif
char *pchMatch = NULL;
char *met = NULL;
static char *pchCommands = NULL, *pchPythonScript = NULL, *lang = NULL;
static int nNewWeights = 0, fNoRC = FALSE, fNoBearoff = FALSE, fNoX = FALSE, fSplash = FALSE, fNoTTY =
FALSE, show_version = FALSE, debug = FALSE;
GOptionEntry ao[] = {
{"no-bearoff", 'b', 0, G_OPTION_ARG_NONE, &fNoBearoff,
N_("Do not use bearoff database"), NULL},
{"commands", 'c', 0, G_OPTION_ARG_FILENAME, &pchCommands,
N_("Evaluate commands in FILE and exit"), "FILE"},
{"lang", 'l', 0, G_OPTION_ARG_STRING, &lang,
N_("Set language to LANG"), "LANG"},
{"new-weights", 'n', 0, G_OPTION_ARG_INT, &nNewWeights,
N_("Create new neural net (of size N)"), "N"},
{"python", 'p', 0, G_OPTION_ARG_FILENAME, &pchPythonScript,
N_("Evaluate Python code in FILE and exit"), "FILE"},
{"quiet", 'q', 0, G_OPTION_ARG_NONE, &fQuiet,
N_("Disable sound effects"), NULL},
{"no-rc", 'r', 0, G_OPTION_ARG_NONE, &fNoRC,
N_("Do not read .gnubgrc and .gnubgautorc commands"),
NULL},
{"splash", 'S', 0, G_OPTION_ARG_NONE, &fSplash,
N_("Show gtk splash screen"), NULL},
{"tty", 't', 0, G_OPTION_ARG_NONE, &fNoX,
N_("Start the command line instead of using the graphical interface"), NULL},
{"version", 'v', 0, G_OPTION_ARG_NONE, &show_version,
N_("Show version information and exit"), NULL},
{"window-system-only", 'w', 0, G_OPTION_ARG_NONE, &fNoTTY,
N_("Ignore tty input when using the graphical interface"), NULL},
{"debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
N_("Turn on debug"), NULL},
{"datadir", 'D', 0, G_OPTION_ARG_STRING, &datadir,
N_("Specify location of general data"), NULL},
{"pkgdatadir", 'P', 0, G_OPTION_ARG_STRING, &pkg_datadir,
N_("Specify location of program specific data"), NULL},
{"docdir", 'O', 0, G_OPTION_ARG_STRING, &docdir,
N_("Specify location of program documentation"), NULL},
{NULL, 0, 0, 0, NULL, NULL, NULL}
};
GError *error = NULL;
GOptionContext *context;
#if USE_MULTITHREAD
MT_InitThreads();
#endif
/* set language */
init_defaults();
#if USE_GTK
gtk_disable_setlocale();
#endif
init_language(&lang);
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
bind_textdomain_codeset(PACKAGE, GNUBG_CHARSET);
/* parse command line options */
context = g_option_context_new("[file.sgf]");
g_option_context_add_main_entries(context, ao, PACKAGE);
#if USE_GTK
g_option_context_add_group(context, gtk_get_option_group(FALSE));
#endif
g_option_context_parse(context, &argc, &argv, &error);
g_option_context_free(context);
if (error) {
outputerrf("%s\n", error->message);
exit(EXIT_FAILURE);
}
if (argc > 1 && *argv[1])
pchMatch = matchfile_from_argv(argv[1]);
if (!debug)
g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, &null_debug, NULL);
#ifdef WIN32
/* data directory: initialise to the path where gnubg is installed */
{
const char *szDataDirectory = getDataDir();
_chdir(szDataDirectory);
}
/* Create a mutex so install can check if it's running */
CreateMutex(NULL, FALSE, "GNU Backgammon Mutex");
#endif
/* print version and exit if -v option given */
VersionMessage();
if (show_version)
exit(EXIT_SUCCESS);
if (CreateGnubgDirectory())
exit(EXIT_FAILURE);
init_autosave();
RenderInitialise();
#ifdef WIN32
fNoTTY = TRUE;
#endif
#if USE_GTK
/* -t option not given */
if (!fNoX)
InitGTK(&argc, &argv);
if (fX) {
fTTY = !fNoTTY && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO);
fInteractive = fShowProgress = TRUE;
if (fSplash)
pwSplash = CreateSplash();
} else
#endif
{
fInteractive = isatty(STDIN_FILENO);
fShowProgress = isatty(STDOUT_FILENO);
}
if (fInteractive) {
PortableSignal(SIGINT, HandleInterrupt, &shInterruptOld, FALSE);
setup_readline();
}
PushSplash(pwSplash, _("Initialising"), _("Random number generator"));
init_rng();
PushSplash(pwSplash, _("Initialising"), _("match equity table"));
met = BuildFilename2("met", "Rockwell-Kazaross.xml");
InitMatchEquity(met);
g_free(met);
PushSplash(pwSplash, _("Initialising"), _("neural nets"));
init_nets(nNewWeights, fNoBearoff);
#if defined(WIN32) && HAVE_SOCKETS
PushSplash(pwSplash, _("Initialising"), _("Windows sockets"));
init_winsock();
#endif
#if USE_PYTHON
PushSplash(pwSplash, _("Initialising"), _("Python"));
PythonInitialise(argv[0]);
#endif
SetExitSoundOff();
/* -r option given */
if (!fNoRC) {
PushSplash(pwSplash, _("Loading"), _("User Settings"));
LoadRCFiles();
}
fflush(stdout);
fflush(stderr);
#if USE_MULTITHREAD
/* Make sure threads started */
MT_StartThreads();
#endif
/* start-up sound */
playSound(SOUND_START);
#if USE_GTK
if (fX) {
if (!fTTY) {
g_set_print_handler(>KOutput);
g_set_printerr_handler(>KOutputErr);
}
RunGTK(pwSplash, pchCommands, pchPythonScript, pchMatch);
Shutdown();
exit(EXIT_SUCCESS);
}
#endif
if (pchMatch)
CommandImportAuto(pchMatch);
/* -c option given */
if (pchCommands) {
fInteractive = FALSE;
CommandLoadCommands(pchCommands);
Shutdown();
exit(EXIT_SUCCESS);
}
/* -p option given */
if (pchPythonScript) {
#if USE_PYTHON
fInteractive = FALSE;
LoadPythonFile(pchPythonScript);
Shutdown();
exit(EXIT_SUCCESS);
#else
outputerrf(_("GNU Backgammon build without Python."));
exit(EXIT_FAILURE);
#endif
}
run_cl();
return (EXIT_FAILURE);
}
/*
* Command: convert normalised money equity to match winning chance.
*
* The result is written to stdout.
* FIXME: implement GTK version.
* FIXME: allow more parameters (match score, match length)
*
* Input:
* sz: string with equity
*
* Output:
* none.
*
*/
extern void CommandEq2MWC ( char *sz )
{
float rEq;
cubeinfo ci;
if ( ! ms.nMatchTo ) {
outputl ( _("Command only valid in match play") );
return;
}
rEq = (float)ParseReal ( &sz );
if ( rEq == ERR_VAL ) rEq = 0.0;
GetMatchStateCubeInfo( &ci, &ms );
outputf ( "%s = %+6.3f: %6.2f%%\n", _("MWC for equity"),
-1.0, 100.0 * eq2mwc ( -1.0, &ci ) );
outputf ( "%s = %+6.3f: %6.2f%%\n", _("MWC for equity"),
+1.0, 100.0 * eq2mwc ( +1.0, &ci ) );
outputf ( "%s:\n", _("By linear interpolation") );
outputf ( "%s = %+6.3f: %6.2f%%\n", _("MWC for equity"),
rEq, 100.0 * eq2mwc ( rEq, &ci ) );
}
/*
* Command: convert match winning chance to normalised money equity.
*
* The result is written to stdout.
* FIXME: implement GTK version.
* FIXME: allow more parameters (match score, match length)
*
* Input:
* sz: string with match winning chance
*
* Output:
* none.
*
*/
extern void CommandMWC2Eq ( char *sz )
{
float rMwc;
cubeinfo ci;
if ( ! ms.nMatchTo ) {
outputl ( _("Command only valid in match play") );
return;
}
GetMatchStateCubeInfo( &ci, &ms );
rMwc = (float)ParseReal ( &sz );
if ( rMwc == ERR_VAL ) rMwc = eq2mwc ( 0.0, &ci );
if ( rMwc > 1.0 ) rMwc /= 100.0;
outputf ( "%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"),
100.0 * eq2mwc ( -1.0, &ci ), -1.0 );
outputf ( "%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"),
100.0 * eq2mwc ( +1.0, &ci ), +1.0 );
outputf ( "%s:\n", _("By linear interpolation") );
outputf ( "%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"),
100.0 * rMwc, mwc2eq ( rMwc, &ci ) );
}
static void
swapGame ( listOLD *plGame ) {
listOLD *pl;
moverecord *pmr;
int n;
for( pl = plGame->plNext; pl != plGame; pl = pl->plNext ) {
pmr = pl->p;
switch ( pmr->mt ) {
case MOVE_GAMEINFO:
/* swap score */
n = pmr->g.anScore[ 0 ];
pmr->g.anScore[ 0 ] = pmr->g.anScore[ 1 ];
pmr->g.anScore[ 1 ] = n;
/* swap winner */
if ( pmr->g.fWinner > -1 )
pmr->g.fWinner = ! pmr->g.fWinner;
/* swap statcontext */
/* recalculate statcontext later ... */
break;
case MOVE_DOUBLE:
case MOVE_TAKE:
case MOVE_DROP:
case MOVE_NORMAL:
case MOVE_RESIGN:
case MOVE_SETDICE:
pmr->fPlayer = ! pmr->fPlayer;
break;
case MOVE_SETBOARD:
case MOVE_SETCUBEVAL:
/*no op */
break;
case MOVE_SETCUBEPOS:
if ( pmr->scp.fCubeOwner > -1 )
pmr->scp.fCubeOwner = ! pmr->scp.fCubeOwner;
break;
}
}
}
extern void CommandSwapPlayers ( char *sz )
{
listOLD *pl;
char *pc;
int n;
/* swap individual move records */
for( pl = lMatch.plNext; pl != &lMatch; pl = pl->plNext ) {
swapGame ( pl->p );
}
/* swap player names */
pc = g_strdup ( ap[ 0 ].szName );
strcpy ( ap[ 0 ].szName, ap[ 1 ].szName );
strcpy ( ap[ 1 ].szName, pc );
g_free ( pc );
/* swap player ratings */
pc = mi.pchRating[ 0 ];
mi.pchRating[ 0 ] = mi.pchRating[ 1 ];
mi.pchRating[ 1 ] = pc;
/* swap current matchstate */
if ( ms.fTurn > -1 )
ms.fTurn = ! ms.fTurn;
if ( ms.fMove > -1 )
ms.fMove = ! ms.fMove;
if ( ms.fCubeOwner > -1 )
ms.fCubeOwner = ! ms.fCubeOwner;
n = ms.anScore[ 0 ];
ms.anScore[ 1 ] = ms.anScore[ 0 ];
ms.anScore[ 0 ] = n;
SwapSides ( ms.anBoard );
#if USE_GTK
if ( fX ) {
GTKSet ( ap );
GTKRegenerateGames();
ChangeGame(NULL);
}
#endif
ShowBoard();
}
extern int
confirmOverwrite ( const char *sz, const int f ) {
char *szPrompt;
int i;
/* check for existing file */
if ( f && ! access ( sz, F_OK ) ) {
szPrompt = (char *) malloc ( 50 + strlen ( sz ) );
sprintf ( szPrompt, _("File \"%s\" exists. Overwrite? "), sz );
i = GetInputYN ( szPrompt );
free ( szPrompt );
return i;
}
else
return TRUE;
}
extern void
setDefaultFileName (char *path)
{
g_free (szCurrentFolder);
g_free (szCurrentFileName);
DisectPath (path, NULL, &szCurrentFileName, &szCurrentFolder);
#if USE_GTK
if (fX)
{
gchar *title =
g_strdup_printf ("%s (%s)", _("GNU Backgammon"), szCurrentFileName);
gtk_window_set_title (GTK_WINDOW (pwMain), title);
g_free (title);
}
#endif
}
extern void
DisectPath (const char *path, const char *extension, char **name, char **folder)
{
char *fnn, *pc;
if (!path)
{
*folder = NULL;
*name = NULL;
return;
}
*folder = g_path_get_dirname (path);
fnn = g_path_get_basename (path);
pc = strrchr (fnn, '.');
if (pc)
*pc = '\0';
*name = g_strconcat (fnn, extension, NULL);
g_free (fnn);
}
/* ask for confirmation if this is a sub-optimal play
* returns TRUE if player wants to re-think the move
*/
static int GetAdviceAnswer( char *sz ) {
char *pch;
#if USE_GTK
if( fX )
return GtkTutor ( sz );
#endif
if( fInterrupt )
return FALSE;
while( (pch = GetInput( sz )) != 0 )
{
if( pch )
switch( *pch ) {
case 'y':
case 'Y':
free( pch );
return TRUE;
case 'n':
case 'N':
free( pch );
return FALSE;
default:
free( pch );
}
outputl( _("Please answer `y' or `n'.") );
}
return FALSE;
}
extern int GiveAdvice( skilltype Skill )
{
char *sz;
/* should never happen */
if ( !fTutor )
return FALSE;
switch (Skill) {
case SKILL_VERYBAD:
sz = _( "You may be about to make a very bad play" );
break;
case SKILL_BAD:
sz = _( "You may be about to make a bad play" );
break;
case SKILL_DOUBTFUL:
sz = _( "You may be about to make a doubtful play" );
break;
default:
return (TRUE);
}
if (Skill > TutorSkill)
return (TRUE);
{
int ret;
char *buf = g_strdup_printf("%s. %s", sz, _("Are you sure?"));
ret = GetAdviceAnswer( buf );
g_free(buf);
return ret;
}
}
extern void TextToClipboard(const char *sz)
{
#if USE_GTK
if (fX)
GTKTextToClipboard(sz);
#else
/* no clipboard: just write string */
outputl(sz);
#endif
}
void
CommandDiceRolls (char *sz) {
int n;
char *pch;
unsigned int anDice[2];
if ( (pch = NextToken( &sz ) ) ) {
n = ParseNumber( &pch );
while (n-- > 0) {
RollDice( anDice, &rngCurrent, rngctxCurrent );
printf( "%d %d\n", anDice[ 0 ], anDice[ 1 ] );
}
}
}
#if HAVE_LIBREADLINE
extern void
CommandHistory( char *sz ) {
int i;
HIST_ENTRY *phe;
for ( i = 0; i < history_length; ++i ) {
phe = history_get( i + history_base );
outputf( "%6d %s\n", i + history_base, phe->line );
}
}
#endif /* HAVE_LIBREADLINE */
extern void CommandClearHint(char *sz)
{
pmr_hint_destroy();
outputl(_("Analysis used for `hint' has been cleared"));
}
/*
* Description: Calculate Effective Pip Counts (EPC), either
* by lookup in one-sided databases or by doing a
* one-sided rollout.
* Parameters :
* Input anBoard (the board)
* fOnlyRace: only calculate EPCs for race positions
* Output arEPC (the calculate EPCs)
* pfSource (source of EPC; 0 = database, 1 = OSR)
*
* Returns: 0 on success, non-zero otherwise
*
*/
extern int
EPC( const TanBoard anBoard, float *arEPC, float *arMu, float *arSigma,
int *pfSource, const int fOnlyBearoff ) {
const float x = ( 2 * 3 + 3 * 4 + 4 * 5 + 4 * 6 + 6 * 7 +
5* 8 + 4 * 9 + 2 * 10 + 2 * 11 + 1 * 12 +
1 * 16 + 1 * 20 + 1 * 24 ) / 36.0f;
if ( isBearoff ( pbc1, anBoard ) ) {
/* one sided in-memory database */
float ar[ 4 ];
unsigned int n;
int i;
for ( i = 0; i < 2; ++i ) {
n = PositionBearoff( anBoard[ i ], pbc1->nPoints, pbc1->nChequers );
if ( BearoffDist( pbc1, n, NULL, NULL, ar, NULL, NULL ) )
return -1;
if ( arEPC )
arEPC[ i ] = x * ar[ 0 ];
if ( arMu )
arMu[ i ] = ar[ 0 ];
if ( arSigma )
arSigma[ i ] = ar[ 1 ];
}
if ( pfSource )
*pfSource = 0;
return 0;
}
else if ( isBearoff( pbcOS, anBoard ) ) {
/* one sided in-memory database */
float ar[ 4 ];
unsigned int n;
int i;
for ( i = 0; i < 2; ++i ) {
n = PositionBearoff( anBoard[ i ], pbcOS->nPoints, pbcOS->nChequers );
if ( BearoffDist( pbcOS, n, NULL, NULL, ar, NULL, NULL ) )
return -1;
if ( arEPC )
arEPC[ i ] = x * ar[ 0 ];
if ( arMu )
arMu[ i ] = ar[ 0 ];
if ( arSigma )
arSigma[ i ] = ar[ 1 ];
}
if ( pfSource )
*pfSource = 0;
return 0;
}
else if ( ! fOnlyBearoff ) {
/* one-sided rollout */
int nTrials = 5760;
float arMux[ 2 ];
float ar[ 5 ];
int i;
raceProbs ( anBoard, nTrials, ar, arMux );
for ( i = 0; i < 2; ++i ) {
if ( arEPC )
arEPC[ i ] = x * arMux[ i ];
if ( arMu )
arMu[ i ] = arMux[ i ];
if ( arSigma )
arSigma[ i ] = -1.0f; /* raceProbs cannot calculate sigma! */
}
if ( pfSource )
*pfSource = 1;
return 0;
}
/* code not reachable */
return -1;
}
#if HAVE_LIBREADLINE
extern char *locale_from_utf8(const char *sz)
{
char *ret;
GError *error = NULL;
g_assert(sz);
ret = g_locale_from_utf8(sz, strlen(sz), NULL, NULL, &error);
if (error) {
g_print("locale_from_utf8 failed: %s\n", error->message);
g_error_free(error);
ret = g_strdup(sz);
}
return ret;
}
#endif
extern char *locale_to_utf8(const char *sz)
{
char *ret;
GError *error = NULL;
g_assert(sz);
ret = g_locale_to_utf8(sz, strlen(sz), NULL, NULL, &error);
if (error) {
g_print("locale_to_utf8 failed: %s\n", error->message);
g_error_free(error);
ret = g_strdup(sz);
}
return ret;
}
#ifdef WIN32
/* WIN32 setlocale must be manipulated through putenv to be gettext compatible */
char * SetupLanguage (const char *newLangCode)
{
static char *org_lang=NULL;
char *lang;
if (!org_lang)
org_lang = g_win32_getlocale();
if (!newLangCode || !strcmp (newLangCode, "system") || !strcmp (newLangCode, ""))
lang = g_strdup_printf("LANG=%s", org_lang);
else
lang = g_strdup_printf("LANG=%s", newLangCode);
putenv(lang);
g_free(lang);
return(setlocale(LC_ALL, ""));
}
#else
char *SetupLanguage(const char *newLangCode)
{
static char *org_lang = NULL;
char *result = NULL;
if (!org_lang) {
org_lang = g_strdup(setlocale(LC_ALL, ""));
if (!org_lang) {
outputerrf(_("Locale in your environment not supported by "
"C library. Falling back to C locale.\n"));
org_lang = g_strdup("C");
}
}
if (newLangCode && *newLangCode && strcmp(newLangCode, "system") != 0) {
g_setenv("LC_ALL", newLangCode, TRUE);
result = setlocale(LC_ALL, newLangCode);
return (result);
}
g_setenv("LC_ALL", org_lang, TRUE);
result = setlocale(LC_ALL, org_lang);
return (result);
}
#endif
void asyncFindMove(findData *pfd)
{
if( FindnSaveBestMoves( pfd->pml, ms.anDice[ 0 ], ms.anDice[ 1 ], pfd->pboard,
pfd->auchMove, pfd->rThr, pfd->pci, pfd->pec, pfd->aamf) < 0)
MT_SetResultFailed();
}
void asyncDumpDecision(decisionData *pdd)
{
if (DumpPosition( pdd->pboard, pdd->szOutput, pdd->pec, pdd->pci,
fOutputMWC, fOutputWinPC, pdd->n, MatchIDFromMatchState( &ms ) ) != 0 )
MT_SetResultFailed();
}
void asyncScoreMove(scoreData *psd)
{
if ( ScoreMove (NULL, psd->pm, psd->pci, psd->pec, psd->pec->nPlies ) < 0 )
MT_SetResultFailed();
}
void asyncEvalRoll(decisionData *pdd)
{
EvaluateRoll (pdd->aarOutput[0], ms.anDice[0], ms.anDice[1], pdd->pboard, pdd->pci, pdd->pec );
}
void asyncAnalyzeMove(moveData *pmd)
{
if (AnalyzeMove ( pmd->pmr, pmd->pms, plGame, NULL, pmd->pesChequer, pmd->pesCube, pmd->aamf, NULL, NULL ) < 0 )
MT_SetResultFailed();
}
void asyncGammonRates(decisionData *pdd)
{
if ( getCurrentGammonRates ( pdd->aarRates, pdd->aarOutput[0], pdd->pboard, pdd->pci, pdd->pec ) < 0 )
MT_SetResultFailed();
}
void asyncMoveDecisionE(decisionData *pdd)
{
if ( GeneralEvaluationE ( pdd->aarOutput[0], pdd->pboard, pdd->pci, pdd->pec) < 0 )
MT_SetResultFailed();
}
void asyncCubeDecisionE(decisionData *pdd)
{
if ( GeneralCubeDecisionE ( pdd->aarOutput, pdd->pboard, pdd->pci, pdd->pec, pdd->pes ) < 0 )
MT_SetResultFailed();
}
void asyncCubeDecision(decisionData *pdd)
{
if ( GeneralCubeDecision( pdd->aarOutput, pdd->aarStdDev, pdd->aarsStatistics,
pdd->pboard, pdd->pci, pdd->pes, NULL, NULL ) < 0 )
MT_SetResultFailed();
}
extern int RunAsyncProcess(AsyncFun fn, void *data, const char *msg)
{
int ret;
#if USE_MULTITHREAD
Task *pt = (Task*)malloc(sizeof(Task));
pt->pLinkedTask = NULL;
pt->fun = fn;
pt->data = data;
MT_AddTask(pt, FALSE);
#endif
ProgressStart(msg);
#if USE_MULTITHREAD
ret = MT_WaitForTasks(Progress, 100, FALSE);
#else
asyncRet = 0;
fn(data); /* Just call function in single threaded build */
ret = asyncRet;
#endif
ProgressEnd();
return ret;
}
extern void ProcessEvents(void)
{
#if USE_GTK
if (fX)
{
while(gtk_events_pending())
gtk_main_iteration();
}
else
#endif
{
while (g_main_context_pending(NULL))
g_main_context_iteration(NULL, TRUE);
}
}
extern int CheckGameExists(void)
{
if (plGame)
{
return TRUE;
}
else
{
outputl( _("No game in progress.") );
return FALSE;
}
}
extern gboolean save_autosave(gpointer unused)
{
int fd;
FILE *pf;
listOLD *pl;
g_return_val_if_fail(plGame, FALSE);
MT_Exclusive();
if (autosave)
g_unlink(autosave);
autosave = g_build_filename(szHomeDirectory, "backup", "XXXXXX.sgf", NULL);
fd = g_mkstemp(autosave);
if (fd < 0) {
g_free(autosave);
autosave = NULL;
MT_Release();
return FALSE;
}
close(fd);
g_unlink(autosave);
pf = g_fopen(autosave, "w");
if (!pf) {
autosave = NULL;
MT_Release();
return FALSE;
}
for (pl = lMatch.plNext; pl != &lMatch; pl = pl->plNext)
SaveGame(pf, pl->p);
fclose(pf);
MT_Release();
return TRUE;
}
extern void delete_autosave(void)
{
if (autosave)
{
g_unlink(autosave);
g_free(autosave);
autosave = NULL;
}
}
extern int get_input_discard(void)
{
if (fInterrupt)
return FALSE;
if (autosave && fAutoSaveConfirmDelete)
{
if (!GetInputYN(_("Are you sure you want to discard the current match and your existing autosave? ")))
return FALSE;
g_unlink(autosave);
g_free(autosave);
autosave = NULL;
return TRUE;
}
if (ms.gs == GAME_PLAYING && fConfirmNew)
return GetInputYN(_("Are you sure you want to discard the current match? "));
return TRUE;
}