1 /*
2 * gnubg.c
3 *
4 * by Gary Wong <gtw@gnu.org>, 1998, 1999, 2000, 2001, 2002, 2003.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of version 3 or later of the GNU General Public License as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Id: gnubg.c,v 1.998 2018/06/16 15:24:05 plm Exp $
20 */
21
22 #include "config.h"
23
24 #include "gnubgmodule.h"
25 #include "glib-ext.h"
26
27 #include <sys/types.h>
28 #include <stdlib.h>
29 #if defined(HAVE_UNISTD_H)
30 #include <unistd.h>
31 #endif
32 #include <errno.h>
33 #include <stdio.h>
34 #include <glib.h>
35 #include <glib/gstdio.h>
36 #include <signal.h>
37 #include <ctype.h>
38 #include <locale.h>
39 #ifdef WIN32
40 #include <direct.h>
41 #include <io.h>
42 #endif
43
44 #if defined(HAVE_LIB_READLINE)
45 static char *gnubg_histfile;
46 #include <readline/history.h>
47 #include <readline/readline.h>
48 static int fReadingOther;
49 static char szCommandSeparators[] = " \t\n\r\v\f";
50 #endif
51
52 #include "analysis.h"
53 #include "backgammon.h"
54 #include "dice.h"
55 #include "drawboard.h"
56 #include "eval.h"
57 #include "sgf.h"
58 #include "export.h"
59 #include "import.h"
60 #include "matchequity.h"
61 #include "matchid.h"
62 #include "positionid.h"
63 #include "render.h"
64 #include "renderprefs.h"
65 #include "rollout.h"
66 #include "sound.h"
67 #include "progress.h"
68 #include "osr.h"
69 #include "format.h"
70 #include "relational.h"
71 #include "credits.h"
72 #include "external.h"
73 #include "neuralnet.h"
74 #include "util.h"
75
76 #if defined(LIBCURL_PROTOCOL_HTTPS)
77 #include <curl/curl.h>
78 #endif
79
80 #if defined(HAVE_SOCKETS)
81 #if defined(HAVE_SYS_SOCKET_H)
82 #include <sys/socket.h>
83 #include <netinet/in.h>
84 #include <arpa/inet.h>
85 #include <netdb.h>
86 #include <sys/un.h>
87 #endif /* #if HAVE_SYS_SOCKET_H */
88 #ifdef WIN32
89 #include <winsock2.h>
90 #include <ws2tcpip.h>
91 #define inet_aton(ip,addr) (addr)->s_addr = inet_addr(ip), 1
92 #define inet_pton(fam,ip,addr) (addr)->s_addr = inet_addr(ip), 1
93 #endif /* #ifdef WIN32 */
94 #endif /* #if HAVE_SOCKETS */
95
96 #if defined(USE_GTK)
97 #include <gtk/gtk.h>
98 #include "gtkboard.h"
99 #include "gtkgame.h"
100 #include "gtkprefs.h"
101 #include "gtksplash.h"
102 #include "gtkchequer.h"
103 #include "gtkwindows.h"
104 #endif
105
106 #if defined(USE_BOARD3D)
107 #include "fun3d.h"
108 #endif
109 #include "multithread.h"
110 #include "openurl.h"
111
112 #if defined(MSDOS) || defined(__MSDOS__) || defined(WIN32)
113 #define NO_BACKSLASH_ESCAPES 1
114 #endif
115
116 #if defined(USE_GTK)
117 int fX = FALSE; /* use X display */
118 unsigned int nDelay = 300;
119 int fNeedPrompt = FALSE;
120 #if defined(HAVE_LIB_READLINE)
121 int fReadingCommand;
122 #endif
123 #endif
124
125 static int fNoRC = FALSE;
126 static char *autosave = NULL;
127 static int loading_rc = FALSE;
128
129 const char *intro_string =
130 N_("This program comes with ABSOLUTELY NO WARRANTY; for details type `show warranty'.\n"
131 "This is free software, and you are welcome to redistribute it under certain conditions; type `show copying' for details.\n");
132 char *szLang = NULL;
133
134 const char szDefaultPrompt[] = "(\\p) ", *szPrompt = szDefaultPrompt;
135 int fInteractive;
136
137 matchstate ms = {
138 {{0}, {0}}, /* anBoard */
139 {0}, /* anDice */
140 -1, /* fTurn */
141 0, /* fResigned */
142 0, /* fResignationDeclined */
143 FALSE, /* fDoubled */
144 0, /* cGames */
145 -1, /* fMove */
146 -1, /* fCubeOwner */
147 FALSE, /* fCrawford */
148 FALSE, /* fPostCrawford */
149 0, /* nMatchTo */
150 {0, 0}, /* anScore */
151 1, /* nCube */
152 0, /* cBeavers */
153 VARIATION_STANDARD, /*bgv */
154 TRUE, /* fCubeUse */
155 TRUE, /* fJacoby */
156 GAME_NONE /* gs */
157 };
158
159 ConstTanBoard
msBoard(void)160 msBoard(void)
161 {
162 return (ConstTanBoard) ms.anBoard;
163 }
164
165 matchinfo mi;
166
167 float rRatingOffset = 2050;
168 int fAnalyseCube = TRUE;
169 int fAnalyseDice = TRUE;
170 int fAnalyseMove = TRUE;
171 int fAutoBearoff = FALSE;
172 int fAutoCrawford = 1;
173 int fAutoGame = TRUE;
174 int fAutoMove = FALSE;
175 int fAutoRoll = TRUE;
176 int fCheat = FALSE;
177 int fConfirmNew = TRUE;
178 int fConfirmSave = TRUE;
179 int nAutoSaveTime = 15;
180 int fAutoSaveRollout = FALSE;
181 int fAutoSaveAnalysis = FALSE;
182 int fAutoSaveConfirmDelete = TRUE;
183
184 /* FIXME: This is at best useless, at worst misleading, as a global flag.
185 * It should be deduced from the rollout context when needed.
186 * See for instance show.c:ShowRollout() around line 270 */
187 int fCubeEqualChequer = FALSE;
188
189 int fCubeUse = TRUE;
190 int fDisplay = TRUE;
191 int fFullScreen = FALSE;
192 int fGotoFirstGame = FALSE;
193 int fInvertMET = FALSE;
194 int fJacoby = TRUE;
195 int fOutputRawboard = FALSE;
196 int fPlayersAreSame = TRUE;
197 int fRecord = TRUE;
198 int fShowProgress;
199 int fStyledGamelist = TRUE;
200 int fTruncEqualPlayer0 = TRUE;
201 int fTutorChequer = TRUE;
202 int fTutorCube = TRUE;
203 int fTutor = FALSE;
204 int fEvalSameAsAnalysis = FALSE;
205 int fJustSwappedPlayers = FALSE;
206 int nConfirmDefault = -1;
207 int nThreadPriority = 0;
208 int nToolbarStyle = 2;
209 unsigned int afCheatRoll[2] = { 0, 0 };
210
211 unsigned int cAutoDoubles = 0;
212 unsigned int nBeavers = 3;
213 unsigned int nDefaultLength = 7;
214
215 #if defined(USE_BOARD3D)
216 int fSync = -1; /* Not set */
217 int fResetSync = FALSE; /* May need to wait for main window */
218 #endif
219
220 skilltype TutorSkill = SKILL_DOUBTFUL;
221 int nTutorSkillCurrent = 0;
222
223 char *szCurrentFileName = NULL;
224 char *szCurrentFolder = NULL;
225
226
227 int fNextTurn = FALSE, fComputing = FALSE;
228
229 float rEvalsPerSec = -1.0f;
230 float arLuckLevel[] = {
231 0.6f, /* LUCK_VERYBAD */
232 0.3f, /* LUCK_BAD */
233 0, /* LUCK_NONE */
234 0.3f, /* LUCK_GOOD */
235 0.6f /* LUCK_VERYGOOD */
236 }, arSkillLevel[] = {
237 0.16f, /* SKILL_VERYBAD */
238 0.08f, /* SKILL_BAD */
239 0.04f, /* SKILL_DOUBTFUL */
240 0 /* SKILL_NONE */
241 };
242
243 #include "movefilters.inc"
244
245 rngcontext *rngctxRollout = NULL;
246
247 rolloutcontext rcRollout = {
248 {
249 /* player 0/1 cube decision */
250 {TRUE, 2, TRUE, TRUE, 0.0},
251 {TRUE, 2, TRUE, TRUE, 0.0}
252 },
253 {
254 /* player 0/1 chequerplay */
255 {TRUE, 0, TRUE, TRUE, 0.0},
256 {TRUE, 0, TRUE, TRUE, 0.0}
257 },
258
259 {
260 /* player 0/1 late cube decision */
261 {TRUE, 2, TRUE, TRUE, 0.0},
262 {TRUE, 2, TRUE, TRUE, 0.0}
263 },
264 {
265 /* player 0/1 late chequerplay */
266 {TRUE, 0, TRUE, TRUE, 0.0},
267 {TRUE, 0, TRUE, TRUE, 0.0}
268 },
269 /* truncation point cube and chequerplay */
270 {TRUE, 2, TRUE, TRUE, 0.0},
271 {TRUE, 2, TRUE, TRUE, 0.0},
272
273 /* move filters */
274 {MOVEFILTER_NORMAL, MOVEFILTER_NORMAL},
275 {MOVEFILTER_NORMAL, MOVEFILTER_NORMAL},
276
277 TRUE, /* cubeful */
278 TRUE, /* variance reduction */
279 FALSE, /* initial position */
280 TRUE, /* rotate */
281 TRUE, /* truncate at BEAROFF2 for cubeless rollouts */
282 TRUE, /* truncate at BEAROFF2_OS for cubeless rollouts */
283 FALSE, /* late evaluations */
284 FALSE, /* Truncation enabled */
285 FALSE, /* no stop on STD */
286 FALSE, /* no stop on JSD */
287 FALSE, /* no move stop on JSD */
288 10, /* truncation */
289 1296, /* number of trials */
290 5, /* late evals start here */
291 RNG_MERSENNE, /* RNG */
292 0, /* seed */
293 324, /* minimum games */
294 0.01f, /* stop when std's are lower than 0.01 */
295 324, /* minimum games */
296 2.33f, /* stop when best has j.s.d. for 99% confidence */
297 0, /* nGamesDone */
298 0.0, /* rStoppedOnJSD */
299 0, /* nSkip */
300 };
301
302 /* parameters for `eval' and `hint' */
303
304 #define EVALSETUP_WORLDCLASS { \
305 /* evaltype */ \
306 EVAL_EVAL, \
307 /* evalcontext */ \
308 { TRUE, 2, TRUE, TRUE, 0.0 }, \
309 /* rolloutcontext */ \
310 { \
311 { \
312 { FALSE, 2, TRUE, TRUE, 0.0 }, /* player 0 cube decision */ \
313 { FALSE, 2, TRUE, TRUE, 0.0 } /* player 1 cube decision */ \
314 }, \
315 { \
316 { FALSE, 0, TRUE, TRUE, 0.0 }, /* player 0 chequerplay */ \
317 { FALSE, 0, TRUE, TRUE, 0.0 } /* player 1 chequerplay */ \
318 }, \
319 { \
320 { FALSE, 2, TRUE, TRUE, 0.0 }, /* p 0 late cube decision */ \
321 { FALSE, 2, TRUE, TRUE, 0.0 } /* p 1 late cube decision */ \
322 }, \
323 { \
324 { FALSE, 0, TRUE, TRUE, 0.0 }, /* p 0 late chequerplay */ \
325 { FALSE, 0, TRUE, TRUE, 0.0 } /* p 1 late chequerplay */ \
326 }, \
327 { FALSE, 2, TRUE, TRUE, 0.0 }, /* truncate cube decision */ \
328 { FALSE, 2, TRUE, TRUE, 0.0 }, /* truncate chequerplay */ \
329 { MOVEFILTER_NORMAL, MOVEFILTER_NORMAL }, \
330 { MOVEFILTER_NORMAL, MOVEFILTER_NORMAL }, \
331 FALSE, /* cubeful */ \
332 TRUE, /* variance reduction */ \
333 FALSE, /* initial position */ \
334 TRUE, /* rotate */ \
335 TRUE, /* truncate at BEAROFF2 for cubeless rollouts */ \
336 TRUE, /* truncate at BEAROFF2_OS for cubeless rollouts */ \
337 FALSE, /* late evaluations */ \
338 TRUE, /* Truncation enabled */ \
339 FALSE, /* no stop on STD */ \
340 FALSE, /* no stop on JSD */ \
341 FALSE, /* no move stop on JSD */ \
342 10, /* truncation */ \
343 1296, /* number of trials */ \
344 5, /* late evals start here */ \
345 RNG_MERSENNE, /* RNG */ \
346 0, /* seed */ \
347 324, /* minimum games */ \
348 0.01f, /* stop when std's are lower than 0.01 */ \
349 324, /* minimum games */ \
350 2.33f, /* stop when best has j.s.d. for 99% confidence */ \
351 0, \
352 0.0, \
353 0 \
354 } \
355 }
356
357 /* parameters for analysis */
358
359 #define EVALSETUP_SUPREMO { \
360 /* evaltype */ \
361 EVAL_EVAL, \
362 /* evalcontext */ \
363 { TRUE, 2, TRUE, TRUE, 0.0 }, \
364 /* rolloutcontext */ \
365 { \
366 { \
367 { FALSE, 2, TRUE, TRUE, 0.0 }, /* player 0 cube decision */ \
368 { FALSE, 2, TRUE, TRUE, 0.0 } /* player 1 cube decision */ \
369 }, \
370 { \
371 { FALSE, 0, TRUE, TRUE, 0.0 }, /* player 0 chequerplay */ \
372 { FALSE, 0, TRUE, TRUE, 0.0 } /* player 1 chequerplay */ \
373 }, \
374 { \
375 { FALSE, 2, TRUE, TRUE, 0.0 }, /* p 0 late cube decision */ \
376 { FALSE, 2, TRUE, TRUE, 0.0 } /* p 1 late cube decision */ \
377 }, \
378 { \
379 { FALSE, 0, TRUE, TRUE, 0.0 }, /* p 0 late chequerplay */ \
380 { FALSE, 0, TRUE, TRUE, 0.0 } /* p 1 late chequerplay */ \
381 }, \
382 { FALSE, 2, TRUE, TRUE, 0.0 }, /* truncate cube decision */ \
383 { FALSE, 2, TRUE, TRUE, 0.0 }, /* truncate chequerplay */ \
384 { MOVEFILTER_NORMAL, MOVEFILTER_NORMAL }, \
385 { MOVEFILTER_NORMAL, MOVEFILTER_NORMAL }, \
386 FALSE, /* cubeful */ \
387 TRUE, /* variance reduction */ \
388 FALSE, /* initial position */ \
389 TRUE, /* rotate */ \
390 TRUE, /* truncate at BEAROFF2 for cubeless rollouts */ \
391 TRUE, /* truncate at BEAROFF2_OS for cubeless rollouts */ \
392 FALSE, /* late evaluations */ \
393 TRUE, /* Truncation enabled */ \
394 FALSE, /* no stop on STD */ \
395 FALSE, /* no stop on JSD */ \
396 FALSE, /* no move stop on JSD */ \
397 10, /* truncation */ \
398 1296, /* number of trials */ \
399 5, /* late evals start here */ \
400 RNG_MERSENNE, /* RNG */ \
401 0, /* seed */ \
402 324, /* minimum games */ \
403 0.01f, /* stop when std's are lower than 0.01 */ \
404 324, /* minimum games */ \
405 2.33f, /* stop when best has j.s.d. for 99% confidence */ \
406 0, \
407 0.0, \
408 0 \
409 } \
410 }
411
412 evalsetup esEvalChequer = EVALSETUP_WORLDCLASS;
413 evalsetup esEvalCube = EVALSETUP_WORLDCLASS;
414 evalsetup esAnalysisChequer = EVALSETUP_SUPREMO;
415 evalsetup esAnalysisCube = EVALSETUP_SUPREMO;
416
417 movefilter aamfEval[MAX_FILTER_PLIES][MAX_FILTER_PLIES] = MOVEFILTER_NORMAL;
418 movefilter aamfAnalysis[MAX_FILTER_PLIES][MAX_FILTER_PLIES] = MOVEFILTER_LARGE;
419
420 extern evalsetup *
GetEvalChequer(void)421 GetEvalChequer(void)
422 {
423 return fEvalSameAsAnalysis ? &esAnalysisChequer : &esEvalChequer;
424 }
425
426 extern evalsetup *
GetEvalCube(void)427 GetEvalCube(void)
428 {
429 return fEvalSameAsAnalysis ? &esAnalysisCube : &esEvalCube;
430 }
431
432 extern TmoveFilter *
GetEvalMoveFilter(void)433 GetEvalMoveFilter(void)
434 {
435 return fEvalSameAsAnalysis ? &aamfAnalysis : &aamfEval;
436 }
437
438 exportsetup exsExport = {
439 TRUE, /* include annotations */
440 TRUE, /* include analysis */
441 TRUE, /* include statistics */
442 TRUE, /* include match information */
443
444 1, /* display board for all moves */
445 -1, /* both players */
446
447 5, /* display max 5 moves */
448 TRUE, /* show detailed probabilities */
449 /* do not show move parameters */
450 {FALSE, TRUE}
451 ,
452 /* display all moves */
453 {TRUE, TRUE, TRUE, TRUE}
454 ,
455
456 TRUE, /* show detailed prob. for cube decisions */
457 {FALSE, TRUE}
458 , /* do not show move parameters */
459 /* display all cube decisions */
460 {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}
461 ,
462
463 NULL, /* HTML URL to pictures */
464 HTML_EXPORT_TYPE_GNU,
465 NULL, /* HTML extension */
466 HTML_EXPORT_CSS_HEAD, /* write CSS stylesheet in <head> */
467
468 4, /* PNG size */
469 4 /* HTML size */
470 };
471
472
473 player ap[2] = {
474 {"gnubg", PLAYER_GNU, EVALSETUP_WORLDCLASS, EVALSETUP_WORLDCLASS, MOVEFILTER_NORMAL, 0, NULL}
475 ,
476 {"user", PLAYER_HUMAN, EVALSETUP_WORLDCLASS, EVALSETUP_WORLDCLASS, MOVEFILTER_NORMAL, 0, NULL}
477 };
478
479 char default_names[2][31] = { "gnubg", "user" };
480
481 char player1aliases[256] = "";
482
483 /* Usage strings */
484 static char szDICE[] = N_("<die> <die>"),
485 szCOMMAND[] = N_("<command>"),
486 szCOMMENT[] = N_("<comment>"),
487 szER[] = "evaluation|rollout",
488 szFILENAME[] = N_("<filename>"),
489 szKEYVALUE[] = N_("[<key>=<value> ...]"),
490 szLENGTH[] = N_("<length>"),
491 szLIMIT[] = N_("<limit>"),
492 szMILLISECONDS[] = N_("<milliseconds>"),
493 szMOVE[] = N_("<from> <to> ..."),
494 szFILTER[] = N_("<ply> <num.xjoin to accept (0 = skip)> "
495 "[<num. of extra moves to accept> <tolerance>]"),
496 szNAME[] = N_("<name>"),
497 szQUIET[] = "[quiet]",
498 szLANG[] = N_("system|<language code>"),
499 szONOFF[] = "on|off",
500 szOPTCOMMAND[] = N_("[command]"),
501 szOPTDATE[] = N_("[yyyy-mm-dd]"),
502 szOPTDEPTH[] = N_("[depth]"),
503 szOPTFILENAME[] = N_("[filename]"),
504 szOPTLENGTH[] = N_("[length]"),
505 szOPTMODULUSOPTSEED[] = N_("[modulus <modulus>|factors <factor> <factor>] "
506 "[seed]"),
507 szOPTNAME[] = N_("[name]"),
508 szOPTPOSITION[] = N_("[position]"),
509 szOPTSEED[] = N_("[seed]"),
510 szOPTVALUE[] = N_("[value]"),
511 szPLAYER[] = N_("<player>"),
512 szPLAYEROPTRATING[] = N_("<player> [rating]"),
513 szPLIES[] = N_("<plies>"),
514 szPOSITION[] = N_("<position>"),
515 szPRIORITY[] = N_("<priority>"),
516 szPROMPT[] = N_("<prompt>"),
517 szSCORE[] = N_("<score> [length]"),
518 szSIZE[] = N_("<size>"),
519 szSTEP[] = N_("[game|roll|rolled|marked] <count>"),
520 szTRIALS[] = N_("<trials>"),
521 szVALUE[] = N_("<value>"),
522 szMATCHID[] = N_("<matchid>"),
523 szGNUBGID[] = N_("<gnubgid>"),
524 szXGID[] = N_("<xgid>"),
525 szURL[] = "<URL>",
526 szMAXERR[] = N_("<fraction>"), szMINGAMES[] = N_("<minimum games to rollout>"), szFOLDER[] = N_("<folder>"),
527 #if defined(USE_GTK)
528 szWARN[] = N_("[<warning>]"), szWARNYN[] = N_("<warning> on|off"),
529 #endif
530 szJSDS[] = N_("<joint standard deviations>"), szSTDDEV[] = N_("<std dev>");
531
532 /* Command defines moved into separate file */
533 #include "commands.inc"
534
535 static int iProgressMax, iProgressValue, fInProgress;
536 static char *pcProgress;
537 static psighandler shInterruptOld;
538 char *default_import_folder = NULL;
539 char *default_export_folder = NULL;
540 char *default_sgf_folder = NULL;
541
542 const char *szHomeDirectory;
543
544 static char const *aszBuildInfo[] = {
545 #if defined(USE_PYTHON)
546 #if (PY_MAJOR_VERSION == 2)
547 N_("Python 2 supported."),
548 #elif (PY_MAJOR_VERSION == 3)
549 N_("Python 3 supported."),
550 #else
551 #error "Unsupported python version"
552 #endif
553 #endif
554 #if defined(USE_SQLITE)
555 N_("SQLite database supported."),
556 #endif
557 #if defined(USE_GTK)
558 #if GTK_CHECK_VERSION(3,0,0)
559 N_("GTK3 graphical interface supported."),
560 #else
561 N_("GTK2 graphical interface supported."),
562 #endif
563 #endif
564 #if defined(HAVE_SOCKETS)
565 N_("External players supported."),
566 #endif
567 #if defined(HAVE_LIBGMP)
568 N_("Long RNG seeds supported."),
569 #endif
570 #if defined(USE_BOARD3D)
571 N_("3D boards supported."),
572 #endif
573 #if defined(WIN32)
574 N_("Windows sound system supported."),
575 #elif defined(HAVE_APPLE_COREAUDIO)
576 N_("Apple CoreAudio sound system supported."),
577 #elif defined(HAVE_APPLE_QUICKTIME)
578 N_("Apple QuickTime sound system supported."),
579 #elif defined(HAVE_CANBERRA)
580 N_("libcanberra sound system supported."),
581 #endif
582 #if defined(USE_MULTITHREAD)
583 N_("Multiple threads supported."),
584 #endif
585 #if defined(USE_SIMD_INSTRUCTIONS)
586 #if defined(USE_SSE2)
587 N_("SSE/SSE2 supported."),
588 #elif defined(USE_AVX)
589 N_("AVX supported."),
590 #elif defined(USE_SSE)
591 N_("SSE supported."),
592 #else
593 N_("NEON supported."),
594 #endif
595 #endif
596 NULL
597 };
598
599 extern const char *
GetBuildInfoString(void)600 GetBuildInfoString(void)
601 {
602 static const char **ppch = aszBuildInfo;
603
604 if (!*ppch) {
605 ppch = aszBuildInfo;
606 return NULL;
607 }
608 return *ppch++;
609 }
610
611 /*
612 * general token extraction
613 input: ppch pointer to pointer to command
614 szToekns - string of token separators
615 output: NULL if no token found
616 ptr to extracted token if found. Token is in original location
617 in input string, but null terminated if not quoted, token
618 will have been moved forward over quote character when quoted
619 ie:
620 input: ' abcd efgh'
621 output ' abcd\0efgh'
622 return value points to abcd, ppch points to efgh
623 input ' "jklm" nopq'
624 output ; jklm\0 nopq'
625 return value points to jklm, ppch points to space before
626 the 'n'
627 ppch points past null terminator
628
629 ignores leading whitespace, advances ppch over token and trailing
630 whitespace
631
632 matching single or double quotes are allowed, any character outside
633 of quotes or in doubly quoted strings can be escaped with a
634 backslash and will be taken as literal. Backslashes within single
635 quoted strings are taken literally. Multiple quoted strings can be
636 concatenated.
637
638 For example: input ' abc\"d"e f\"g h i"jk'l m n \" o p q'rst uvwzyz'
639 with the terminator list ' \t\r\n\v\f'
640 The returned token will be the string
641 <abc"de f"g h j ijkl m n \" o p qrst>
642 ppch will point to the 'u'
643 The \" between c and d is not in a single quoted string, so is reduced to
644 a double quote and is *not* the start of a quoted string.
645 The " before the 'd' begins a double quoted string, so spaces and tabs are
646 not terminators. The \" between f and g is reduced to a double quote and
647 does not terminate the quoted string. which ends with the double quote
648 between i and j. The \" between n and o is taken as a pair of literal
649 characters because they are within the single quoted string beginning
650 before l and ending after q.
651 It is not possible to put a single quote within a single quoted string.
652 You can have single quotes unescaped within double quoted strings and
653 double quotes unescaped within single quoted strings.
654 */
655 extern char *
NextTokenGeneral(char ** ppch,const char * szTokens)656 NextTokenGeneral(char **ppch, const char *szTokens)
657 {
658
659
660 char *pch, *pchSave, chQuote = 0;
661 int fEnd = FALSE;
662 #if !defined(G_DISABLE_ASSERT)
663 char *pchEnd;
664 #endif
665
666 if (!*ppch)
667 return NULL;
668
669 #if !defined(G_DISABLE_ASSERT)
670 pchEnd = strchr(*ppch, 0);
671 #endif
672
673 /* skip leading whitespace */
674 while (isspace(**ppch))
675 (*ppch)++;
676
677 if (!*(pch = pchSave = *ppch))
678 /* nothing left */
679 return NULL;
680
681 while (!fEnd) {
682
683 if (**ppch && strchr(szTokens, **ppch)) {
684 /* this character ends token */
685 if (!chQuote) {
686 fEnd = TRUE;
687 (*ppch)++; /* step over token */
688 } else
689 *pchSave++ = **ppch;
690 } else {
691 switch (**ppch) {
692 case '"':
693 /* quote mark */
694 if (!chQuote)
695 /* start quoting */
696 chQuote = **ppch;
697 else if (chQuote == **ppch)
698 /* end quoting */
699 chQuote = 0;
700 else
701 /* literal */
702 *pchSave++ = **ppch;
703 break;
704
705 #ifdef NO_BACKSLASH_ESCAPES
706 case '%':
707 #else
708 case '\\':
709 #endif
710 /* backslash */
711 if (chQuote == '\'')
712 /* literal */
713 *pchSave++ = **ppch;
714 else {
715 (*ppch)++;
716
717 if (**ppch)
718 /* next character is literal */
719 *pchSave++ = **ppch;
720 else {
721 /* end of string -- the backlash doesn't quote anything */
722 #ifdef NO_BACKSLASH_ESCAPES
723 *pchSave++ = '%';
724 #else
725 *pchSave++ = '\\';
726 #endif
727 fEnd = TRUE;
728 }
729 }
730 break;
731
732 case 0:
733 /* end of string -- always ends token */
734 fEnd = TRUE;
735 break;
736
737 default:
738 *pchSave++ = **ppch;
739 }
740
741 }
742
743 if (!fEnd)
744 (*ppch)++;
745 }
746
747 while (isspace(**ppch))
748 (*ppch)++;
749
750 *pchSave = 0;
751
752 #if !defined(G_DISABLE_ASSERT)
753 g_assert(pchSave <= pchEnd);
754 g_assert(*ppch <= pchEnd);
755 g_assert(pch <= pchEnd);
756 #endif
757
758 return pch;
759
760 }
761
762 /* extrace a token from a string. Tokens are terminated by tab, newline,
763 * carriage return, vertical tab or form feed.
764 * Input:
765 *
766 * ppch = pointer to pointer to input string. This will be updated
767 * to point past any token found. If the string is exhausetd,
768 * the pointer at ppch will point to the terminating NULL, so it is
769 * safe to call this function repeatedly after failure
770 *
771 * Output:
772 * null terminated token if found or NULL if no tokens present.
773 */
774 extern char *
NextToken(char ** ppch)775 NextToken(char **ppch)
776 {
777
778 return NextTokenGeneral(ppch, " \t\n\r\v\f");
779
780 }
781
782 /* return a count of the number of separate runs of one or more
783 * non-whitespace characters. This is the number of tokens that
784 * NextToken() will return. It may not be the number of tokens
785 * NextTokenGeneral() will return, as it does not count quoted strings
786 * containing whitespace as single tokens
787 */
788 static int
CountTokens(char * pch)789 CountTokens(char *pch)
790 {
791
792 int c = 0;
793
794 do {
795 while (isspace(*pch))
796 pch++;
797
798 if (*pch) {
799 c++;
800
801 while (*pch && !isspace(*pch))
802 pch++;
803 }
804 } while (*pch);
805
806 return c;
807 }
808
809 /* extract a token and convert to double. On error or no token, return
810 * ERR_VAL (a very large negative double.
811 */
812
813 extern float
ParseReal(char ** ppch)814 ParseReal(char **ppch)
815 {
816
817 char *pch, *pchOrig;
818 float r;
819
820 if (!ppch || !(pchOrig = NextToken(ppch)))
821 return ERR_VAL;
822
823 r = (float) g_ascii_strtod(pchOrig, &pch);
824
825 return *pch ? ERR_VAL : r;
826 }
827
828 /* get the next token from the input and convert as an
829 * integer. Returns INT_MIN on empty input or non-numerics found. Does
830 * handle negative integers. On failure, one token (if any were available
831 * will have been consumed, it is not pushed back into the input.
832 */
833 extern int
ParseNumber(char ** ppch)834 ParseNumber(char **ppch)
835 {
836
837 char *pch, *pchOrig;
838
839 if (!ppch || !(pchOrig = NextToken(ppch)))
840 return INT_MIN;
841
842 for (pch = pchOrig; *pch; pch++)
843 if (!isdigit(*pch) && *pch != '-')
844 return INT_MIN;
845
846 return atoi(pchOrig);
847 }
848
849 /* get a player either by name or as player 0 or 1 (indicated by the single
850 * character '0' or '1'. Returns -1 on no input, 2 if not a recoginsed name
851 * Note - this is not a token extracting routine, it expects to be handed
852 * an already extracted token
853 */
854 extern int
ParsePlayer(char * sz)855 ParsePlayer(char *sz)
856 {
857
858 int i;
859
860 if (!sz)
861 return -1;
862
863 if ((*sz == '0' || *sz == '1') && !sz[1])
864 return *sz - '0';
865
866 for (i = 0; i < 2; i++)
867 if (!CompareNames(sz, ap[i].szName))
868 return i;
869
870 if (!StrNCaseCmp(sz, "both", strlen(sz)))
871 return 2;
872
873 return -1;
874 }
875
876
877 /* Convert a string to a board array. Currently allows the string to
878 * be a position ID, a gnubg-nn position string, "=n" notation,
879 * or empty (in which case the current board is used).
880 *
881 * The input string should be specified in *ppch; this string must be
882 * modifiable, and the pointer will be updated to point to the token
883 * following a board specification if possible (see NextToken()). The
884 * board will be returned in an, and if pchDesc is non-NULL, then
885 * descriptive text (the position ID, formatted move, or "Current
886 * position", depending on the input) will be stored there.
887 *
888 * Returns -1 on failure, 0 on success, or 1 on success if the position
889 * specified has the opponent on roll (e.g. because it used "=n" notation). */
890 extern int
ParsePosition(TanBoard an,char ** ppch,char * pchDesc)891 ParsePosition(TanBoard an, char **ppch, char *pchDesc)
892 {
893 int i;
894 char *pch;
895
896 /* FIXME allow more formats, e.g. FIBS "boardstyle 3" */
897
898 if (!ppch || !(pch = NextToken(ppch))) {
899 memcpy(an, msBoard(), sizeof(TanBoard));
900
901 if (pchDesc)
902 strcpy(pchDesc, _("Current position"));
903
904 return 0;
905 }
906
907 if (!strcmp(pch, "simple")) {
908
909 /* board given as 26 integers.
910 * integer 1 : # of my chequers on the bar
911 * integer 2-25: number of chequers on point 1-24
912 * positive ints: my chequers
913 * negative ints: opp chequers
914 * integer 26 : # of opp chequers on the bar
915 */
916
917 int n;
918
919 for (i = 0; i < 26; i++) {
920
921 if ((n = ParseNumber(ppch)) == INT_MIN) {
922 outputf(_("`simple' must be followed by 26 integers; " "found only %d\n"), i);
923 return -1;
924 }
925
926 if (i == 0) {
927 /* my chequers on the bar */
928 an[1][24] = abs(n);
929 } else if (i == 25) {
930 /* opp chequers on the bar */
931 an[0][24] = abs(n);
932 } else {
933
934 an[1][i - 1] = 0;
935 an[0][24 - i] = 0;
936
937 if (n < 0)
938 an[0][24 - i] = -n;
939 else if (n > 0)
940 an[1][i - 1] = n;
941
942 }
943
944 }
945
946 if (pchDesc)
947 strcpy(pchDesc, *ppch);
948
949 *ppch = NULL;
950
951 return CheckPosition((ConstTanBoard) an) ? 0 : -1;
952 }
953
954 if (strlen(pch) == 20) { /* gnubg-nn position string */
955 static oldpositionkey key;
956
957 for (i = 0; i < 10; ++i) {
958 if (pch[2 * i + 0] >= 'A' && pch[2 * i + 0] <= 'P' && pch[2 * i + 1] >= 'A' && pch[2 * i + 1] <= 'P')
959 key.auch[i] = (unsigned char) (((pch[2 * i + 0] - 'A') << 4)
960 + (pch[2 * i + 1] - 'A'));
961 else {
962 outputl(_("Illegal position."));
963 return -1;
964 }
965 }
966
967 oldPositionFromKey(an, &key);
968
969 return 0;
970 }
971
972 if (!PositionFromID(an, pch)) {
973 outputl(_("Illegal position."));
974 return -1;
975 }
976
977 if (pchDesc)
978 strcpy(pchDesc, pch);
979
980 return 0;
981 }
982
983 /* Parse "key=value" pairs on a command line. PPCH takes a pointer to
984 * a command line on input, and returns a pointer to the next parameter.
985 * The key is returned in apch[ 0 ], and the value in apch[ 1 ].
986 * The function return value is the number of parts successfully read
987 * (0 = no key was found, 1 = key only, 2 = both key and value). */
988 extern int
ParseKeyValue(char ** ppch,char * apch[2])989 ParseKeyValue(char **ppch, char *apch[2])
990 {
991
992 if (!ppch || !(apch[0] = NextToken(ppch)))
993 return 0;
994
995 if (!(apch[1] = strchr(apch[0], '=')))
996 return 1;
997
998 *apch[1] = 0;
999 apch[1]++;
1000 return 2;
1001 }
1002
1003 /* Compare player names. Performed case insensitively, and with all
1004 * whitespace characters and underscore considered identical. */
1005 extern int
CompareNames(char * sz0,char * sz1)1006 CompareNames(char *sz0, char *sz1)
1007 {
1008
1009 static char ach[] = " \t\r\n\f\v_";
1010
1011 for (; *sz0 || *sz1; sz0++, sz1++)
1012 if (toupper(*sz0) != toupper(*sz1) && (!strchr(ach, *sz0) || !strchr(ach, *sz1)))
1013 return toupper(*sz0) - toupper(*sz1);
1014
1015 return 0;
1016 }
1017
1018 extern void
UpdateSetting(void * p)1019 UpdateSetting(void *p)
1020 {
1021 #if defined(USE_GTK)
1022 if (fX)
1023 GTKSet(p);
1024 #else
1025 (void) p; /* suppress unused parameter compiler warning */
1026 #endif
1027 }
1028
1029 extern void
UpdateSettings(void)1030 UpdateSettings(void)
1031 {
1032
1033 UpdateSetting(&ms.nCube);
1034 UpdateSetting(&ms.fCubeOwner);
1035 UpdateSetting(&ms.fTurn);
1036 UpdateSetting(&ms.nMatchTo);
1037 UpdateSetting(&ms.fCrawford);
1038 UpdateSetting(&ms.fJacoby);
1039 UpdateSetting(&ms.gs);
1040
1041 ShowBoard();
1042
1043 #if defined(USE_BOARD3D)
1044 RestrictiveRedraw();
1045 #endif
1046 }
1047
1048 /* handle turning a setting on / off
1049 * inputs: szName - the setting being adjusted
1050 * pf = pointer to current on/off state (will be updated)
1051 * sz = pointer to command line - a token will be extracted,
1052 * but furhter calls to NextToken will return only the on/off
1053 * value, so you can't have commands in the form
1054 * set something on <more tokens>
1055 * szOn - text to output when turning setting on
1056 * szOff - text to output when turning setting off
1057 * output: -1 on error
1058 * 0 setting is now off
1059 * 1 setting is now on
1060 * acceptable tokens are on/off yes/no true/false
1061 */
1062 extern int
SetToggle(const char * szName,int * pf,char * sz,const char * szOn,const char * szOff)1063 SetToggle(const char *szName, int *pf, char *sz, const char *szOn, const char *szOff)
1064 {
1065 char *pch = NextToken(&sz);
1066 size_t cch;
1067
1068 if (!pch) {
1069 outputf(_("You must specify whether to set '%s' on or off.\n"), szName);
1070
1071 return -1;
1072 }
1073
1074 cch = strlen(pch);
1075
1076 if (!StrCaseCmp("on", pch) || !StrNCaseCmp("yes", pch, cch) || !StrNCaseCmp("true", pch, cch)) {
1077 outputl(szOn);
1078
1079 if (*pf != TRUE) {
1080 *pf = TRUE;
1081 UpdateSetting(pf);
1082 }
1083
1084 return TRUE;
1085 }
1086
1087 if (!StrCaseCmp("off", pch) || !StrNCaseCmp("no", pch, cch) || !StrNCaseCmp("false", pch, cch)) {
1088 outputl(szOff);
1089
1090 if (*pf != FALSE) {
1091 *pf = FALSE;
1092 UpdateSetting(pf);
1093 }
1094
1095 return FALSE;
1096 }
1097
1098 outputf(_("Illegal keyword `%s'.\n"), pch);
1099
1100 return -1;
1101 }
1102
1103 /* FIXME? This is only used with fRestart == FALSE */
1104 extern void
PortableSignal(int nSignal,void (* p)(int),psighandler * pOld,int fRestart)1105 PortableSignal(int nSignal, void (*p) (int), psighandler * pOld, int fRestart)
1106 {
1107 #if defined(HAVE_SIGACTION)
1108 struct sigaction sa;
1109
1110 sa.sa_handler = p;
1111 sigemptyset(&sa.sa_mask);
1112 sa.sa_flags =
1113 #if SA_RESTART
1114 (fRestart ? SA_RESTART : 0) |
1115 #endif
1116 #if SA_NOCLDSTOP
1117 SA_NOCLDSTOP |
1118 #endif
1119 0;
1120 sigaction(nSignal, p ? &sa : NULL, pOld);
1121 #elif defined(HAVE_SIGVEC)
1122 struct sigvec sv;
1123
1124 sv.sv_handler = p;
1125 sigemptyset(&sv.sv_mask);
1126 sv.sv_flags = nSignal == SIGINT || nSignal == SIGIO ? SV_INTERRUPT : 0;
1127
1128 sigvec(nSignal, p ? &sv : NULL, pOld);
1129 #else
1130 (void) fRestart; /* silence compiler warning */
1131 if (pOld)
1132 *pOld = signal(nSignal, p);
1133 else if (p)
1134 signal(nSignal, p);
1135 #endif
1136 }
1137
1138 extern void
PortableSignalRestore(int nSignal,psighandler * p)1139 PortableSignalRestore(int nSignal, psighandler * p)
1140 {
1141 #if defined(HAVE_SIGACTION)
1142 sigaction(nSignal, p, NULL);
1143 #elif defined(HAVE_SIGVEC)
1144 sigvec(nSignal, p, NULL);
1145 #else
1146 signal(nSignal, *p);
1147 #endif
1148 }
1149
1150 /* Reset the SIGINT handler, on return to the main command loop. Notify
1151 * the user if processing had been interrupted. */
1152 extern void
ResetInterrupt(void)1153 ResetInterrupt(void)
1154 {
1155 if (fInterrupt) {
1156 {
1157 outputf("(%s)", _("Interrupted"));
1158 outputx();
1159 }
1160
1161 fInterrupt = FALSE;
1162
1163 /* if a batch was running signal a cancellation of current match */
1164 fMatchCancelled = TRUE;
1165
1166 #if defined(USE_GTK)
1167 if (nNextTurn) {
1168 g_source_remove(nNextTurn);
1169 nNextTurn = 0;
1170 }
1171 #endif
1172 }
1173 }
1174
1175
1176 extern void
HandleCommand(char * sz,command * ac)1177 HandleCommand(char *sz, command * ac)
1178 {
1179
1180 command *pc;
1181 char *pch;
1182 size_t cch;
1183
1184 if (ac == acTop) {
1185 outputnew();
1186
1187 if (*sz == '#') /* Comment */
1188 return;
1189 else if (*sz == ':') {
1190 return;
1191 } else if (*sz == '>') {
1192
1193 while (*sz == '>')
1194 ++sz;
1195
1196 /* leading white space confuses Python :-) */
1197
1198 while (isspace(*sz))
1199 ++sz;
1200
1201 #if defined(USE_PYTHON)
1202 PythonRun(sz);
1203 #else
1204 outputl(_("This installation of GNU Backgammon was compiled without Python support."));
1205 outputx();
1206 #endif
1207 return;
1208 }
1209
1210 }
1211
1212 if (!(pch = NextToken(&sz))) {
1213 if (ac != acTop)
1214 outputl(_("Incomplete command."));
1215
1216 outputx();
1217 return;
1218 }
1219
1220 cch = strlen(pch);
1221
1222 if (ac == acTop && (isdigit(*pch) || !StrNCaseCmp(pch, "bar/", cch > 4 ? 4 : cch))) {
1223 if (pch + cch < sz)
1224 pch[cch] = ' ';
1225
1226 CommandMove(pch);
1227
1228 outputx();
1229 return;
1230 }
1231
1232 for (pc = ac; pc->sz; pc++)
1233 if (!StrNCaseCmp(pch, pc->sz, cch))
1234 break;
1235
1236 if (!pc->sz) {
1237 if (!loading_rc)
1238 outputerrf(_("Unknown keyword `%s'.\n"), pch);
1239 return;
1240 }
1241
1242 if (pc->pf) {
1243 pc->pf(sz);
1244
1245 outputx();
1246 } else
1247 HandleCommand(sz, pc->pc);
1248 }
1249
1250 extern void
InitBoard(TanBoard anBoard,const bgvariation bgv)1251 InitBoard(TanBoard anBoard, const bgvariation bgv)
1252 {
1253
1254 switch (bgv) {
1255
1256 case VARIATION_STANDARD:
1257 PositionFromID(anBoard, "4HPwATDgc/ABMA");
1258 break;
1259 case VARIATION_NACKGAMMON:
1260 PositionFromID(anBoard, "4Dl4ADbgOXgANg");
1261 break;
1262
1263 case VARIATION_HYPERGAMMON_1:
1264 PositionFromID(anBoard, "AACAAAAAAgAAAA");
1265 break;
1266 case VARIATION_HYPERGAMMON_2:
1267 PositionFromID(anBoard, "AABAAQAACgAAAA");
1268 break;
1269 case VARIATION_HYPERGAMMON_3:
1270 PositionFromID(anBoard, "AACgAgAAKgAAAA");
1271 break;
1272
1273 default:
1274 g_assert_not_reached();
1275
1276 }
1277
1278 }
1279
1280
1281 extern void
GetMatchStateCubeInfo(cubeinfo * pci,const matchstate * pms)1282 GetMatchStateCubeInfo(cubeinfo * pci, const matchstate * pms)
1283 {
1284
1285 SetCubeInfo(pci, pms->nCube, pms->fCubeOwner, pms->fMove,
1286 pms->nMatchTo, pms->anScore, pms->fCrawford, pms->fJacoby, nBeavers, pms->bgv);
1287 }
1288
1289 static void
DisplayCubeAnalysis(float aarOutput[2][NUM_ROLLOUT_OUTPUTS],float aarStdDev[2][NUM_ROLLOUT_OUTPUTS],const evalsetup * pes)1290 DisplayCubeAnalysis(float aarOutput[2][NUM_ROLLOUT_OUTPUTS],
1291 float aarStdDev[2][NUM_ROLLOUT_OUTPUTS], const evalsetup * pes)
1292 {
1293
1294 cubeinfo ci;
1295
1296 if (pes->et == EVAL_NONE)
1297 return;
1298
1299 GetMatchStateCubeInfo(&ci, &ms);
1300
1301 outputl(OutputCubeAnalysis(aarOutput, aarStdDev, pes, &ci));
1302 }
1303
1304 extern char *
GetLuckAnalysis(const matchstate * pms,float rLuck)1305 GetLuckAnalysis(const matchstate * pms, float rLuck)
1306 {
1307
1308 static char sz[16];
1309 cubeinfo ci;
1310
1311 if (fOutputMWC && pms->nMatchTo) {
1312 GetMatchStateCubeInfo(&ci, pms);
1313
1314 sprintf(sz, "%+0.3f%%", 100.0f * (eq2mwc(rLuck, &ci) - eq2mwc(0.0f, &ci)));
1315 } else
1316 sprintf(sz, "%+0.3f", rLuck);
1317
1318 return sz;
1319 }
1320
1321 static void
DisplayAnalysis(moverecord * pmr)1322 DisplayAnalysis(moverecord * pmr)
1323 {
1324
1325 unsigned int i;
1326 char szBuf[1024];
1327
1328 switch (pmr->mt) {
1329 case MOVE_NORMAL:
1330 DisplayCubeAnalysis(pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble);
1331
1332 outputf("%s %u%u", _("Rolled"), pmr->anDice[0], pmr->anDice[1]);
1333
1334 if (pmr->rLuck != ERR_VAL)
1335 outputf(" (%s):\n", GetLuckAnalysis(&ms, pmr->rLuck));
1336 else
1337 outputl(":");
1338
1339 for (i = 0; i < pmr->ml.cMoves; i++) {
1340 if (i >= 10 /* FIXME allow user to choose limit */ &&
1341 i != pmr->n.iMove)
1342 continue;
1343 outputc(i == pmr->n.iMove ? '*' : ' ');
1344 output(FormatMoveHint(szBuf, &ms, &pmr->ml, i,
1345 i != pmr->n.iMove ||
1346 i != pmr->ml.cMoves - 1 || pmr->ml.cMoves == 1 || i < exsExport.nMoves, TRUE, TRUE));
1347
1348 }
1349
1350 break;
1351
1352 case MOVE_DOUBLE:
1353 DisplayCubeAnalysis(pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble);
1354 break;
1355
1356 case MOVE_TAKE:
1357 case MOVE_DROP:
1358 /* FIXME */
1359 break;
1360
1361 case MOVE_SETDICE:
1362 if (pmr->rLuck != ERR_VAL)
1363 outputf("%s %u%u (%s):\n", _("Rolled"), pmr->anDice[0], pmr->anDice[1], GetLuckAnalysis(&ms, pmr->rLuck));
1364 break;
1365
1366 default:
1367 break;
1368 }
1369 }
1370
1371 extern void
ShowBoard(void)1372 ShowBoard(void)
1373 {
1374 char szBoard[2048];
1375 char sz[50], szCube[50], szPlayer0[MAX_NAME_LEN + 3], szPlayer1[MAX_NAME_LEN + 3],
1376 szScore0[50], szScore1[50], szMatch[50];
1377 char *apch[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
1378 moverecord *pmr;
1379 TanBoard an;
1380
1381 if (cOutputDisabled || !foutput_on)
1382 return;
1383
1384 if (ms.gs == GAME_NONE) {
1385 #if defined(USE_GTK)
1386 if (fX) {
1387 TanBoard anBoardTemp;
1388 InitBoard(anBoardTemp, ms.bgv);
1389 game_set(BOARD(pwBoard), anBoardTemp, 0, ap[1].szName,
1390 ap[0].szName, ms.nMatchTo, ms.anScore[1], ms.anScore[0], 0, 0, FALSE, anChequers[ms.bgv]);
1391 } else
1392 #endif
1393
1394 outputl(_("No game in progress."));
1395
1396 return;
1397 }
1398
1399 memcpy(an, msBoard(), sizeof(TanBoard));
1400 if (!ms.fMove)
1401 SwapSides(an);
1402
1403 #if defined(USE_GTK)
1404 if (!fX) {
1405 #endif
1406 if (fOutputRawboard) {
1407 outputl(FIBSBoard(szBoard, an, ms.fMove, ap[1].szName,
1408 ap[0].szName, ms.nMatchTo, ms.anScore[1],
1409 ms.anScore[0], ms.anDice[0],
1410 ms.anDice[1], ms.nCube,
1411 ms.fCubeOwner, ms.fDoubled, ms.fTurn, ms.fCrawford, anChequers[ms.bgv],
1412 ms.fPostCrawford));
1413
1414 return;
1415 }
1416
1417 apch[0] = szPlayer0;
1418 apch[6] = szPlayer1;
1419
1420 sprintf(apch[1] = szScore0, ngettext("%d point", "%d points", ms.anScore[0]), ms.anScore[0]);
1421
1422 sprintf(apch[5] = szScore1, ngettext("%d point", "%d points", ms.anScore[1]), ms.anScore[1]);
1423
1424 if (ms.fDoubled) {
1425 apch[ms.fTurn ? 4 : 2] = szCube;
1426
1427 sprintf(szPlayer0, "O: %s", ap[0].szName);
1428 sprintf(szPlayer1, "X: %s", ap[1].szName);
1429 sprintf(szCube, _("Cube offered at %d"), ms.nCube << 1);
1430 } else {
1431 sprintf(szPlayer0, "O: %s", ap[0].szName);
1432 sprintf(szPlayer1, "X: %s", ap[1].szName);
1433
1434 apch[ms.fMove ? 4 : 2] = sz;
1435
1436 if (ms.anDice[0])
1437 sprintf(sz, _("%s %u%u"), _("Rolled"), ms.anDice[0], ms.anDice[1]);
1438 else if (!GameStatus(msBoard(), ms.bgv))
1439 strcpy(sz, _("On roll"));
1440 else
1441 sz[0] = 0;
1442
1443 if (ms.fCubeOwner < 0) {
1444 apch[3] = szCube;
1445
1446 if (ms.nMatchTo)
1447 if (ms.nMatchTo == 1)
1448 sprintf(szCube, _("1 point match"));
1449 else if (ms.fCrawford)
1450 sprintf(szCube, _("%d point match (Crawford game)"), ms.nMatchTo);
1451 else
1452 sprintf(szCube, _("%d point match (Cube: %d)"), ms.nMatchTo, ms.nCube);
1453 else
1454 sprintf(szCube, "(%s: %d)", _("Cube"), ms.nCube);
1455 } else {
1456 size_t cch = strlen(ap[ms.fCubeOwner].szName);
1457
1458 if (cch > 20)
1459 cch = 20;
1460
1461 sprintf(szCube, "%c: %*s (%s: %d)", ms.fCubeOwner ? 'X' : 'O',
1462 (int) cch, ap[ms.fCubeOwner].szName, _("Cube"), ms.nCube);
1463
1464 apch[ms.fCubeOwner ? 6 : 0] = szCube;
1465
1466 if (ms.nMatchTo)
1467 sprintf(apch[3] = szMatch, _("%d point match"), ms.nMatchTo);
1468 }
1469 }
1470 if (ms.fResigned > 0)
1471 /* FIXME it's not necessarily the player on roll who resigned */
1472 sprintf(strchr(sz, 0), ", %s %s", _("resigns"), gettext(aszGameResult[ms.fResigned - 1]));
1473
1474 outputl(DrawBoard(szBoard, (ConstTanBoard) an, ms.fMove, apch, MatchIDFromMatchState(&ms), anChequers[ms.bgv]));
1475
1476 if (
1477 #if defined(USE_GTK)
1478 PanelShowing(WINDOW_ANALYSIS) &&
1479 #endif
1480 plLastMove && (pmr = plLastMove->plNext->p)) {
1481 DisplayAnalysis(pmr);
1482 if (pmr->sz)
1483 outputl(pmr->sz); /* FIXME word wrap */
1484 }
1485 #if defined(USE_GTK)
1486 } else {
1487 game_set(BOARD(pwBoard), an, ms.fMove, ap[1].szName,
1488 ap[0].szName, ms.nMatchTo, ms.anScore[1],
1489 ms.anScore[0], ms.anDice[0], ms.anDice[1],
1490 ap[ms.fTurn].pt != PLAYER_HUMAN && !fComputing && !nNextTurn, anChequers[ms.bgv]);
1491 }
1492 #endif
1493
1494 #ifdef UNDEF
1495 {
1496 char *pc;
1497 printf("%s: %s\n", _("MatchID"), pc = MatchIDFromMatchState(&ms));
1498 MatchStateFromID(&ms, pc);
1499 }
1500 #endif
1501 }
1502
1503 extern char *
FormatPrompt(void)1504 FormatPrompt(void)
1505 {
1506
1507 static char sz[128]; /* FIXME check for overflow in rest of function */
1508 const char *pch = szPrompt;
1509 char *pchDest = sz;
1510 unsigned int anPips[2];
1511
1512 while (*pch)
1513 if (*pch == '\\') {
1514 pch++;
1515 switch (*pch) {
1516 case 0:
1517 goto done;
1518
1519 case 'c':
1520 case 'C':
1521 /* Pip count */
1522 if (ms.gs == GAME_NONE)
1523 strcpy(pchDest, _("No game"));
1524 else {
1525 PipCount(msBoard(), anPips);
1526 sprintf(pchDest, "%u:%u", anPips[1], anPips[0]);
1527 }
1528 break;
1529
1530 case 'p':
1531 case 'P':
1532 /* Player on roll */
1533 switch (ms.gs) {
1534 case GAME_NONE:
1535 strcpy(pchDest, _("No game"));
1536 break;
1537
1538 case GAME_PLAYING:
1539 strcpy(pchDest, ap[ms.fTurn].szName);
1540 break;
1541
1542 case GAME_OVER:
1543 case GAME_RESIGNED:
1544 case GAME_DROP:
1545 strcpy(pchDest, _("Game over"));
1546 break;
1547 }
1548 break;
1549
1550 case 's':
1551 case 'S':
1552 /* Match score */
1553 sprintf(pchDest, "%d:%d", ms.anScore[0], ms.anScore[1]);
1554 break;
1555
1556 case 'v':
1557 case 'V':
1558 /* Version */
1559 strcpy(pchDest, VERSION);
1560 break;
1561
1562 default:
1563 *pchDest++ = *pch;
1564 *pchDest = 0;
1565 }
1566
1567 pchDest = strchr(pchDest, 0);
1568 pch++;
1569 } else
1570 *pchDest++ = *pch++;
1571
1572 done:
1573 *pchDest = 0;
1574
1575 return sz;
1576 }
1577
1578 extern void
CommandEval(char * sz)1579 CommandEval(char *sz)
1580 {
1581
1582 char szOutput[4096];
1583 int n;
1584 TanBoard an;
1585 cubeinfo ci;
1586 decisionData dd;
1587
1588 if (!*sz && ms.gs == GAME_NONE) {
1589 outputl(_("No position specified and no game in progress."));
1590 return;
1591 }
1592
1593 if ((n = ParsePosition(an, &sz, NULL)) < 0)
1594 return;
1595
1596 if (n && ms.fMove)
1597 /* =n notation used; the opponent is on roll in the position given. */
1598 SwapSides(an);
1599
1600 if (ms.gs == GAME_NONE)
1601 memcpy(&ci, &ciCubeless, sizeof(ci));
1602 else
1603 SetCubeInfo(&ci, ms.nCube, ms.fCubeOwner, n ? !ms.fMove : ms.fMove,
1604 ms.nMatchTo, ms.anScore, ms.fCrawford, ms.fJacoby, nBeavers, ms.bgv);
1605
1606 /* Consider cube action */
1607 dd.pboard = (ConstTanBoard) an;
1608 dd.pec = &GetEvalCube()->ec;
1609 dd.pci = &ci;
1610 dd.szOutput = szOutput;
1611 dd.n = n;
1612 if (RunAsyncProcess((AsyncFun) asyncDumpDecision, &dd, _("Evaluating position...")) == 0) {
1613 #if defined(USE_GTK)
1614 if (fX)
1615 GTKEval(szOutput);
1616 else
1617 #endif
1618 outputl(szOutput);
1619 }
1620 }
1621
1622 extern command *
FindHelpCommand(command * pcBase,char * sz,char * pchCommand,char * pchUsage)1623 FindHelpCommand(command * pcBase, char *sz, char *pchCommand, char *pchUsage)
1624 {
1625
1626 command *pc;
1627 const char *pch;
1628 size_t cch;
1629
1630 if (!(pch = NextToken(&sz)))
1631 return pcBase;
1632
1633 cch = strlen(pch);
1634
1635 for (pc = pcBase->pc; pc && pc->sz; pc++)
1636 if (!StrNCaseCmp(pch, pc->sz, cch))
1637 break;
1638
1639 if (!pc || !pc->sz)
1640 return NULL;
1641
1642 pch = pc->sz;
1643 while (*pch)
1644 *pchCommand++ = *pchUsage++ = *pch++;
1645 *pchCommand++ = ' ';
1646 *pchCommand = 0;
1647 *pchUsage++ = ' ';
1648 *pchUsage = 0;
1649
1650 if (pc->szUsage) {
1651 pch = gettext(pc->szUsage);
1652 while (*pch)
1653 *pchUsage++ = *pch++;
1654 *pchUsage++ = ' ';
1655 *pchUsage = 0;
1656 }
1657
1658 if (pc->pc)
1659 /* subcommand */
1660 return FindHelpCommand(pc, sz, pchCommand, pchUsage);
1661 else
1662 /* terminal command */
1663 return pc;
1664 }
1665
1666 extern char *
CheckCommand(char * sz,command * ac)1667 CheckCommand(char *sz, command * ac)
1668 {
1669 command *pc;
1670 size_t cch;
1671 char *pch = NextToken(&sz);
1672 if (!pch)
1673 return 0;
1674
1675 cch = strlen(pch);
1676 for (pc = ac; pc->sz; pc++)
1677 if (!StrNCaseCmp(pch, pc->sz, cch))
1678 break;
1679 if (!pc->sz)
1680 return pch;
1681
1682 if (pc->pf) {
1683 return 0;
1684 } else {
1685 return CheckCommand(sz, pc->pc);
1686 }
1687 }
1688
1689 extern void
CommandHelp(char * sz)1690 CommandHelp(char *sz)
1691 {
1692
1693 command *pc, *pcFull;
1694 char szCommand[128], szUsage[128], *szHelp;
1695
1696 #if defined(USE_GTK)
1697 if (fX) {
1698 GTKHelp(sz);
1699 return;
1700 }
1701 #endif
1702
1703 if (!(pc = FindHelpCommand(&cTop, sz, szCommand, szUsage))) {
1704 outputf(_("No help available for topic `%s'"), sz);
1705 output("\n");
1706
1707 return;
1708 }
1709
1710 if (pc->szHelp)
1711 /* the command has its own help text */
1712 szHelp = gettext(pc->szHelp);
1713 else if (pc == &cTop)
1714 /* top-level help isn't for any command */
1715 szHelp = NULL;
1716 else {
1717 /* perhaps the command is an abbreviation; search for the full
1718 * version */
1719 szHelp = NULL;
1720
1721 for (pcFull = acTop; pcFull->sz; pcFull++)
1722 if (pcFull->pf == pc->pf && pcFull->szHelp) {
1723 szHelp = gettext(pcFull->szHelp);
1724 break;
1725 }
1726 }
1727
1728 if (szHelp) {
1729 outputf("%s- %s\n\n%s: %s", szCommand, szHelp, _("Usage"), szUsage);
1730
1731 if (pc->pc && pc->pc->sz)
1732 outputf("<%s>\n", _("subcommand"));
1733 else
1734 outputc('\n');
1735 }
1736
1737 if (pc->pc && pc->pc->sz) {
1738 outputl(pc == &cTop ? _("Available commands:") : _("Available subcommands:"));
1739
1740 pc = pc->pc;
1741
1742 for (; pc->sz; pc++)
1743 if (pc->szHelp)
1744 outputf("%-15s\t%s\n", pc->sz, gettext(pc->szHelp));
1745 }
1746 }
1747
1748
1749 extern char *
FormatMoveHint(char * sz,const matchstate * pms,movelist * pml,int i,int fRankKnown,int fDetailProb,int fShowParameters)1750 FormatMoveHint(char *sz, const matchstate * pms, movelist * pml,
1751 int i, int fRankKnown, int fDetailProb, int fShowParameters)
1752 {
1753
1754 cubeinfo ci;
1755 char szTemp[2048], szMove[32];
1756 float *ar, *arStdDev;
1757 float rEq, rEqTop;
1758
1759 GetMatchStateCubeInfo(&ci, pms);
1760
1761 strcpy(sz, "");
1762
1763 /* number */
1764
1765 if (i && !fRankKnown)
1766 strcat(sz, " ?? ");
1767 else
1768 sprintf(strchr(sz, 0), " %4i. ", i + 1);
1769
1770 /* eval */
1771
1772 sprintf(strchr(sz, 0),
1773 "%-14s %-28s %s: ",
1774 FormatEval(szTemp, &pml->amMoves[i].esMove),
1775 FormatMove(szMove, pms->anBoard,
1776 pml->amMoves[i].anMove), (!pms->nMatchTo || !fOutputMWC) ? _("Eq.") : _("MWC"));
1777
1778 /* equity or mwc for move */
1779
1780 ar = pml->amMoves[i].arEvalMove;
1781 arStdDev = pml->amMoves[i].arEvalStdDev;
1782 rEq = pml->amMoves[i].rScore;
1783 rEqTop = pml->amMoves[0].rScore;
1784
1785 strcat(sz, OutputEquity(rEq, &ci, TRUE));
1786
1787 /* difference */
1788
1789 if (i)
1790 sprintf(strchr(sz, 0), " (%s)\n", OutputEquityDiff(rEq, rEqTop, &ci));
1791 else
1792 strcat(sz, "\n");
1793
1794 /* percentages */
1795
1796 if (fDetailProb) {
1797
1798 switch (pml->amMoves[i].esMove.et) {
1799 case EVAL_EVAL:
1800 /* FIXME: add cubeless and cubeful equities */
1801 strcat(sz, " ");
1802 strcat(sz, OutputPercents(ar, TRUE));
1803 strcat(sz, "\n");
1804 break;
1805 case EVAL_ROLLOUT:
1806 strcat(sz, OutputRolloutResult(" ", NULL, (float (*)[NUM_ROLLOUT_OUTPUTS])
1807 ar, (float (*)[NUM_ROLLOUT_OUTPUTS])
1808 arStdDev, &ci, 0, 1, pml->amMoves[i].esMove.rc.fCubeful));
1809 break;
1810 default:
1811 break;
1812
1813 }
1814 }
1815
1816 /* eval parameters */
1817
1818 if (fShowParameters) {
1819
1820 switch (pml->amMoves[i].esMove.et) {
1821 case EVAL_EVAL:
1822 strcat(sz, " ");
1823 strcat(sz, OutputEvalContext(&pml->amMoves[i].esMove.ec, TRUE));
1824 strcat(sz, "\n");
1825 break;
1826 case EVAL_ROLLOUT:
1827 strcat(sz, OutputRolloutContext(" ", &pml->amMoves[i].esMove.rc));
1828 break;
1829
1830 default:
1831 break;
1832
1833 }
1834
1835 }
1836
1837 return sz;
1838
1839 #if 0
1840
1841
1842 if (!pms->nMatchTo || (pms->nMatchTo && !fOutputMWC)) {
1843 /* output in equity */
1844 float *ar, rEq, rEqTop;
1845
1846 ar = pml->amMoves[0].arEvalMove;
1847 rEqTop = pml->amMoves[0].rScore;
1848
1849 if (!i) {
1850 if (fOutputWinPC)
1851 sprintf(sz, " %4i. %-14s %-28s Eq.: %+6.3f\n"
1852 " %5.1f%% %5.1f%% %5.1f%% -"
1853 " %5.1f%% %5.1f%% %5.1f%%\n",
1854 1, FormatEval(szTemp, &pml->amMoves[0].esMove),
1855 FormatMove(szMove, pms->anBoard,
1856 pml->amMoves[0].anMove),
1857 rEqTop,
1858 100.0 * ar[0], 100.0 * ar[1], 100.0 * ar[2],
1859 100.0 * (1.0 - ar[0]), 100.0 * ar[3], 100.0 * ar[4]);
1860 else
1861 sprintf(sz, " %4i. %-14s %-28s Eq.: %+6.3f\n"
1862 " %5.3f %5.3f %5.3f -"
1863 " %5.3f %5.3f %5.3f\n",
1864 1, FormatEval(szTemp, &pml->amMoves[0].esMove),
1865 FormatMove(szMove, pms->anBoard,
1866 pml->amMoves[0].anMove), rEqTop, ar[0], ar[1], ar[2], (1.0 - ar[0]), ar[3], ar[4]);
1867 } else {
1868 ar = pml->amMoves[i].arEvalMove;
1869 rEq = pml->amMoves[i].rScore;
1870
1871 if (fRankKnown)
1872 sprintf(sz, " %4i.", i + 1);
1873 else
1874 strcpy(sz, " ?? ");
1875
1876 if (fOutputWinPC)
1877 sprintf(sz + 6, " %-14s %-28s Eq.: %+6.3f (%+6.3f)\n"
1878 " %5.1f%% %5.1f%% %5.1f%% -"
1879 " %5.1f%% %5.1f%% %5.1f%%\n",
1880 FormatEval(szTemp, &pml->amMoves[i].esMove),
1881 FormatMove(szMove, pms->anBoard,
1882 pml->amMoves[i].anMove),
1883 rEq, rEq - rEqTop,
1884 100.0 * ar[0], 100.0 * ar[1], 100.0 * ar[2],
1885 100.0 * (1.0 - ar[0]), 100.0 * ar[3], 100.0 * ar[4]);
1886 else
1887 sprintf(sz + 6, " %-14s %-28s Eq.: %+6.3f (%+6.3f)\n"
1888 " %5.3f %5.3f %5.3f -"
1889 " %5.3f %5.3f %5.3f\n",
1890 FormatEval(szTemp, &pml->amMoves[i].esMove),
1891 FormatMove(szMove, pms->anBoard,
1892 pml->amMoves[i].anMove),
1893 rEq, rEq - rEqTop, ar[0], ar[1], ar[2], (1.0 - ar[0]), ar[3], ar[4]);
1894 }
1895 } else {
1896 /* output in mwc */
1897
1898 float *ar, rMWC, rMWCTop;
1899
1900 ar = pml->amMoves[0].arEvalMove;
1901 rMWCTop = 100.0 * eq2mwc(pml->amMoves[0].rScore, &ci);
1902
1903 if (!i) {
1904 if (fOutputWinPC)
1905 sprintf(sz, " %4i. %-14s %-28s MWC: %7.3f%%\n"
1906 " %5.1f%% %5.1f%% %5.1f%% -"
1907 " %5.1f%% %5.1f%% %5.1f%%\n",
1908 1, FormatEval(szTemp, &pml->amMoves[0].esMove),
1909 FormatMove(szMove, pms->anBoard,
1910 pml->amMoves[0].anMove),
1911 rMWCTop,
1912 100.0 * ar[0], 100.0 * ar[1], 100.0 * ar[2],
1913 100.0 * (1.0 - ar[0]), 100.0 * ar[3], 100.0 * ar[4]);
1914 else
1915 sprintf(sz, " %4i. %-14s %-28s MWC: %7.3f%%\n"
1916 " %5.3f %5.3f %5.3f -"
1917 " %5.3f %5.3f %5.3f\n",
1918 1, FormatEval(szTemp, &pml->amMoves[0].esMove),
1919 FormatMove(szMove, pms->anBoard,
1920 pml->amMoves[0].anMove), rMWCTop, ar[0], ar[1], ar[2], (1.0 - ar[0]), ar[3], ar[4]);
1921 } else {
1922 ar = pml->amMoves[i].arEvalMove;
1923 rMWC = 100.0 * eq2mwc(pml->amMoves[i].rScore, &ci);
1924
1925 if (fRankKnown)
1926 sprintf(sz, " %4i.", i + 1);
1927 else
1928 strcpy(sz, " ?? ");
1929
1930 if (fOutputWinPC)
1931 sprintf(sz + 6, " %-14s %-28s MWC: %7.3f%% (%+7.3f%%)\n"
1932 " %5.1f%% %5.1f%% %5.1f%% -"
1933 " %5.1f%% %5.1f%% %5.1f%%\n",
1934 FormatEval(szTemp, &pml->amMoves[i].esMove),
1935 FormatMove(szMove, pms->anBoard,
1936 pml->amMoves[i].anMove),
1937 rMWC, rMWC - rMWCTop,
1938 100.0 * ar[0], 100.0 * ar[1], 100.0 * ar[2],
1939 100.0 * (1.0 - ar[0]), 100.0 * ar[3], 100.0 * ar[4]);
1940 else
1941 sprintf(sz + 6, " %-14s %-28s MWC: %7.3f%% (%+7.3f%%)\n"
1942 " %5.3f %5.3f %5.3f -"
1943 " %5.3f %5.3f %5.3f\n",
1944 FormatEval(szTemp, &pml->amMoves[i].esMove),
1945 FormatMove(szMove, pms->anBoard,
1946 pml->amMoves[i].anMove),
1947 rMWC, rMWC - rMWCTop, ar[0], ar[1], ar[2], (1.0 - ar[0]), ar[3], ar[4]);
1948 }
1949 }
1950
1951 return sz;
1952 #endif
1953 }
1954
1955
1956 static void
HintResigned(void)1957 HintResigned(void)
1958 {
1959 float rEqBefore, rEqAfter;
1960 static cubeinfo ci;
1961 static decisionData dd;
1962
1963 GetMatchStateCubeInfo(&ci, &ms);
1964
1965 #if defined(USE_BOARD3D)
1966 if (fX) { /* Stop waving flag, otherwise hangs */
1967 BoardData *bd = BOARD(pwBoard)->board_data;
1968 StopIdle3d(bd, bd->bd3d);
1969 }
1970 #endif
1971
1972 /* evaluate current position */
1973
1974 dd.pboard = msBoard();
1975 dd.pci = &ci;
1976 dd.pec = &GetEvalCube()->ec;
1977 if (RunAsyncProcess((AsyncFun) asyncMoveDecisionE, &dd, _("Considering resignation...")) != 0)
1978 return;
1979
1980 getResignEquities(dd.aarOutput[0], &ci, ms.fResigned, &rEqBefore, &rEqAfter);
1981
1982 #if defined(USE_GTK)
1983 if (fX) {
1984
1985 GTKResignHint(dd.aarOutput[0], rEqBefore, rEqAfter, &ci, ms.nMatchTo && fOutputMWC);
1986
1987 return;
1988
1989 }
1990 #endif
1991
1992 if (!ms.nMatchTo || !fOutputMWC) {
1993
1994 outputf("%s : %+6.3f\n", _("Equity before resignation"), -rEqBefore);
1995 outputf("%s : %+6.3f (%+6.3f)\n\n", _("Equity after resignation"), -rEqAfter, rEqBefore - rEqAfter);
1996 outputf("%s : %s\n\n", _("Correct resign decision"), (rEqBefore - rEqAfter >= 0) ? _("Accept") : _("Reject"));
1997
1998 } else {
1999
2000 rEqBefore = eq2mwc(-rEqBefore, &ci);
2001 rEqAfter = eq2mwc(-rEqAfter, &ci);
2002
2003 outputf("%s : %6.2f%%\n", _("Equity before resignation"), rEqBefore * 100.0f);
2004 outputf("%s : %6.2f%% (%6.2f%%)\n\n", _("Equity after resignation"),
2005 rEqAfter * 100.0f, 100.0f * (rEqAfter - rEqBefore));
2006 outputf("%s : %s\n\n", _("Correct resign decision"), (rEqAfter - rEqBefore >= 0) ? _("Accept") : _("Reject"));
2007 }
2008 }
2009
2010 static int
hint_cube(moverecord * pmr,cubeinfo * pci)2011 hint_cube(moverecord * pmr, cubeinfo * pci)
2012 {
2013 static decisionData dd;
2014 if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE) {
2015 /* no analysis performed yet */
2016 dd.pboard = msBoard();
2017 dd.pci = pci;
2018 dd.pes = GetEvalCube();
2019 if (RunAsyncProcess((AsyncFun) asyncCubeDecision, &dd, _("Considering cube action...")) != 0)
2020 return -1;
2021
2022 pmr_cubedata_set(pmr, dd.pes, dd.aarOutput, dd.aarStdDev);
2023 }
2024 return 0;
2025 }
2026
2027 static skilltype
no_double_skill(moverecord * pmr,cubeinfo * pci)2028 no_double_skill(moverecord * pmr, cubeinfo * pci)
2029 {
2030 float arDouble[4];
2031 float eq = 0.0f;
2032 cubedecision cd;
2033 if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
2034 return SKILL_NONE;
2035 cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
2036 switch (cd) {
2037 case DOUBLE_TAKE:
2038 case DOUBLE_BEAVER:
2039 case REDOUBLE_TAKE:
2040 eq = arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_TAKE];
2041 break;
2042 case DOUBLE_PASS:
2043 case REDOUBLE_PASS:
2044 eq = arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_DROP];
2045 break;
2046 default:
2047 break;
2048 }
2049
2050 return Skill(eq);
2051 }
2052
2053 static skilltype
double_skill(moverecord * pmr,cubeinfo * pci)2054 double_skill(moverecord * pmr, cubeinfo * pci)
2055 {
2056 float arDouble[4];
2057 float eq = 0.0f;
2058 cubedecision cd;
2059 if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
2060 return SKILL_NONE;
2061 cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
2062
2063 switch (cd) {
2064 case NODOUBLE_TAKE:
2065 case NODOUBLE_BEAVER:
2066 case NO_REDOUBLE_TAKE:
2067 case NO_REDOUBLE_BEAVER:
2068 case TOOGOOD_TAKE:
2069 case TOOGOODRE_TAKE:
2070 eq = arDouble[OUTPUT_TAKE] - arDouble[OUTPUT_NODOUBLE];
2071 break;
2072
2073 case TOOGOOD_PASS:
2074 case TOOGOODRE_PASS:
2075 eq = arDouble[OUTPUT_DROP] - arDouble[OUTPUT_NODOUBLE];
2076 break;
2077 default:
2078 break;
2079 }
2080 return Skill(eq);
2081 }
2082
2083 static skilltype
drop_skill(moverecord * pmr,cubeinfo * pci)2084 drop_skill(moverecord * pmr, cubeinfo * pci)
2085 {
2086 float arDouble[4];
2087 float eq = 0.0f;
2088 cubedecision cd;
2089 if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
2090 return SKILL_NONE;
2091 cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
2092 switch (cd) {
2093 case DOUBLE_TAKE:
2094 case DOUBLE_BEAVER:
2095 case REDOUBLE_TAKE:
2096 case NODOUBLE_TAKE:
2097 case NODOUBLE_BEAVER:
2098 case NO_REDOUBLE_TAKE:
2099 case NO_REDOUBLE_BEAVER:
2100 case TOOGOOD_TAKE:
2101 case TOOGOODRE_TAKE:
2102 case OPTIONAL_DOUBLE_BEAVER:
2103 case OPTIONAL_DOUBLE_TAKE:
2104 case OPTIONAL_REDOUBLE_TAKE:
2105 /* equity is for doubling player, invert for response */
2106 eq = arDouble[OUTPUT_TAKE] - arDouble[OUTPUT_DROP];
2107 break;
2108 default:
2109 break;
2110 }
2111 return Skill(eq);
2112 }
2113
2114 static skilltype
take_skill(moverecord * pmr,cubeinfo * pci)2115 take_skill(moverecord * pmr, cubeinfo * pci)
2116 {
2117 float arDouble[4];
2118 float eq = 0.0f;
2119 cubedecision cd;
2120 if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
2121 return SKILL_NONE;
2122 cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
2123 switch (cd) {
2124 case DOUBLE_PASS:
2125 case REDOUBLE_PASS:
2126 case TOOGOOD_PASS:
2127 case TOOGOODRE_PASS:
2128 case OPTIONAL_DOUBLE_PASS:
2129 case OPTIONAL_REDOUBLE_PASS:
2130 /* equity is for doubling player, invert for response */
2131 eq = arDouble[OUTPUT_DROP] - arDouble[OUTPUT_TAKE];
2132 break;
2133 default:
2134 break;
2135 }
2136 return Skill(eq);
2137 }
2138
2139 static skilltype
move_skill(moverecord * pmr)2140 move_skill(moverecord * pmr)
2141 {
2142 move *move_i;
2143 move *move_0;
2144 if (pmr->n.iMove >= pmr->ml.cMoves || !pmr->ml.amMoves)
2145 return SKILL_NONE;
2146 move_i = &pmr->ml.amMoves[pmr->n.iMove];
2147 move_0 = &pmr->ml.amMoves[0];
2148 if (move_i->esMove.et == EVAL_NONE || move_0->esMove.et == EVAL_NONE)
2149 return SKILL_NONE;
2150 else
2151 return Skill(move_i->rScore - move_0->rScore);
2152 }
2153
2154 extern void
find_skills(moverecord * pmr,const matchstate * pms,int did_double,int did_take)2155 find_skills(moverecord * pmr, const matchstate * pms, int did_double, int did_take)
2156 {
2157 cubeinfo ci;
2158 doubletype dt = DoubleType(pms->fDoubled, pms->fMove, pms->fTurn);
2159 taketype tt = (taketype) dt;
2160 GetMatchStateCubeInfo(&ci, pms);
2161
2162 if (pmr->mt != MOVE_NORMAL && pmr->mt != MOVE_DOUBLE && pmr->mt != MOVE_TAKE && pmr->mt != MOVE_DROP) {
2163 pmr->n.stMove = SKILL_NONE;
2164 pmr->stCube = SKILL_NONE;
2165 return;
2166 }
2167 if (pmr->mt == MOVE_DOUBLE && dt != DT_NORMAL) {
2168 pmr->stCube = SKILL_NONE;
2169 return;
2170 }
2171 if (pmr->mt == MOVE_TAKE && tt > TT_NORMAL) {
2172 pmr->stCube = SKILL_NONE;
2173 return;
2174 }
2175
2176 if (did_double == FALSE)
2177 pmr->stCube = no_double_skill(pmr, &ci);
2178 else if (did_double == TRUE)
2179 pmr->stCube = double_skill(pmr, &ci);
2180 else if (did_take == FALSE)
2181 pmr->stCube = drop_skill(pmr, &ci);
2182 else if (did_take == TRUE)
2183 pmr->stCube = take_skill(pmr, &ci);
2184
2185 if (pmr->mt == MOVE_NORMAL && pmr->ml.cMoves > 0 && pmr->n.iMove < pmr->ml.cMoves)
2186 pmr->n.stMove = move_skill(pmr);
2187
2188 }
2189
2190 extern void
hint_double(int show,int did_double)2191 hint_double(int show, int did_double)
2192 {
2193 static cubeinfo ci;
2194 moverecord *pmr;
2195 int hist;
2196 doubletype dt = DoubleType(ms.fDoubled, ms.fMove, ms.fTurn);
2197 if (dt != DT_NORMAL) {
2198 if (show)
2199 outputerrf(_("This decision is part of beaver/raccoon sequence and cannot be hinted"));
2200 return;
2201 }
2202
2203 GetMatchStateCubeInfo(&ci, &ms);
2204
2205 if (!GetDPEq(NULL, NULL, &ci)) {
2206 outputerrf(_("You cannot double."));
2207 return;
2208 }
2209
2210 pmr = get_current_moverecord(&hist);
2211
2212 if (!pmr)
2213 return;
2214
2215 if (hint_cube(pmr, &ci) < 0)
2216 return;
2217
2218 if (hist && did_double == -1)
2219 did_double = (pmr->mt == MOVE_DOUBLE) ? TRUE : FALSE;
2220
2221 find_skills(pmr, &ms, did_double, -1);
2222
2223 #if defined(USE_GTK)
2224 if (fX) {
2225 if (hist && show)
2226 ChangeGame(NULL);
2227 if (show)
2228 GTKCubeHint(pmr, &ms, did_double, -1, hist);
2229 return;
2230 }
2231 #endif
2232 outputl(OutputCubeAnalysis
2233 (pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble, &ci));
2234 }
2235
2236 extern void
hint_take(int show,int did_take)2237 hint_take(int show, int did_take)
2238 {
2239 static cubeinfo ci;
2240 moverecord *pmr;
2241 int hist;
2242 taketype tt = (taketype) DoubleType(ms.fDoubled, ms.fMove, ms.fTurn);
2243 if (tt > TT_NORMAL) {
2244 if (show)
2245 outputerrf(_("This decision is part of beaver/raccoon sequence and cannot be hinted"));
2246 return;
2247 }
2248
2249 GetMatchStateCubeInfo(&ci, &ms);
2250 pmr = get_current_moverecord(&hist);
2251 if (!pmr)
2252 return;
2253 if (hint_cube(pmr, &ci) < 0)
2254 return;
2255
2256 if (hist && did_take == -1)
2257 did_take = (pmr->mt == MOVE_TAKE) ? TRUE : FALSE;
2258
2259 find_skills(pmr, &ms, -1, did_take);
2260
2261 #if defined(USE_GTK)
2262 if (fX) {
2263 if (hist && show)
2264 ChangeGame(NULL);
2265 if (show)
2266 GTKCubeHint(pmr, &ms, -1, did_take, hist);
2267 return;
2268 }
2269 #endif
2270
2271 outputl(OutputCubeAnalysis
2272 (pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble, &ci));
2273 }
2274
2275 extern void
hint_move(char * sz,gboolean show,procrecorddata * procdatarec)2276 hint_move(char *sz, gboolean show, procrecorddata * procdatarec)
2277 {
2278 unsigned int i;
2279 char szBuf[1024];
2280 int parse_n = ParseNumber(&sz);
2281 unsigned int n = (parse_n <= 0) ? 10 : parse_n;
2282 moverecord *pmr;
2283 cubeinfo ci;
2284 int hist;
2285 movelist ml;
2286 findData fd;
2287 int fSaveShowProg = fShowProgress;
2288
2289 if (!ms.anDice[0])
2290 return;
2291
2292 GetMatchStateCubeInfo(&ci, &ms);
2293
2294 pmr = get_current_moverecord(&hist);
2295 if (!pmr)
2296 return;
2297
2298 if (pmr->esChequer.et == EVAL_NONE) {
2299 fd.pml = &ml;
2300 fd.pboard = msBoard();
2301 fd.keyMove = NULL;
2302 fd.rThr = arSkillLevel[SKILL_DOUBTFUL];
2303 fd.pci = &ci;
2304 fd.pec = &GetEvalChequer()->ec;
2305 fd.aamf = *GetEvalMoveFilter();
2306 if (procdatarec) {
2307 show = FALSE;
2308 fShowProgress = (procdatarec->avInputData[PROCREC_HINT_ARGIN_SHOWPROGRESS] != NULL);
2309 }
2310 if ((RunAsyncProcess((AsyncFun) asyncFindMove, &fd, _("Considering move...")) != 0) || fInterrupt) {
2311 fShowProgress = fSaveShowProg;
2312 return;
2313 }
2314 fShowProgress = fSaveShowProg;
2315
2316 pmr_movelist_set(pmr, GetEvalChequer(), &ml);
2317 }
2318 #if defined(USE_GTK)
2319 if (!hist && fX)
2320 GTKGetMove(pmr->n.anMove);
2321 #endif
2322 if (pmr->n.anMove[0] == -1) {
2323 pmr->n.iMove = UINT_MAX;
2324 pmr->n.stMove = SKILL_NONE;
2325 } else {
2326 pmr->n.iMove = locateMove(msBoard(), pmr->n.anMove, &pmr->ml);
2327 /* Tutor mode may have called asyncFindMove() above before
2328 * n.iMove was known. Do it again, ensuring that the actual
2329 * move is evaluated at the best ply. */
2330 fd.pml = &ml;
2331 fd.pboard = msBoard();
2332 fd.keyMove = &(pmr->ml.amMoves[pmr->n.iMove].key);
2333 fd.rThr = arSkillLevel[SKILL_DOUBTFUL];
2334 fd.pci = &ci;
2335 fd.pec = &GetEvalChequer()->ec;
2336 fd.aamf = *GetEvalMoveFilter();
2337 asyncFindMove(&fd);
2338 pmr_movelist_set(pmr, GetEvalChequer(), &ml);
2339 find_skills(pmr, &ms, FALSE, -1);
2340 }
2341 #if defined(USE_GTK)
2342 if (!procdatarec && fX) {
2343 if (hist && show)
2344 ChangeGame(NULL);
2345 if (show)
2346 GTKHint(pmr, hist);
2347 return;
2348 } else
2349 #endif
2350 if (!procdatarec && !show)
2351 return;
2352
2353 if (!procdatarec && show && !pmr->ml.cMoves) {
2354 outputl(_("There are no legal moves."));
2355 return;
2356 }
2357
2358 if (procdatarec) {
2359 procdatarec->avOutputData[PROCREC_HINT_ARGOUT_MATCHSTATE] = (void *) &ms;
2360 procdatarec->avOutputData[PROCREC_HINT_ARGOUT_CUBEINFO] = (void *) &ci;
2361 procdatarec->avOutputData[PROCREC_HINT_ARGOUT_MOVELIST] = (void *) &pmr->ml;
2362 procdatarec->avOutputData[PROCREC_HINT_ARGOUT_MOVERECORD] = (void *) &pmr;
2363 }
2364
2365 n = MIN(pmr->ml.cMoves, n);
2366 for (i = 0; i < n; i++) {
2367 if (show)
2368 output(FormatMoveHint(szBuf, &ms, &pmr->ml, i, TRUE, TRUE, TRUE));
2369 else if (procdatarec && procdatarec->pfProcessRecord) {
2370 procdatarec->avOutputData[PROCREC_HINT_ARGOUT_INDEX] = (void *) (ptrdiff_t) i;
2371 if (!procdatarec->pfProcessRecord(procdatarec))
2372 break;
2373 }
2374 }
2375 }
2376
2377 extern void
CommandHint(char * sz)2378 CommandHint(char *sz)
2379 {
2380 listOLD *pl;
2381 moverecord *pmr;
2382
2383 if (ms.gs != GAME_PLAYING) {
2384 outputl(_("You must set up a board first."));
2385
2386 return;
2387 }
2388
2389 /* The code further below handles only the case of asking a hint
2390 * on a resignation by gnubg while playing against it. When
2391 * clicking on a "resign" entry in the moves panel while reviewing
2392 * a game, the match state ms is not up to date and we have to
2393 * look at the moves list */
2394
2395 pl = plLastMove->plNext;
2396 if ((pmr = pl->p) && pmr->mt == MOVE_RESIGN) {
2397 HintResigned();
2398 return;
2399 }
2400
2401 /* hint on cube decision */
2402
2403 if (!ms.anDice[0] && !ms.fDoubled && !ms.fResigned) {
2404 hint_double(TRUE, -1);
2405 return;
2406 }
2407
2408 /* Give hint on resignation */
2409
2410 if (ms.fResigned) {
2411 HintResigned();
2412 return;
2413 }
2414
2415 /* Give hint on take decision */
2416
2417 if (ms.fDoubled) {
2418 hint_take(TRUE, -1);
2419 return;
2420 }
2421
2422 /* Give hint on chequer play decision */
2423
2424 if (ms.anDice[0]) {
2425 hint_move(sz, TRUE, NULL);
2426 return;
2427 }
2428
2429 }
2430
2431 static void
Shutdown(void)2432 Shutdown(void)
2433 {
2434
2435 RenderFinalise();
2436
2437 free_rngctx(rngctxCurrent);
2438 free_rngctx(rngctxRollout);
2439
2440 FreeMatch();
2441 ClearMatch();
2442
2443 #if defined(USE_GTK)
2444 MoveListDestroy();
2445 #endif
2446
2447 MT_Close();
2448
2449 EvalShutdown();
2450
2451 #if defined(USE_PYTHON)
2452 PythonShutdown();
2453 #endif
2454
2455 #if defined(LIBCURL_PROTOCOL_HTTPS)
2456 /* Cleanup curl */
2457 curl_global_cleanup();
2458 #endif
2459
2460 #if defined(HAVE_SOCKETS)
2461 #ifdef WIN32
2462 WSACleanup();
2463 #endif
2464 #endif
2465
2466 SoundWait();
2467 }
2468
2469 /* Called on various exit commands -- e.g. EOF on stdin, "quit" command,
2470 * etc. If stdin is not a TTY, this should always exit immediately (to
2471 * avoid enless loops on EOF). If stdin is a TTY, and fConfirmNew is set,
2472 * and a game is in progress, then we ask the user if they're sure. */
2473 extern void
PromptForExit(void)2474 PromptForExit(void)
2475 {
2476
2477 static int fExiting = FALSE;
2478 #if defined(USE_GTK)
2479 BoardData *bd = NULL;
2480
2481 if (fX) {
2482 bd = BOARD(pwBoard)->board_data;
2483 g_assert(bd);
2484 }
2485 #endif
2486 if (fExiting)
2487 return;
2488
2489 fExiting = TRUE;
2490
2491 if (fInteractive) {
2492 fInterrupt = FALSE;
2493 if (!get_input_discard()) {
2494 fInterrupt = FALSE;
2495 fExiting = FALSE;
2496 return;
2497 }
2498 }
2499 #if defined(USE_BOARD3D)
2500 if (fX && bd && (display_is_3d(bd->rd))) { /* Stop any 3d animations */
2501 StopIdle3d(bd, bd->bd3d);
2502 }
2503 #endif
2504 #if defined(HAVE_SOCKETS)
2505 /* Close any open connections */
2506 if (ap[0].pt == PLAYER_EXTERNAL)
2507 closesocket(ap[0].h);
2508 if (ap[1].pt == PLAYER_EXTERNAL)
2509 closesocket(ap[1].h);
2510 #endif
2511
2512 playSound(SOUND_EXIT);
2513
2514 #if defined(USE_BOARD3D)
2515 if (fX && bd && display_is_3d(bd->rd) && bd->rd->closeBoardOnExit && bd->rd->fHinges3d)
2516 CloseBoard3d(bd, bd->bd3d, bd->rd);
2517 #endif
2518 ProcessEvents();
2519
2520 SoundWait(); /* Wait for sound to finish before final close */
2521
2522 if (fInteractive)
2523 PortableSignalRestore(SIGINT, &shInterruptOld);
2524
2525 #if defined(USE_GTK)
2526 if (fX) {
2527 stop_board_expose(bd);
2528 board_free_pixmaps(bd);
2529 }
2530 #if defined(USE_BOARD3D)
2531 if (fX && gtk_gl_init_success && bd)
2532 Tidy3dObjects(bd->bd3d, bd->rd);
2533 #endif
2534 #endif
2535
2536 #if defined(HAVE_LIB_READLINE)
2537 write_history(gnubg_histfile);
2538 #endif /* HAVE_LIB_READLINE */
2539
2540 #if defined(USE_GTK)
2541 if (gtk_main_level() == 1)
2542 gtk_main_quit();
2543 else
2544 #endif
2545 {
2546 Shutdown();
2547 exit(EXIT_SUCCESS);
2548 }
2549 }
2550
2551 extern void
CommandNotImplemented(char * UNUSED (sz))2552 CommandNotImplemented(char *UNUSED(sz))
2553 {
2554 outputl(_("That command is not yet implemented."));
2555 }
2556
2557 extern void
CommandQuit(char * UNUSED (sz))2558 CommandQuit(char *UNUSED(sz))
2559 {
2560 PromptForExit();
2561 }
2562
2563
2564 extern void
CommandRollout(char * sz)2565 CommandRollout(char *sz)
2566 {
2567 float arOutput[NUM_ROLLOUT_OUTPUTS];
2568 float arStdDev[NUM_ROLLOUT_OUTPUTS];
2569 rolloutstat arsStatistics[2];
2570 TanBoard anBoard;
2571 cubeinfo ci;
2572 char asz[1][40];
2573 void *p;
2574
2575 if (CountTokens(sz) > 0) {
2576 outputerrf("%s", _("The rollout command takes no arguments and only rollouts the current position"));
2577 return;
2578 }
2579 if (ms.gs != GAME_PLAYING) {
2580 outputerrf("%s", _("No position specified and no game in progress."));
2581 return;
2582 }
2583 #if defined(USE_GTK)
2584 if (fX)
2585 GTKShowWarning(WARN_ROLLOUT, NULL);
2586 #endif
2587 sprintf(asz[0], _("Current Position"));
2588 memcpy(anBoard, msBoard(), sizeof(TanBoard));
2589 SetCubeInfo(&ci, ms.nCube, ms.fCubeOwner, ms.fMove, ms.nMatchTo, ms.anScore, ms.fCrawford, ms.fJacoby, nBeavers,
2590 ms.bgv);
2591 RolloutProgressStart(&ci, 1, NULL, &rcRollout, asz, FALSE, &p);
2592 GeneralEvaluationR(arOutput, arStdDev, arsStatistics, (ConstTanBoard) anBoard, &ci, &rcRollout, RolloutProgress, p);
2593 RolloutProgressEnd(&p, FALSE);
2594
2595 }
2596
2597 static void
LoadCommands(FILE * pf,char * szFile)2598 LoadCommands(FILE * pf, char *szFile)
2599 {
2600 char sz[2048], *pch;
2601
2602 outputpostpone();
2603
2604 /* FIXME shouldn't restart sys calls on signals during this fgets */
2605 while (fgets(sz, sizeof(sz), pf) != NULL) {
2606
2607 if ((pch = strchr(sz, '\n')))
2608 *pch = 0;
2609 if ((pch = strchr(sz, '\r')))
2610 *pch = 0;
2611
2612 if (fInterrupt) {
2613 outputresume();
2614 return;
2615 }
2616
2617 if (*sz == '#') /* Comment */
2618 continue;
2619
2620 #if defined(USE_PYTHON)
2621
2622 if (!strcmp(sz, ">")) {
2623
2624 /* Python escape. */
2625
2626 /* Ideally we should be able to handle both > print 1+1 sys.exit() and > print 1+1
2627 * currently we only handle the latter... */
2628
2629 outputerrf("%s", _("Only Python commands supported, not multiline code"));
2630
2631 continue;
2632
2633 }
2634 #endif /* USE_PYTHON */
2635
2636 HandleCommand(sz, acTop);
2637
2638 /* FIXME handle NextTurn events? */
2639 }
2640 if (ferror(pf)) {
2641 outputerr(szFile);
2642 }
2643
2644 outputresume();
2645 }
2646
2647 extern void
CommandLoadPython(char * sz)2648 CommandLoadPython(char *sz)
2649 {
2650 sz = NextToken(&sz);
2651
2652 if (sz && *sz)
2653 #if defined(USE_PYTHON)
2654 LoadPythonFile(sz, FALSE);
2655 #else
2656 outputl(_("This build of GNU Backgammon does not support Python"));
2657 #endif
2658 else
2659 outputl(_("You must specify a file to load from."));
2660 }
2661
2662 extern void
CommandLoadCommands(char * sz)2663 CommandLoadCommands(char *sz)
2664 {
2665 FILE *pf;
2666
2667 #if defined(WIN32)
2668 /* Make sure unquoted filenames are quoted before processing
2669 * to allow proper support for filenames with spaces */
2670 char *szQuoted = NULL;
2671 char *szQuotedTemp = NULL;
2672 if (sz[0] != '"') {
2673 szQuoted = szQuotedTemp = g_strdup_printf("\"%s\"", sz);
2674 sz = NextToken(&szQuoted);
2675 } else
2676 sz = NextToken(&sz);
2677 #else
2678 sz = NextToken(&sz);
2679 #endif
2680
2681 if (!sz || !*sz) {
2682 outputl(_("You must specify a file to load from."));
2683 return;
2684 }
2685
2686 if ((pf = gnubg_g_fopen(sz, "r"))) {
2687 LoadCommands(pf, sz);
2688 fclose(pf);
2689 } else
2690 outputerr(sz);
2691
2692 #if defined(WIN32)
2693 if (szQuotedTemp)
2694 g_free(szQuotedTemp);
2695 #endif
2696 }
2697
2698
2699 extern void
CommandCopy(char * UNUSED (sz))2700 CommandCopy(char *UNUSED(sz))
2701 {
2702 char *aps[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
2703 char szOut[2048];
2704 char szCube[32], szPlayer0[MAX_NAME_LEN + 3], szPlayer1[MAX_NAME_LEN + 3], szScore0[35], szScore1[35], szMatch[35];
2705 char szRolled[32];
2706 TanBoard anBoardTemp;
2707
2708 aps[0] = szPlayer0;
2709 aps[6] = szPlayer1;
2710
2711 sprintf(aps[1] = szScore0, ngettext("%d point", "%d points", ms.anScore[0]), ms.anScore[0]);
2712
2713 sprintf(aps[5] = szScore1, ngettext("%d point", "%d points", ms.anScore[1]), ms.anScore[1]);
2714
2715 if (ms.fDoubled) {
2716 aps[ms.fTurn ? 4 : 2] = szCube;
2717
2718 sprintf(szPlayer0, "O: %s", ap[0].szName);
2719 sprintf(szPlayer1, "X: %s", ap[1].szName);
2720 sprintf(szCube, _("Cube offered at %d"), ms.nCube << 1);
2721 } else {
2722 sprintf(szPlayer0, "O: %s", ap[0].szName);
2723 sprintf(szPlayer1, "X: %s", ap[1].szName);
2724
2725 aps[ms.fMove ? 4 : 2] = szRolled;
2726
2727 if (ms.anDice[0])
2728 sprintf(szRolled, "%s %u%u", _("Rolled"), ms.anDice[0], ms.anDice[1]);
2729 else if (!GameStatus(msBoard(), ms.bgv))
2730 strcpy(szRolled, _("On roll"));
2731 else
2732 szRolled[0] = 0;
2733
2734 if (ms.fCubeOwner < 0) {
2735 aps[3] = szCube;
2736
2737 if (ms.nMatchTo)
2738 if (ms.nMatchTo == 1)
2739 sprintf(szCube, _("1 point match"));
2740 else if (ms.fCrawford)
2741 sprintf(szCube, _("%d point match (Crawford game)"), ms.nMatchTo);
2742 else
2743 sprintf(szCube, _("%d point match (Cube: %d)"), ms.nMatchTo, ms.nCube);
2744 else
2745 sprintf(szCube, "(%s: %d)", _("Cube"), ms.nCube);
2746 } else {
2747 size_t cch = strlen(ap[ms.fCubeOwner].szName);
2748
2749 if (cch > 20)
2750 cch = 20;
2751
2752 sprintf(szCube, "%c: %*s (%s: %d)", ms.fCubeOwner ? 'X' :
2753 'O', (int) cch, ap[ms.fCubeOwner].szName, _("Cube"), ms.nCube);
2754
2755 aps[ms.fCubeOwner ? 6 : 0] = szCube;
2756
2757 if (ms.nMatchTo)
2758 sprintf(aps[3] = szMatch, _("%d point match"), ms.nMatchTo);
2759 }
2760 }
2761
2762 memcpy(anBoardTemp, msBoard(), sizeof(TanBoard));
2763
2764 if (!ms.fMove)
2765 SwapSides(anBoardTemp);
2766
2767 DrawBoard(szOut, (ConstTanBoard) anBoardTemp, ms.fMove, aps, MatchIDFromMatchState(&ms), anChequers[ms.bgv]);
2768
2769 {
2770 unsigned int anPips[2];
2771 char szPipCount[32];
2772
2773 PipCount((ConstTanBoard) anBoardTemp, anPips);
2774 sprintf(szPipCount, "Pip counts : O %u, X %u\n", anPips[0], anPips[1]);
2775
2776 strcat(szOut, " ");
2777 strcat(szOut, szPipCount);
2778 }
2779
2780 TextToClipboard(szOut);
2781 }
2782
2783 static void
LoadRCFiles(void)2784 LoadRCFiles(void)
2785 {
2786 char *sz, *szz;
2787
2788 loading_rc = TRUE;
2789 outputoff();
2790 sz = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
2791 szz = g_strdup_printf("\"%s\"", sz);
2792 if (g_file_test(sz, G_FILE_TEST_EXISTS))
2793 CommandLoadCommands(szz);
2794
2795 UpdateSettings();
2796 g_free(sz);
2797 g_free(szz);
2798
2799 sz = g_build_filename(szHomeDirectory, "gnubgrc", NULL);
2800 szz = g_strdup_printf("\"%s\"", sz);
2801 if (g_file_test(sz, G_FILE_TEST_EXISTS))
2802 CommandLoadCommands(szz);
2803 g_free(sz);
2804 g_free(szz);
2805
2806 outputon();
2807 loading_rc = FALSE;
2808 }
2809
2810
2811 static void
SaveRNGSettings(FILE * pf,const char * sz,rng rngCurrent,rngcontext * rngctx)2812 SaveRNGSettings(FILE * pf, const char *sz, rng rngCurrent, rngcontext * rngctx)
2813 {
2814
2815 switch (rngCurrent) {
2816 case RNG_BBS:
2817 fprintf(pf, "%s rng bbs\n", sz); /* FIXME save modulus */
2818 break;
2819 case RNG_ISAAC:
2820 fprintf(pf, "%s rng isaac\n", sz);
2821 break;
2822 case RNG_MANUAL:
2823 fprintf(pf, "%s rng manual\n", sz);
2824 break;
2825 case RNG_MD5:
2826 fprintf(pf, "%s rng md5\n", sz);
2827 break;
2828 case RNG_MERSENNE:
2829 fprintf(pf, "%s rng mersenne\n", sz);
2830 break;
2831 case RNG_RANDOM_DOT_ORG:
2832 fprintf(pf, "%s rng random.org\n", sz);
2833 break;
2834 case RNG_FILE:
2835 fprintf(pf, "%s rng file \"%s\"\n", sz, GetDiceFileName(rngctx));
2836 break;
2837 default:
2838 break;
2839 }
2840
2841 }
2842
2843
2844 static void
SaveMoveFilterSettings(FILE * pf,const char * sz,movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES])2845 SaveMoveFilterSettings(FILE * pf, const char *sz, movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES])
2846 {
2847
2848 int i, j;
2849 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
2850
2851 for (i = 0; i < MAX_FILTER_PLIES; ++i)
2852 for (j = 0; j <= i; ++j) {
2853 fprintf(pf, "%s %d %d %d %d %s\n",
2854 sz,
2855 i + 1, j,
2856 aamf[i][j].Accept,
2857 aamf[i][j].Extra, g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%0.3g", aamf[i][j].Threshold));
2858 }
2859 }
2860
2861
2862
2863
2864 static void
SaveEvalSettings(FILE * pf,const char * sz,evalcontext * pec)2865 SaveEvalSettings(FILE * pf, const char *sz, evalcontext * pec)
2866 {
2867
2868 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
2869 gchar *szNoise = g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", pec->rNoise);
2870 fprintf(pf, "%s plies %u\n"
2871 "%s prune %s\n"
2872 "%s cubeful %s\n"
2873 "%s noise %s\n"
2874 "%s deterministic %s\n",
2875 sz, pec->nPlies,
2876 sz, pec->fUsePrune ? "on" : "off",
2877 sz, pec->fCubeful ? "on" : "off", sz, szNoise, sz, pec->fDeterministic ? "on" : "off");
2878 }
2879
2880
2881 extern void
SaveRolloutSettings(FILE * pf,const char * sz,rolloutcontext * prc)2882 SaveRolloutSettings(FILE * pf, const char *sz, rolloutcontext * prc)
2883 {
2884
2885 char *pch;
2886 int i; /* flags and stuff */
2887 gchar szTemp1[G_ASCII_DTOSTR_BUF_SIZE];
2888 gchar szTemp2[G_ASCII_DTOSTR_BUF_SIZE];
2889
2890 g_ascii_formatd(szTemp1, G_ASCII_DTOSTR_BUF_SIZE, "%05.4f", prc->rStdLimit);
2891 g_ascii_formatd(szTemp2, G_ASCII_DTOSTR_BUF_SIZE, "%05.4f", prc->rJsdLimit);
2892
2893 fprintf(pf,
2894 "%s cubeful %s\n"
2895 "%s varredn %s\n"
2896 "%s quasirandom %s\n"
2897 "%s initial %s\n"
2898 "%s truncation enable %s\n"
2899 "%s truncation plies %u\n"
2900 "%s bearofftruncation exact %s\n"
2901 "%s bearofftruncation onesided %s\n"
2902 "%s later enable %s\n"
2903 "%s later plies %u\n"
2904 "%s trials %u\n"
2905 "%s cube-equal-chequer %s\n"
2906 "%s players-are-same %s\n"
2907 "%s truncate-equal-player0 %s\n"
2908 "%s limit enable %s\n"
2909 "%s limit minimumgames %u\n"
2910 "%s limit maxerror %s\n"
2911 "%s jsd stop %s\n"
2912 "%s jsd minimumgames %u\n"
2913 "%s jsd limit %s\n",
2914 sz, prc->fCubeful ? "on" : "off",
2915 sz, prc->fVarRedn ? "on" : "off",
2916 sz, prc->fRotate ? "on" : "off",
2917 sz, prc->fInitial ? "on" : "off",
2918 sz, prc->fDoTruncate ? "on" : "off",
2919 sz, prc->nTruncate,
2920 sz, prc->fTruncBearoff2 ? "on" : "off",
2921 sz, prc->fTruncBearoffOS ? "on" : "off",
2922 sz, prc->fLateEvals ? "on" : "off",
2923 sz, prc->nLate,
2924 sz, prc->nTrials,
2925 sz, fCubeEqualChequer ? "on" : "off",
2926 sz, fPlayersAreSame ? "on" : "off",
2927 sz, fTruncEqualPlayer0 ? "on" : "off",
2928 sz, prc->fStopOnSTD ? "on" : "off",
2929 sz, prc->nMinimumGames,
2930 sz, szTemp1, sz, prc->fStopOnJsd ? "on" : "off", sz, prc->nMinimumJsdGames, sz, szTemp2);
2931
2932 SaveRNGSettings(pf, sz, prc->rngRollout, rngctxRollout);
2933
2934 /* chequer play and cube decision evalcontexts */
2935
2936 pch = malloc(strlen(sz) + 50);
2937
2938 strcpy(pch, sz);
2939
2940 for (i = 0; i < 2; i++) {
2941
2942 sprintf(pch, "%s player %i chequerplay", sz, i);
2943 SaveEvalSettings(pf, pch, &prc->aecChequer[i]);
2944
2945 sprintf(pch, "%s player %i cubedecision", sz, i);
2946 SaveEvalSettings(pf, pch, &prc->aecCube[i]);
2947
2948 sprintf(pch, "%s player %i movefilter", sz, i);
2949 SaveMoveFilterSettings(pf, pch, prc->aaamfChequer[i]);
2950
2951 }
2952
2953 for (i = 0; i < 2; i++) {
2954
2955 sprintf(pch, "%s later player %i chequerplay", sz, i);
2956 SaveEvalSettings(pf, pch, &prc->aecChequerLate[i]);
2957
2958 sprintf(pch, "%s later player %i cubedecision", sz, i);
2959 SaveEvalSettings(pf, pch, &prc->aecCubeLate[i]);
2960
2961 sprintf(pch, "%s later player %i movefilter", sz, i);
2962 SaveMoveFilterSettings(pf, pch, prc->aaamfLate[i]);
2963
2964 }
2965
2966 sprintf(pch, "%s truncation cubedecision", sz);
2967 SaveEvalSettings(pf, pch, &prc->aecCubeTrunc);
2968 sprintf(pch, "%s truncation chequerplay", sz);
2969 SaveEvalSettings(pf, pch, &prc->aecChequerTrunc);
2970
2971 free(pch);
2972
2973 }
2974
2975 static void
SaveEvalSetupSettings(FILE * pf,const char * sz,evalsetup * pes)2976 SaveEvalSetupSettings(FILE * pf, const char *sz, evalsetup * pes)
2977 {
2978
2979 char szTemp[1024];
2980
2981 switch (pes->et) {
2982 case EVAL_EVAL:
2983 fprintf(pf, "%s type evaluation\n", sz);
2984 break;
2985 case EVAL_ROLLOUT:
2986 fprintf(pf, "%s type rollout\n", sz);
2987 break;
2988 default:
2989 break;
2990 }
2991
2992 strcpy(szTemp, sz);
2993 SaveEvalSettings(pf, strcat(szTemp, " evaluation"), &pes->ec);
2994
2995 strcpy(szTemp, sz);
2996 SaveRolloutSettings(pf, strcat(szTemp, " rollout"), &pes->rc);
2997
2998 }
2999
3000 static void
SaveAnalysisSettings(FILE * pf)3001 SaveAnalysisSettings(FILE * pf)
3002 {
3003 gchar aszThr[7][G_ASCII_DTOSTR_BUF_SIZE];
3004 g_ascii_formatd(aszThr[0], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_BAD]);
3005 g_ascii_formatd(aszThr[1], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_DOUBTFUL]);
3006 g_ascii_formatd(aszThr[2], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_GOOD]);
3007 g_ascii_formatd(aszThr[3], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_BAD]);
3008 g_ascii_formatd(aszThr[4], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_VERYBAD]);
3009 g_ascii_formatd(aszThr[5], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_VERYGOOD]);
3010 g_ascii_formatd(aszThr[6], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_VERYBAD]);
3011
3012 SaveEvalSetupSettings(pf, "set analysis chequerplay", &esAnalysisChequer);
3013 SaveEvalSetupSettings(pf, "set analysis cubedecision", &esAnalysisCube);
3014 SaveMoveFilterSettings(pf, "set analysis movefilter", aamfAnalysis);
3015 SaveEvalSettings(pf, "set analysis luckanalysis", &ecLuck);
3016 fprintf(pf, "set analysis threshold bad %s\n", aszThr[0]);
3017 fprintf(pf, "set analysis threshold doubtful %s\n", aszThr[1]);
3018 fprintf(pf, "set analysis threshold lucky %s\n", aszThr[2]);
3019 fprintf(pf, "set analysis threshold unlucky %s\n", aszThr[3]);
3020 fprintf(pf, "set analysis threshold verybad %s\n", aszThr[4]);
3021 fprintf(pf, "set analysis threshold verylucky %s\n", aszThr[5]);
3022 fprintf(pf, "set analysis threshold veryunlucky %s\n", aszThr[6]);
3023 fprintf(pf, "set analysis cube %s\n", fAnalyseCube ? "on" : "off");
3024 fprintf(pf, "set analysis luck %s\n", fAnalyseDice ? "on" : "off");
3025 fprintf(pf, "set analysis moves %s\n", fAnalyseMove ? "on" : "off");
3026 fprintf(pf, "set analysis player 0 analyse %s\n", afAnalysePlayers[0] ? "yes" : "no");
3027 fprintf(pf, "set analysis player 1 analyse %s\n", afAnalysePlayers[1] ? "yes" : "no");
3028 }
3029
3030 static void
SaveImportExportSettings(FILE * pf)3031 SaveImportExportSettings(FILE * pf)
3032 {
3033 int i;
3034 if (default_import_folder && *default_import_folder)
3035 fprintf(pf, "set import folder \"%s\"\n", default_import_folder);
3036 if (default_export_folder && *default_export_folder)
3037 fprintf(pf, "set export folder \"%s\"\n", default_export_folder);
3038 if (default_sgf_folder && *default_sgf_folder)
3039 fprintf(pf, "set sgf folder \"%s\"\n", default_sgf_folder);
3040
3041 fprintf(pf, "set export include annotations %s\n", exsExport.fIncludeAnnotation ? "yes" : "no");
3042 fprintf(pf, "set export include analysis %s\n", exsExport.fIncludeAnalysis ? "yes" : "no");
3043 fprintf(pf, "set export include statistics %s\n", exsExport.fIncludeStatistics ? "yes" : "no");
3044 fprintf(pf, "set export include matchinfo %s\n", exsExport.fIncludeMatchInfo ? "yes" : "no");
3045 fprintf(pf, "set export show board %d\n", exsExport.fDisplayBoard);
3046
3047 if (exsExport.fSide == 3)
3048 fprintf(pf, "set export show player both\n");
3049 else if (exsExport.fSide)
3050 fprintf(pf, "set export show player %d\n", exsExport.fSide - 1);
3051
3052 fprintf(pf, "set export move number %u\n", exsExport.nMoves);
3053 fprintf(pf, "set export moves parameters evaluation %s\n", exsExport.afMovesParameters[0] ? "yes" : "no");
3054 fprintf(pf, "set export moves parameters rollout %s\n", exsExport.afMovesParameters[1] ? "yes" : "no");
3055 fprintf(pf, "set export moves probabilities %s\n", exsExport.fMovesDetailProb ? "yes" : "no");
3056
3057 for (i = 0; i < N_SKILLS; i++) {
3058 if (i == SKILL_NONE)
3059 fprintf(pf, "set export moves display unmarked %s\n", exsExport.afMovesDisplay[i] ? "yes" : "no");
3060 else
3061 fprintf(pf, "set export moves display %s %s\n",
3062 aszSkillTypeCommand[i], exsExport.afMovesDisplay[i] ? "yes" : "no");
3063 }
3064
3065 fprintf(pf, "set export cube parameters evaluation %s\n", exsExport.afCubeParameters[0] ? "yes" : "no");
3066 fprintf(pf, "set export cube parameters rollout %s\n", exsExport.afCubeParameters[1] ? "yes" : "no");
3067 fprintf(pf, "set export cube probabilities %s\n", exsExport.fCubeDetailProb ? "yes" : "no");
3068
3069 for (i = 0; i < N_SKILLS; i++) {
3070 if (i == SKILL_NONE)
3071 fprintf(pf, "set export cube display unmarked %s\n", exsExport.afCubeDisplay[i] ? "yes" : "no");
3072 else
3073 fprintf(pf, "set export cube display %s %s\n",
3074 aszSkillTypeCommand[i], exsExport.afCubeDisplay[i] ? "yes" : "no");
3075 }
3076
3077 fprintf(pf, "set export cube display actual %s\n", exsExport.afCubeDisplay[EXPORT_CUBE_ACTUAL] ? "yes" : "no");
3078 fprintf(pf, "set export cube display missed %s\n", exsExport.afCubeDisplay[EXPORT_CUBE_MISSED] ? "yes" : "no");
3079 fprintf(pf, "set export cube display close %s\n", exsExport.afCubeDisplay[EXPORT_CUBE_CLOSE] ? "yes" : "no");
3080 fprintf(pf, "set export html pictureurl \"%s\"\n", exsExport.szHTMLPictureURL);
3081 fprintf(pf, "set export html type \"%s\"\n", aszHTMLExportType[exsExport.het]);
3082 fprintf(pf, "set export html css %s\n", aszHTMLExportCSSCommand[exsExport.hecss]);
3083 fprintf(pf, "set export png size %d\n", exsExport.nPNGSize);
3084 fprintf(pf, "set export html size %d\n", exsExport.nHtmlSize);
3085
3086 }
3087
3088 #if defined(USE_GTK)
3089 static void
SaveGUISettings(FILE * pf)3090 SaveGUISettings(FILE * pf)
3091 {
3092 const char *aszAnimation[] = { "none", "blink", "slide" };
3093 const char *aszShowPips[N_GUI_SHOW_PIPS] = { "none", "pips", "epc", "wastage" };
3094 int dummy;
3095
3096 SaveWindowSettings(pf);
3097 WriteWarnings(pf);
3098 if (fX)
3099 GTKSaveSettings();
3100
3101 fprintf(pf, "set fullscreen %s\n", fFullScreen ? "on" : "off");
3102 if (fFullScreen) {
3103 GetFullscreenWindowSettings(&dummy, &fShowIDs, &dummy);
3104 fprintf(pf, "set gui showids %s\n", fShowIDs ? "on" : "off");
3105 fShowIDs = FALSE;
3106 } else
3107 fprintf(pf, "set gui showids %s\n", fShowIDs ? "on" : "off");
3108
3109 fprintf(pf, "set gui animation %s\n", aszAnimation[animGUI]);
3110 fprintf(pf, "set gui animation speed %u\n", nGUIAnimSpeed);
3111 fprintf(pf, "set gui beep %s\n", fGUIBeep ? "on" : "off");
3112 fprintf(pf, "set gui dicearea %s\n", GetMainAppearance()->fDiceArea ? "on" : "off");
3113 fprintf(pf, "set gui highdiefirst %s\n", fGUIHighDieFirst ? "on" : "off");
3114 fprintf(pf, "set gui illegal %s\n", fGUIIllegal ? "on" : "off");
3115 fprintf(pf, "set gui showpips %s\n", aszShowPips[gui_show_pips]);
3116 fprintf(pf, "set gui dragtargethelp %s\n", fGUIDragTargetHelp ? "on" : "off");
3117 fprintf(pf, "set gui usestatspanel %s\n", fGUIUseStatsPanel ? "on" : "off");
3118 fprintf(pf, "set gui movelistdetail %s\n", showMoveListDetail ? "on" : "off");
3119 fprintf(pf, "set gui grayedit %s\n", fGUIGrayEdit ? "on" : "off");
3120 fprintf(pf, "set gui windowpositions %s\n", fGUISetWindowPos ? "on" : "off");
3121
3122 fprintf(pf, "set styledgamelist %s\n", fStyledGamelist ? "on" : "off");
3123 fprintf(pf, "set delay %u\n", nDelay);
3124
3125 fprintf(pf, "set toolbar %d\n", nToolbarStyle);
3126 if (!fToolbarShowing)
3127 fputs("set toolbar off\n", pf);
3128
3129 #if defined(USE_BOARD3D)
3130 if (fSync != -1)
3131 fprintf(pf, "set vsync3d %s\n", fSync ? "yes" : "no");
3132 #endif
3133 }
3134 #endif
3135
3136 static void
SaveSoundSettings(FILE * pf)3137 SaveSoundSettings(FILE * pf)
3138 {
3139 gnubgsound i;
3140 fprintf(pf, "set sound enable %s\n", fSound ? "yes" : "no");
3141 fprintf(pf, "set sound system command %s\n", sound_get_command());
3142
3143 for (i = (gnubgsound) 0; i < NUM_SOUNDS; ++i) {
3144 char *file = GetSoundFile(i);
3145 fprintf(pf, "set sound sound %s \"%s\"\n", sound_command[i], file);
3146 g_free(file);
3147 }
3148 }
3149
3150 static void
SavePlayerSettings(FILE * pf)3151 SavePlayerSettings(FILE * pf)
3152 {
3153 int i;
3154 char szTemp[4096];
3155
3156 fprintf(pf, "set defaultnames \"%s\" \"%s\"\n", default_names[0], default_names[1]);
3157 if (strlen(player1aliases) > 0)
3158 fprintf(pf, "set aliases %s\n", player1aliases);
3159
3160 for (i = 0; i < 2; i++) {
3161 fprintf(pf, "set player %d name %s\n", i, ap[i].szName);
3162
3163 switch (ap[i].pt) {
3164 case PLAYER_GNU:
3165 fprintf(pf, "set player %d gnubg\n", i);
3166 sprintf(szTemp, "set player %d chequerplay", i);
3167 SaveEvalSetupSettings(pf, szTemp, &ap[i].esChequer);
3168 sprintf(szTemp, "set player %d cubedecision", i);
3169 SaveEvalSetupSettings(pf, szTemp, &ap[i].esCube);
3170 sprintf(szTemp, "set player %d movefilter", i);
3171 SaveMoveFilterSettings(pf, szTemp, ap[i].aamf);
3172 break;
3173
3174 case PLAYER_HUMAN:
3175 fprintf(pf, "set player %d human\n", i);
3176 break;
3177
3178 case PLAYER_EXTERNAL:
3179 /* don't save external players */
3180 break;
3181 }
3182 }
3183 fprintf(pf, "set cheat enable %s\n", fCheat ? "on" : "off");
3184 for (i = 0; i < 2; ++i)
3185 fprintf(pf, "set cheat player %d roll %u\n", i, afCheatRoll[i] + 1);
3186
3187 }
3188
3189 static void
SavePlayingSettings(FILE * pf)3190 SavePlayingSettings(FILE * pf)
3191 {
3192 fprintf(pf, "set automatic bearoff %s\n", fAutoBearoff ? "on" : "off");
3193 fprintf(pf, "set automatic crawford %s\n", fAutoCrawford ? "on" : "off");
3194 fprintf(pf, "set automatic game %s\n", fAutoGame ? "on" : "off");
3195 fprintf(pf, "set automatic move %s\n", fAutoMove ? "on" : "off");
3196 fprintf(pf, "set automatic roll %s\n", fAutoRoll ? "on" : "off");
3197 }
3198
3199 static void
SaveRuleSettings(FILE * pf)3200 SaveRuleSettings(FILE * pf)
3201 {
3202 fprintf(pf, "set automatic doubles %u\n", cAutoDoubles);
3203 fprintf(pf, "set beavers %u\n", nBeavers);
3204 fprintf(pf, "set jacoby %s\n", fJacoby ? "on" : "off");
3205 fprintf(pf, "set matchlength %u\n", nDefaultLength);
3206 fprintf(pf, "set variation %s\n", aszVariationCommands[bgvDefault]);
3207 }
3208
3209 static void
SaveEvaluationSettings(FILE * pf)3210 SaveEvaluationSettings(FILE * pf)
3211 {
3212 fprintf(pf, "set eval sameasanalysis %s\n", fEvalSameAsAnalysis ? "on" : "off");
3213 SaveEvalSetupSettings(pf, "set evaluation chequerplay", &esEvalChequer);
3214 SaveEvalSetupSettings(pf, "set evaluation cubedecision", &esEvalCube);
3215 SaveMoveFilterSettings(pf, "set evaluation movefilter", aamfEval);
3216 fprintf(pf, "set cache %u\n", GetEvalCacheEntries());
3217 fprintf(pf, "set matchequitytable \"%s\"\n", miCurrent.szFileName);
3218 fprintf(pf, "set invert matchequitytable %s\n", fInvertMET ? "on" : "off");
3219 #if defined(USE_MULTITHREAD)
3220 fprintf(pf, "set threads %u\n", MT_GetNumThreads());
3221 #endif
3222 }
3223
3224 static void
SaveAutoSaveSettings(FILE * pf)3225 SaveAutoSaveSettings(FILE * pf)
3226 {
3227 fprintf(pf, "set autosave time %d\n", nAutoSaveTime);
3228 fprintf(pf, "set autosave rollout %s\n", fAutoSaveRollout ? "on" : "off");
3229 fprintf(pf, "set autosave analysis %s\n", fAutoSaveAnalysis ? "on" : "off");
3230 fprintf(pf, "set autosave confirm %s\n", fAutoSaveConfirmDelete ? "on" : "off");
3231 }
3232
3233 static void
SaveMiscSettings(FILE * pf)3234 SaveMiscSettings(FILE * pf)
3235 {
3236 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
3237 fprintf(pf, "set tutor mode %s\n", fTutor ? "on" : "off");
3238 fprintf(pf, "set tutor cube %s\n", fTutorCube ? "on" : "off");
3239 fprintf(pf, "set tutor chequer %s\n", fTutorChequer ? "on" : "off");
3240 fprintf(pf, "set tutor skill ");
3241 if (TutorSkill == SKILL_VERYBAD)
3242 fprintf(pf, "very bad\n");
3243 else if (TutorSkill == SKILL_BAD)
3244 fprintf(pf, "bad\n");
3245 else
3246 fprintf(pf, "doubtful\n");
3247
3248 fprintf(pf, "set clockwise %s\n", fClockwise ? "on" : "off");
3249 fprintf(pf, "set confirm new %s\n", fConfirmNew ? "on" : "off");
3250 fprintf(pf, "set confirm save %s\n", fConfirmSave ? "on" : "off");
3251 fprintf(pf, "set cube use %s\n", fCubeUse ? "on" : "off");
3252 fprintf(pf, "set display %s\n", fDisplay ? "on" : "off");
3253
3254 fprintf(pf, "set confirm default ");
3255 if (nConfirmDefault == 1)
3256 fprintf(pf, "yes\n");
3257 else if (nConfirmDefault == 0)
3258 fprintf(pf, "no\n");
3259 else
3260 fprintf(pf, "ask\n");
3261
3262 fprintf(pf, "set gotofirstgame %s\n", fGotoFirstGame ? "on" : "off");
3263 fprintf(pf, "set output matchpc %s\n", fOutputMatchPC ? "on" : "off");
3264 fprintf(pf, "set output mwc %s\n", fOutputMWC ? "on" : "off");
3265 fprintf(pf, "set output rawboard %s\n", fOutputRawboard ? "on" : "off");
3266 fprintf(pf, "set output winpc %s\n", fOutputWinPC ? "on" : "off");
3267 fprintf(pf, "set output digits %d\n", fOutputDigits);
3268 fprintf(pf, "set output errorratefactor %s\n",
3269 g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%f", rErrorRateFactor));
3270 fprintf(pf, "set prompt %s\n", szPrompt);
3271 fprintf(pf, "set browser \"%s\"\n", get_web_browser());
3272 fprintf(pf, "set priority nice %d\n", nThreadPriority);
3273 fprintf(pf, "set ratingoffset %s\n", g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%f", rRatingOffset));
3274 }
3275
3276 extern void
CommandSaveSettings(char * szParam)3277 CommandSaveSettings(char *szParam)
3278 {
3279 FILE *pf;
3280 char *szFile;
3281
3282 szParam = NextToken(&szParam);
3283
3284 /* If we were started without a configuration file, don't
3285 * save anything since this will overwrite an existing file
3286 * with defaults */
3287 if (fNoRC)
3288 return;
3289
3290 if (!szParam || !*szParam) {
3291 /* no filename parameter given -- save to default location */
3292 szFile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
3293 } else
3294 szFile = g_strdup(szParam);
3295
3296 if (!strcmp(szFile, "-"))
3297 pf = stdout;
3298 else
3299 pf = gnubg_g_fopen(szFile, "w");
3300
3301 if (!pf) {
3302 outputerr(szFile);
3303 g_free(szFile);
3304 return;
3305 }
3306 #if defined(USE_GTK)
3307 /* the following may set errno because of a gtk bug so we do it
3308 * first and reset errno afterwards */
3309 if (fX)
3310 RefreshGeometries();
3311 #endif
3312
3313 errno = 0;
3314
3315 fprintf(pf,
3316 "#\n"
3317 "# GNU Backgammon command file\n"
3318 "# generated by %s\n"
3319 "#\n"
3320 "# WARNING: The file 'gnubgautorc' is automatically "
3321 "generated the first time\n"
3322 "# settings are changed and will be overwritten with "
3323 "every subsequent change.\n"
3324 "# If you want to add startup commands manually, you should "
3325 "use 'gnubgrc'\n" "# instead.\n" "\n", VERSION_STRING);
3326
3327 /* language preference */
3328 fprintf(pf, "set lang %s\n", szLang);
3329
3330 #if defined(USE_GTK)
3331 SaveGUISettings(pf);
3332 #endif
3333 SaveAnalysisSettings(pf);
3334 SaveRenderingSettings(pf);
3335 SavePlayingSettings(pf);
3336 SaveRuleSettings(pf);
3337 SaveEvaluationSettings(pf);
3338 SavePlayerSettings(pf);
3339 SaveRNGSettings(pf, "set", rngCurrent, rngctxCurrent);
3340 SaveRolloutSettings(pf, "set rollout", &rcRollout);
3341 SaveImportExportSettings(pf);
3342 SaveSoundSettings(pf);
3343 RelationalSaveSettings(pf);
3344
3345 SaveMiscSettings(pf);
3346 SaveAutoSaveSettings(pf);
3347 if (pf != stdout)
3348 fclose(pf);
3349
3350 if (errno)
3351 outputerr(szFile);
3352 else {
3353 outputf(_("Settings saved to %s."), (!strcmp(szFile, "-")) ? _("standard output stream") : szFile);
3354 output("\n");
3355 if (cOutputPostponed) {
3356 outputresume();
3357 outputx();
3358 outputpostpone();
3359 }
3360 }
3361 g_free(szFile);
3362
3363 }
3364
3365 #if defined(HAVE_LIB_READLINE)
3366 static command *pcCompleteContext;
3367
3368 static char *
NullGenerator(const char * UNUSED (sz),int UNUSED (nState))3369 NullGenerator(const char *UNUSED(sz), int UNUSED(nState))
3370 {
3371 return NULL;
3372 }
3373
3374 static char *
GenerateKeywords(const char * sz,int nState)3375 GenerateKeywords(const char *sz, int nState)
3376 {
3377 static size_t cch;
3378 static command *pc;
3379 char *szDup;
3380
3381 if (!nState) {
3382 cch = strlen(sz);
3383 pc = pcCompleteContext;
3384 }
3385
3386 while (pc && pc->sz) {
3387 if (!StrNCaseCmp(sz, pc->sz, cch) && pc->szHelp) {
3388 if (!(szDup = malloc(strlen(pc->sz) + 1)))
3389 return NULL;
3390
3391 strcpy(szDup, pc->sz);
3392
3393 pc++;
3394
3395 return szDup;
3396 }
3397
3398 pc++;
3399 }
3400
3401 return NULL;
3402 }
3403
3404 static char *
ERCompletion(const char * sz,int nState)3405 ERCompletion(const char *sz, int nState)
3406 {
3407 static int i;
3408 static size_t cch;
3409 const char *pch;
3410 char *szDup;
3411
3412 if (!nState) {
3413 cch = strlen(sz);
3414 i = 0;
3415 }
3416
3417 while (i < 2) {
3418 pch = i++ ? "rollout" : "evaluation";
3419
3420 if (!StrNCaseCmp(sz, pch, cch)) {
3421 if (!(szDup = malloc(strlen(pch) + 1)))
3422 return NULL;
3423
3424 return strcpy(szDup, pch);
3425 }
3426 ++i;
3427 }
3428
3429 return NULL;
3430 }
3431
3432 static char *
OnOffCompletion(const char * sz,int nState)3433 OnOffCompletion(const char *sz, int nState)
3434 {
3435 static unsigned int i;
3436 static size_t cch;
3437 static const char *asz[] = { "false", "no", "off", "on", "true", "yes" };
3438 const char *pch;
3439 char *szDup;
3440
3441 if (!nState) {
3442 cch = strlen(sz);
3443 i = 0;
3444 }
3445
3446 while (i < sizeof(asz) / sizeof(asz[0])) {
3447 pch = asz[i++];
3448
3449 if (!StrNCaseCmp(sz, pch, cch)) {
3450 if (!(szDup = malloc(strlen(pch) + 1)))
3451 return NULL;
3452
3453 return strcpy(szDup, pch);
3454 }
3455 }
3456
3457 return NULL;
3458 }
3459
3460 static char *
PlayerCompletionGen(const char * sz,int nState,int fBoth)3461 PlayerCompletionGen(const char *sz, int nState, int fBoth)
3462 {
3463 static int i;
3464 static size_t cch;
3465 const char *pch;
3466 char *szDup;
3467
3468 if (!nState) {
3469 cch = strlen(sz);
3470 i = 0;
3471 }
3472
3473 while (i < (fBoth ? 5 : 4)) {
3474 switch (i) {
3475 case 0:
3476 pch = "0";
3477 break;
3478 case 1:
3479 pch = "1";
3480 break;
3481 case 2:
3482 pch = ap[0].szName;
3483 break;
3484 case 3:
3485 pch = ap[1].szName;
3486 break;
3487 case 4:
3488 pch = "both";
3489 break;
3490 default:
3491 abort();
3492 }
3493
3494 i++;
3495
3496 if (!StrNCaseCmp(sz, pch, cch)) {
3497 if (!(szDup = malloc(strlen(pch) + 1)))
3498 return NULL;
3499
3500 return strcpy(szDup, pch);
3501 }
3502 }
3503
3504 return NULL;
3505 }
3506
3507 static char *
PlayerCompletion(const char * sz,int nState)3508 PlayerCompletion(const char *sz, int nState)
3509 {
3510 return PlayerCompletionGen(sz, nState, FALSE);
3511 }
3512
3513 static char *
PlayerCompletionBoth(const char * sz,int nState)3514 PlayerCompletionBoth(const char *sz, int nState)
3515 {
3516 return PlayerCompletionGen(sz, nState, TRUE);
3517 }
3518
3519
3520 static command *
FindContext(command * pc,char * szOrig,int ich)3521 FindContext(command * pc, char *szOrig, int ich)
3522 {
3523 char *sz = (char *) g_alloca(strlen(szOrig) * sizeof(char) + 1);
3524 char *pch, *pchCurrent;
3525 command *pcResume = NULL;
3526
3527 pch = strcpy(sz, szOrig);
3528 pch[ich] = 0;
3529
3530 do {
3531 if (!(pchCurrent = NextToken(&pch)))
3532 /* no command */
3533 return pc;
3534
3535 if (!pch)
3536 /* incomplete command */
3537 return pc;
3538
3539 if (pcResume) {
3540 pc = pcResume;
3541 pcResume = NULL;
3542 continue;
3543 }
3544
3545 while (pc && pc->sz) {
3546 if (!StrNCaseCmp(pchCurrent, pc->sz, strlen(pchCurrent))) {
3547 pc = pc->pc;
3548
3549 if (pc == acSetPlayer || pc == acSetRolloutPlayer ||
3550 pc == acSetRolloutLatePlayer || pc == acSetAnalysisPlayer || pc == acSetCheatPlayer) {
3551 pcResume = pc;
3552 pc = &cPlayerBoth;
3553 }
3554
3555 break;
3556 }
3557
3558 pc++;
3559 }
3560 } while (pcResume || (pc && pc->sz));
3561
3562 if (pc && pc->pc) {
3563 /* dummy command for parameter completion */
3564 if (!NextToken(&pch))
3565 return pc;
3566 }
3567
3568 /* the command is already complete */
3569 return NULL;
3570 }
3571
3572 static char **
CompleteKeyword(const char * szText,int iStart,int UNUSED (iEnd))3573 CompleteKeyword(const char *szText, int iStart, int UNUSED(iEnd))
3574 {
3575
3576 if (fReadingOther)
3577 return rl_completion_matches(szText, NullGenerator);
3578
3579 pcCompleteContext = FindContext(acTop, rl_line_buffer, iStart);
3580
3581 if (!pcCompleteContext)
3582 return NULL;
3583
3584 if (pcCompleteContext == &cER)
3585 return rl_completion_matches(szText, ERCompletion);
3586 else if (pcCompleteContext == &cFilename) {
3587 rl_filename_completion_desired = TRUE;
3588 return rl_completion_matches(szText, rl_filename_completion_function);
3589 } else if (pcCompleteContext == &cOnOff)
3590 return rl_completion_matches(szText, OnOffCompletion);
3591 else if (pcCompleteContext == &cPlayer)
3592 return rl_completion_matches(szText, PlayerCompletion);
3593 else if (pcCompleteContext == &cPlayerBoth)
3594 return rl_completion_matches(szText, PlayerCompletionBoth);
3595 else
3596 return rl_completion_matches(szText, GenerateKeywords);
3597 }
3598 #endif
3599
3600 extern void
Prompt(void)3601 Prompt(void)
3602 {
3603
3604 #if defined(HAVE_LIB_READLINE)
3605 if (!fInteractive || !isatty(STDIN_FILENO))
3606 return;
3607 #endif
3608
3609 g_print("%s", FormatPrompt());
3610 fflush(stdout);
3611 }
3612
3613 #if defined(HAVE_LIB_READLINE)
3614 static char *
locale_from_utf8(const char * sz)3615 locale_from_utf8(const char *sz)
3616 {
3617 char *ret;
3618 GError *error = NULL;
3619 g_assert(sz);
3620 ret = g_locale_from_utf8(sz, strlen(sz), NULL, NULL, &error);
3621 if (error) {
3622 g_print("locale_from_utf8 failed: %s\n", error->message);
3623 g_error_free(error);
3624 ret = g_strdup(sz);
3625 }
3626 return ret;
3627 }
3628 #endif
3629
3630 static char *
locale_to_utf8(const char * sz)3631 locale_to_utf8(const char *sz)
3632 {
3633 char *ret;
3634 GError *error = NULL;
3635 g_assert(sz);
3636 ret = g_locale_to_utf8(sz, strlen(sz), NULL, NULL, &error);
3637 if (error) {
3638 g_print("locale_to_utf8 failed: %s\n", error->message);
3639 g_error_free(error);
3640 ret = g_strdup(sz);
3641 }
3642 return ret;
3643 }
3644
3645 #if defined(USE_GTK)
3646 #if defined(HAVE_LIB_READLINE)
3647 extern void
ProcessInput(char * sz)3648 ProcessInput(char *sz)
3649 {
3650
3651 char *pchExpanded;
3652
3653 rl_callback_handler_remove();
3654 fReadingCommand = FALSE;
3655
3656 if (!sz) {
3657 outputc('\n');
3658 PromptForExit();
3659 } else {
3660
3661 fInterrupt = FALSE;
3662
3663 /* expand history */
3664
3665 history_expand(sz, &pchExpanded);
3666
3667 if (*pchExpanded)
3668 add_history(pchExpanded);
3669
3670 if (fX)
3671 GTKDisallowStdin();
3672
3673 HandleCommand(pchExpanded, acTop);
3674 free(pchExpanded);
3675 }
3676
3677 if (fX)
3678 GTKAllowStdin();
3679
3680 ResetInterrupt();
3681
3682
3683 /* Recalculate prompt -- if we call nothing, then readline will
3684 * redisplay the old prompt. This isn't what we want: we either
3685 * want no prompt at all, yet (if NextTurn is going to be called),
3686 * or if we do want to prompt immediately, we recalculate it in
3687 * case the information in the old one is out of date. */
3688 if (nNextTurn)
3689 fNeedPrompt = TRUE;
3690 else {
3691 char *sz = locale_from_utf8(FormatPrompt());
3692 rl_callback_handler_install(sz, ProcessInput);
3693 g_free(sz);
3694 fReadingCommand = TRUE;
3695 }
3696 }
3697
3698 #endif
3699
3700 /* Handle a command as if it had been typed by the user. */
3701 extern void
UserCommand(const char * szCommand)3702 UserCommand(const char *szCommand)
3703 {
3704 char *sz;
3705
3706 g_return_if_fail(szCommand);
3707 g_return_if_fail(*szCommand);
3708 /* Unfortunately we need to copy the command, because it might be in
3709 * read-only storage and HandleCommand might want to modify it. */
3710 sz = g_strdup(szCommand);
3711 #ifndef WIN32
3712 if (!fTTY || !fInteractive)
3713 #endif
3714 {
3715 fInterrupt = FALSE;
3716 HandleCommand(sz, acTop);
3717 g_free(sz);
3718 ResetInterrupt();
3719 return;
3720 }
3721
3722 /* Note that the command is always echoed to stdout; the output*()
3723 * functions are bypassed. */
3724 #if defined(HAVE_LIB_READLINE)
3725 rl_end = 0; /* crashes without this line */
3726 rl_redisplay();
3727 g_print("%s\n", sz);
3728 ProcessInput(sz);
3729 g_free(sz);
3730 return;
3731 #elif !defined(WIN32)
3732 g_print("\n");
3733 Prompt();
3734 g_print("%s\n", sz);
3735 fInterrupt = FALSE;
3736 HandleCommand(sz, acTop);
3737 g_free(sz);
3738 ResetInterrupt();
3739 if (nNextTurn)
3740 Prompt();
3741 else
3742 fNeedPrompt = TRUE;
3743 return;
3744 #endif
3745 }
3746
3747 extern gint
NextTurnNotify(gpointer UNUSED (p))3748 NextTurnNotify(gpointer UNUSED(p))
3749 {
3750 NextTurn(TRUE);
3751
3752 ResetInterrupt();
3753
3754 if (fNeedPrompt) {
3755 #if defined(HAVE_LIB_READLINE)
3756 if (fInteractive) {
3757 char *sz = locale_from_utf8(FormatPrompt());
3758 rl_callback_handler_install(sz, ProcessInput);
3759 g_free(sz);
3760 fReadingCommand = TRUE;
3761 } else
3762 #endif
3763 Prompt();
3764
3765 fNeedPrompt = FALSE;
3766 }
3767
3768 return FALSE; /* remove idle handler, if GTK */
3769 }
3770 #endif
3771
3772 /* Read a line from stdin, and handle X and readline input if
3773 * appropriate. This function blocks until a line is ready, and does
3774 * not call HandleEvents(), and because fBusy will be set some X
3775 * commands will not work. Therefore, it should not be used for
3776 * reading top level commands. The line it returns has been allocated
3777 * with malloc (as with readline()). */
3778 extern char *
GetInput(char * szPrompt)3779 GetInput(char *szPrompt)
3780 {
3781
3782 char *sz;
3783 char *pch;
3784 char *pchConverted;
3785 #if defined(USE_GTK)
3786 g_assert(fTTY && !fX);
3787 #endif
3788
3789 #if defined(HAVE_LIB_READLINE)
3790 if (fInteractive) {
3791 char *prompt;
3792 /* Using readline, but not X. */
3793 if (fInterrupt)
3794 return NULL;
3795
3796 fReadingOther = TRUE;
3797
3798 prompt = locale_from_utf8(szPrompt);
3799 while (!(sz = readline(prompt))) {
3800 outputc('\n');
3801 PromptForExit();
3802 }
3803 g_free(prompt);
3804
3805 fReadingOther = FALSE;
3806
3807 if (fInterrupt)
3808 return NULL;
3809
3810 pchConverted = locale_to_utf8(sz);
3811 free(sz);
3812 return pchConverted;
3813 }
3814 #endif
3815 /* Not using readline or X. */
3816 if (fInterrupt)
3817 return NULL;
3818
3819 g_print("%s", szPrompt);
3820 fflush(stdout);
3821
3822 sz = malloc(256); /* FIXME it would be nice to handle longer strings */
3823
3824 clearerr(stdin);
3825 pch = fgets(sz, 256, stdin);
3826
3827 if (fInterrupt) {
3828 free(sz);
3829 return NULL;
3830 }
3831
3832 if (!pch) {
3833 if (!isatty(STDIN_FILENO))
3834 exit(EXIT_SUCCESS);
3835
3836 outputc('\n');
3837 PromptForExit();
3838 }
3839
3840 if ((pch = strchr(sz, '\n')))
3841 *pch = 0;
3842 if ((pch = strchr(sz, '\r')))
3843 *pch = 0;
3844
3845 pchConverted = locale_to_utf8(sz);
3846 free(sz);
3847 return pchConverted;
3848 }
3849
3850 /* Ask a yes/no question. Interrupting the question is considered a "no"
3851 * answer. */
3852 extern int
GetInputYN(char * szPrompt)3853 GetInputYN(char *szPrompt)
3854 {
3855
3856 char *pch;
3857
3858 if (nConfirmDefault != -1) {
3859 outputf("%s %s\n", szPrompt, nConfirmDefault ? _("yes") : _("no"));
3860 return nConfirmDefault;
3861 }
3862
3863 if (!fInteractive)
3864 return TRUE;
3865
3866 #if defined(USE_GTK)
3867 if (fX)
3868 return GTKGetInputYN(szPrompt);
3869 #endif
3870
3871 if (fInterrupt)
3872 return FALSE;
3873
3874 while ((pch = GetInput(szPrompt)) != 0) {
3875 if (pch)
3876 switch (*pch) {
3877 case 'y':
3878 case 'Y':
3879 g_free(pch);
3880 return TRUE;
3881 case 'n':
3882 case 'N':
3883 g_free(pch);
3884 return FALSE;
3885 default:
3886 g_free(pch);
3887 }
3888
3889 outputl(_("Please answer `y' or `n'."));
3890 }
3891 return FALSE;
3892 }
3893
3894 /* Like strncpy, except it does the right thing */
3895 extern char *
strcpyn(char * szDest,const char * szSrc,int cch)3896 strcpyn(char *szDest, const char *szSrc, int cch)
3897 {
3898
3899 char *pchDest = szDest;
3900 const char *pchSrc = szSrc;
3901
3902 if (cch-- < 1)
3903 return szDest;
3904
3905 while (cch--)
3906 if (!(*pchDest++ = *pchSrc++))
3907 return szDest;
3908
3909 *pchDest = 0;
3910
3911 return szDest;
3912 }
3913
3914 extern void
CommandSetOutputOutput(char * sz)3915 CommandSetOutputOutput(char *sz)
3916 {
3917 SetToggle("output", &foutput_on, sz, _("output will be shown"), _("output will not be shown"));
3918 return;
3919 }
3920
3921 static GTimeVal tvProgress;
3922
3923 static int
ProgressThrottle(void)3924 ProgressThrottle(void)
3925 {
3926 GTimeVal tv, tvDiff;
3927 g_get_current_time(&tv);
3928
3929 tvDiff.tv_sec = tv.tv_sec - tvProgress.tv_sec;
3930 if ((tvDiff.tv_usec = tv.tv_usec + 1000000 - tvProgress.tv_usec) >= 1000000)
3931 tvDiff.tv_usec -= 1000000;
3932 else
3933 tvDiff.tv_sec--;
3934
3935 if (tvDiff.tv_sec || tvDiff.tv_usec >= 100000) {
3936 /* sufficient time elapsed; record current time */
3937 tvProgress.tv_sec = tv.tv_sec;
3938 tvProgress.tv_usec = tv.tv_usec;
3939 return 0;
3940 }
3941
3942 /* insufficient time elapsed */
3943 return -1;
3944 }
3945
3946 extern void
ProgressStart(const char * sz)3947 ProgressStart(const char *sz)
3948 {
3949 if (!fShowProgress)
3950 return;
3951
3952 fInProgress = TRUE;
3953
3954 #if defined(USE_GTK)
3955 if (fX) {
3956 GTKProgressStart(sz);
3957 return;
3958 }
3959 #endif
3960
3961 if (sz) {
3962 fputs(sz, stdout);
3963 fflush(stdout);
3964 }
3965 }
3966
3967
3968 extern void
ProgressStartValue(char * sz,int iMax)3969 ProgressStartValue(char *sz, int iMax)
3970 {
3971
3972 if (!fShowProgress)
3973 return;
3974
3975 iProgressMax = iMax;
3976 iProgressValue = 0;
3977 pcProgress = sz;
3978
3979 fInProgress = TRUE;
3980
3981 #if defined(USE_GTK)
3982 if (fX) {
3983 GTKProgressStartValue(sz, iMax);
3984 return;
3985 }
3986 #endif
3987
3988 if (sz) {
3989 fputs(sz, stdout);
3990 fflush(stdout);
3991 }
3992
3993 }
3994
3995
3996 extern void
ProgressValue(int iValue)3997 ProgressValue(int iValue)
3998 {
3999
4000 if (!fShowProgress || iProgressValue == iValue)
4001 return;
4002
4003 iProgressValue = iValue;
4004
4005 if (ProgressThrottle())
4006 return;
4007 #if defined(USE_GTK)
4008 if (fX) {
4009 GTKProgressValue(iValue, iProgressMax);
4010 return;
4011 }
4012 #endif
4013
4014 outputf("\r%s %d/%d\r", pcProgress, iProgressValue, iProgressMax);
4015 fflush(stdout);
4016
4017 }
4018
4019
4020 extern void
ProgressValueAdd(int iValue)4021 ProgressValueAdd(int iValue)
4022 {
4023
4024 ProgressValue(iProgressValue + iValue);
4025
4026 }
4027
4028
4029 static gboolean
Progress(gpointer UNUSED (unused))4030 Progress(gpointer UNUSED(unused))
4031 {
4032 static int i = 0;
4033 static char ach[5] = "/-\\|";
4034
4035 if (!fShowProgress)
4036 return FALSE;
4037
4038 if (ProgressThrottle())
4039 return TRUE;
4040 #if defined(USE_GTK)
4041 if (fX) {
4042 GTKProgress();
4043 return TRUE;
4044 }
4045 #endif
4046
4047 putchar(ach[i++]);
4048 i &= 0x03;
4049 putchar('\b');
4050 fflush(stdout);
4051 return TRUE;
4052 }
4053
4054 #if !defined(USE_MULTITHREAD)
4055 extern void
CallbackProgress(void)4056 CallbackProgress(void)
4057 {
4058 #if defined(USE_GTK)
4059 if (fX) {
4060 GTKDisallowStdin();
4061 if (fInProgress)
4062 GTKSuspendInput();
4063
4064 while (gtk_events_pending())
4065 gtk_main_iteration();
4066
4067 if (fInProgress)
4068 GTKResumeInput();
4069 GTKAllowStdin();
4070 }
4071 #endif
4072
4073 if (fInProgress && !iProgressMax)
4074 Progress(NULL);
4075 }
4076 #endif
4077
4078 extern void
ProgressEnd(void)4079 ProgressEnd(void)
4080 {
4081
4082 int i;
4083
4084 if (!fShowProgress)
4085 return;
4086
4087 fInProgress = FALSE;
4088 iProgressMax = 0;
4089 iProgressValue = 0;
4090 pcProgress = NULL;
4091
4092 #if defined(USE_GTK)
4093 if (fX) {
4094 GTKProgressEnd();
4095 return;
4096 }
4097 #endif
4098
4099 putchar('\r');
4100 for (i = 0; i < 79; i++)
4101 putchar(' ');
4102 putchar('\r');
4103 fflush(stdout);
4104
4105 }
4106
4107 static void
move_rc_files(void)4108 move_rc_files(void)
4109 {
4110 /* Move files to the new location. Remove this part when all users have had
4111 * their files moved.*/
4112 char *olddir, *oldfile, *newfile;
4113 #ifdef WIN32
4114 olddir = g_strdup(getDataDir());
4115 #else
4116 olddir = g_build_filename(szHomeDirectory, "..", NULL);
4117 #endif
4118
4119 newfile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
4120 oldfile = g_build_filename(olddir, ".gnubgautorc", NULL);
4121 if (g_file_test(oldfile, G_FILE_TEST_IS_REGULAR) && !g_file_test(newfile, G_FILE_TEST_EXISTS))
4122 g_rename(oldfile, newfile);
4123 g_free(oldfile);
4124 g_free(newfile);
4125
4126 newfile = g_build_filename(szHomeDirectory, "gnubgrc", NULL);
4127 oldfile = g_build_filename(olddir, ".gnubgrc", NULL);
4128 if (g_file_test(oldfile, G_FILE_TEST_IS_REGULAR) && !g_file_test(newfile, G_FILE_TEST_EXISTS))
4129 g_rename(oldfile, newfile);
4130 g_free(oldfile);
4131 g_free(newfile);
4132
4133 g_free(olddir);
4134
4135 }
4136
4137 /*
4138 * Create $HOME/.gnubg if not existing
4139 *
4140 */
4141
4142 static int
CreateGnubgDirectory(void)4143 CreateGnubgDirectory(void)
4144 {
4145 char *newfile;
4146
4147 if (!g_file_test(szHomeDirectory, G_FILE_TEST_IS_DIR)) {
4148 if (g_mkdir(szHomeDirectory, 0700) < 0) {
4149 outputerr(szHomeDirectory);
4150 return -1;
4151 }
4152 }
4153 newfile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
4154 if (!g_file_test(newfile, G_FILE_TEST_IS_REGULAR))
4155 move_rc_files();
4156 g_free(newfile);
4157 return 0;
4158 }
4159
4160
4161 extern void
HandleInterrupt(int UNUSED (idSignal))4162 HandleInterrupt(int UNUSED(idSignal))
4163 {
4164 /* NB: It is safe to write to fInterrupt even if it cannot be read
4165 * atomically, because it is only used to hold a binary value. */
4166 fInterrupt = TRUE;
4167 }
4168
4169 static void
BearoffProgress(unsigned int i)4170 BearoffProgress(unsigned int i)
4171 {
4172 #if defined(USE_GTK)
4173 if (fX) {
4174 GTKBearoffProgress(i);
4175 return;
4176 }
4177 #endif
4178 putchar("\\|/-"[(i / 1000) % 4]);
4179 putchar('\r');
4180 fflush(stdout);
4181 }
4182
4183 static void
VersionMessage(void)4184 VersionMessage(void)
4185 {
4186 g_print("%s\n%s\n", _(VERSION_STRING), _(aszCOPYRIGHT));
4187 g_print("%s", _(intro_string));
4188 }
4189
4190 #if defined(HAVE_LIB_READLINE)
4191 static char *
get_readline(void)4192 get_readline(void)
4193 {
4194 char *pchExpanded;
4195 char *szInput;
4196 char *sz;
4197 char *prompt = locale_from_utf8(FormatPrompt());
4198 while (!(szInput = readline(prompt))) {
4199 outputc('\n');
4200 PromptForExit();
4201 }
4202 g_free(prompt);
4203 sz = locale_to_utf8(szInput);
4204 free(szInput);
4205 fInterrupt = FALSE;
4206 history_expand(sz, &pchExpanded);
4207 g_free(sz);
4208 if (*pchExpanded)
4209 add_history(pchExpanded);
4210 return pchExpanded;
4211 }
4212 #endif
4213
4214 static char *
get_stdin_line(void)4215 get_stdin_line(void)
4216 {
4217 char sz[2048], *pch;
4218
4219 sz[0] = 0;
4220 Prompt();
4221
4222 clearerr(stdin);
4223
4224 /* FIXME shouldn't restart sys calls on signals during this
4225 * fgets */
4226 if (fgets(sz, sizeof(sz), stdin) == NULL) {
4227 if (ferror(stdin)) {
4228 outputerr("stdin");
4229 exit(EXIT_FAILURE);
4230 } else {
4231 Shutdown();
4232 exit(EXIT_SUCCESS);
4233 }
4234 }
4235
4236 if ((pch = strchr(sz, '\n')))
4237 *pch = 0;
4238
4239
4240 if (feof(stdin)) {
4241 if (!isatty(STDIN_FILENO)) {
4242 Shutdown();
4243 exit(EXIT_SUCCESS);
4244 }
4245
4246 outputc('\n');
4247
4248 if (!sz[0])
4249 PromptForExit();
4250 return NULL;
4251 }
4252
4253 fInterrupt = FALSE;
4254 return strdup(sz);
4255 }
4256
4257
4258
4259 static void
run_cl(void)4260 run_cl(void)
4261 {
4262 char *line;
4263 for (;;) {
4264 #if defined(HAVE_LIB_READLINE)
4265 if (fInteractive) {
4266 line = get_readline();
4267 HandleCommand(line, acTop);
4268 free(line);
4269 } else
4270 #endif
4271 {
4272 line = get_stdin_line();
4273 HandleCommand(line, acTop);
4274 free(line);
4275 }
4276 while (fNextTurn)
4277 NextTurn(TRUE);
4278 ResetInterrupt();
4279 }
4280 }
4281
4282 static void
init_language(char ** lang)4283 init_language(char **lang)
4284 {
4285 char *szFile, szTemp[4096];
4286 char *pch;
4287 FILE *pf;
4288
4289 outputoff();
4290 szFile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
4291
4292 if (!*lang && (pf = gnubg_g_fopen(szFile, "r"))) {
4293
4294 while (fgets(szTemp, sizeof(szTemp), pf) != NULL) {
4295 if ((pch = strchr(szTemp, '\n')))
4296 *pch = 0;
4297 if ((pch = strchr(szTemp, '\r')))
4298 *pch = 0;
4299
4300 if (!strncmp("set lang", szTemp, 8)) {
4301 *lang = g_strdup(szTemp + 9);
4302 break;
4303 }
4304 }
4305 if (ferror(pf))
4306 outputerr(szFile);
4307
4308 fclose(pf);
4309 }
4310 g_free(szFile);
4311 CommandSetLang(*lang);
4312 g_free(*lang);
4313 outputon();
4314 }
4315
4316 static void
setup_readline(void)4317 setup_readline(void)
4318 {
4319 #if defined(HAVE_LIB_READLINE)
4320 char *pch;
4321 int i;
4322 gnubg_histfile = g_build_filename(szHomeDirectory, "history", NULL);
4323 rl_readline_name = "gnubg";
4324 rl_basic_word_break_characters = rl_filename_quote_characters = szCommandSeparators;
4325 rl_completer_quote_characters = "\"";
4326 /* assume readline 4.2 or later */
4327 rl_completion_entry_function = NullGenerator;
4328 rl_attempted_completion_function = CompleteKeyword;
4329 /* setup history */
4330 read_history(gnubg_histfile);
4331 using_history();
4332 if ((pch = getenv("HISTSIZE")) && ((i = atoi(pch)) > 0))
4333 stifle_history(i);
4334 #endif
4335 }
4336
4337 #if !defined(USE_GTK)
4338 static void
PushSplash(char * UNUSED (unused),char * UNUSED (heading),char * UNUSED (message))4339 PushSplash(char *UNUSED(unused), char *UNUSED(heading), char *UNUSED(message))
4340 {
4341 }
4342 #endif
4343
4344 static void
init_nets(int fNoBearoff)4345 init_nets(int fNoBearoff)
4346 {
4347 char *gnubg_weights = BuildFilename("gnubg.weights");
4348 char *gnubg_weights_binary = BuildFilename("gnubg.wd");
4349 EvalInitialise(gnubg_weights, gnubg_weights_binary, fNoBearoff, fShowProgress ? BearoffProgress : NULL);
4350 g_free(gnubg_weights);
4351 g_free(gnubg_weights_binary);
4352 }
4353
4354 extern int
GetManualDice(unsigned int anDice[2])4355 GetManualDice(unsigned int anDice[2])
4356 {
4357
4358 char *pz;
4359 char *sz = NULL;
4360 int i;
4361
4362 #if defined(USE_GTK)
4363 if (fX) {
4364 if (GTKGetManualDice(anDice)) {
4365 fInterrupt = 1;
4366 return -1;
4367 } else
4368 return 0;
4369 }
4370 #endif
4371
4372 for (;;) {
4373 TryAgain:
4374 if (fInterrupt) {
4375 anDice[0] = anDice[1] = 0;
4376 return -1;
4377 }
4378
4379 sz = GetInput(_("Enter dice: "));
4380
4381 if (fInterrupt) {
4382 g_free(sz);
4383 anDice[0] = anDice[1] = 0;
4384 return -1;
4385 }
4386
4387 /* parse input and read a couple of dice */
4388 /* any string with two numbers is allowed */
4389
4390 pz = sz;
4391
4392 for (i = 0; i < 2; i++) {
4393 while (*pz && ((*pz < '1') || (*pz > '6')))
4394 pz++;
4395
4396 if (!*pz) {
4397 outputl(_("You must enter two numbers between 1 and 6."));
4398 goto TryAgain;
4399 }
4400
4401 anDice[i] = (int) (*pz - '0');
4402 pz++;
4403 }
4404
4405 g_free(sz);
4406 return 0;
4407 }
4408 }
4409
4410 static void
init_rng(void)4411 init_rng(void)
4412 {
4413 if (!(rngctxCurrent = InitRNG(NULL, NULL, TRUE, rngCurrent))) {
4414 printf("%s\n", _("Failure setting up RNG"));
4415 exit(EXIT_FAILURE);
4416 }
4417 if (!(rngctxRollout = InitRNG(&rcRollout.nSeed, NULL, TRUE, rcRollout.rngRollout))) {
4418 printf("%s\n", _("Failure setting up RNG for rollout."));
4419 exit(EXIT_FAILURE);
4420 }
4421
4422 /* we don't want rollouts to use the same seed as normal dice (which
4423 * could happen if InitRNG had to use the current time as a seed) -- mix
4424 * it up a little bit */
4425 rcRollout.nSeed ^= 0x792A584B;
4426 }
4427
4428 #if defined(WIN32) && defined(HAVE_SOCKETS)
4429 static void
init_winsock()4430 init_winsock()
4431 {
4432 short wVersionRequested;
4433 WSADATA wsaData;
4434 wVersionRequested = MAKEWORD(1, 1);
4435 if (WSAStartup(wVersionRequested, &wsaData) != 0) {
4436 outputerr("Windows sockets initialisation");
4437 }
4438 }
4439 #endif
4440
4441 static char *
matchfile_from_argv(char * sz)4442 matchfile_from_argv(char *sz)
4443 {
4444 char *pchMatch;
4445 #ifdef WIN32
4446 gsize br, bw;
4447 gchar *utf8fn;
4448
4449 if (g_path_is_absolute(sz))
4450 pchMatch = g_strdup_printf("\"%s\"", sz);
4451 else {
4452 char *tmp = g_build_filename(g_get_current_dir(), sz, NULL);
4453 pchMatch = g_strdup_printf("\"%s\"", tmp);
4454 g_free(tmp);
4455 }
4456 utf8fn = g_locale_to_utf8(pchMatch, -1, &br, &bw, NULL);
4457 g_free(pchMatch);
4458 pchMatch = utf8fn;
4459 #else
4460 pchMatch = g_strdup_printf("\"%s\"", sz);
4461 #endif
4462 return pchMatch;
4463 }
4464
4465 static void
init_defaults(void)4466 init_defaults(void)
4467 {
4468 /* init some html export options */
4469
4470 exsExport.szHTMLPictureURL = g_strdup("html-images/");
4471 exsExport.szHTMLExtension = g_strdup("png");
4472
4473 SetMatchDate(&mi);
4474
4475 strcpy(ap[1].szName, g_get_user_name());
4476 strcpy(default_names[1], g_get_user_name());
4477
4478 ListCreate(&lMatch);
4479 IniStatcontext(&scMatch);
4480
4481 szHomeDirectory = g_build_filename(g_get_home_dir(), ".gnubg", NULL);
4482
4483 }
4484
4485 static void
init_autosave(void)4486 init_autosave(void)
4487 {
4488 char *backupdir;
4489 backupdir = g_build_filename(szHomeDirectory, "backup", NULL);
4490 g_mkdir(backupdir, 0700);
4491 g_free(backupdir);
4492 }
4493
4494 static void
null_debug(const gchar * UNUSED (dom),GLogLevelFlags UNUSED (logflags),const gchar * UNUSED (message),gpointer UNUSED (unused))4495 null_debug(const gchar * UNUSED(dom), GLogLevelFlags UNUSED(logflags), const gchar * UNUSED(message),
4496 gpointer UNUSED(unused))
4497 {
4498 }
4499
4500 int
main(int argc,char * argv[])4501 main(int argc, char *argv[])
4502 {
4503 #if defined(USE_GTK)
4504 GtkWidget *pwSplash = NULL;
4505 #else
4506 char *pwSplash = NULL;
4507 #endif
4508 char *pchMatch = NULL;
4509 char *met = NULL;
4510
4511 static char *pchCommands = NULL, *pchPythonScript = NULL, *lang = NULL;
4512 static int fNoBearoff = FALSE, fNoX = FALSE, fSplash = FALSE, fNoTTY = FALSE, show_version = FALSE, debug = FALSE;
4513 GOptionEntry ao[] = {
4514 {"no-bearoff", 'b', 0, G_OPTION_ARG_NONE, &fNoBearoff,
4515 N_("Do not use bearoff database"), NULL},
4516 {"commands", 'c', 0, G_OPTION_ARG_FILENAME, &pchCommands,
4517 N_("Evaluate commands in FILE and exit"), "FILE"},
4518 {"lang", 'l', 0, G_OPTION_ARG_STRING, &lang,
4519 N_("Set language to LANG"), "LANG"},
4520 {"python", 'p', 0, G_OPTION_ARG_FILENAME, &pchPythonScript,
4521 N_("Evaluate Python code in FILE and exit"), "FILE"},
4522 {"quiet", 'q', 0, G_OPTION_ARG_NONE, &fQuiet,
4523 N_("Disable sound effects"), NULL},
4524 {"no-rc", 'r', 0, G_OPTION_ARG_NONE, &fNoRC,
4525 N_("Do not read .gnubgrc and .gnubgautorc commands"),
4526 NULL},
4527 {"splash", 'S', 0, G_OPTION_ARG_NONE, &fSplash,
4528 N_("Show gtk splash screen"), NULL},
4529 {"tty", 't', 0, G_OPTION_ARG_NONE, &fNoX,
4530 N_("Start the command-line instead of using the graphical interface"), NULL},
4531 {"version", 'v', 0, G_OPTION_ARG_NONE, &show_version,
4532 N_("Show version information and exit"), NULL},
4533 {"window-system-only", 'w', 0, G_OPTION_ARG_NONE, &fNoTTY,
4534 N_("Ignore tty input when using the graphical interface"), NULL},
4535 {"debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
4536 N_("Turn on debug"), NULL},
4537 {"datadir", 'D', 0, G_OPTION_ARG_STRING, &datadir,
4538 N_("Specify location of general data"), NULL},
4539 {"pkgdatadir", 'P', 0, G_OPTION_ARG_STRING, &pkg_datadir,
4540 N_("Specify location of program specific data"), NULL},
4541 {"docdir", 'O', 0, G_OPTION_ARG_STRING, &docdir,
4542 N_("Specify location of program documentation"), NULL},
4543 {"prefsdir", 's', 0, G_OPTION_ARG_STRING, &prefsdir,
4544 N_("Specify location of user's preferences directory"), NULL},
4545 {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
4546 };
4547 GError *error = NULL;
4548 GOptionContext *context;
4549
4550 #if ! GLIB_CHECK_VERSION(2,36,0)
4551 g_type_init();
4552 #endif
4553
4554 #if defined(LIBCURL_PROTOCOL_HTTPS)
4555 curl_global_init(CURL_GLOBAL_ALL);
4556 #endif
4557
4558 default_import_folder = g_strdup(".");
4559 default_export_folder = g_strdup(".");
4560 default_sgf_folder = g_strdup(".");
4561
4562 output_initialize();
4563
4564 /* set language */
4565 init_defaults();
4566 #if defined(USE_GTK)
4567 gtk_disable_setlocale();
4568 #endif
4569 init_language(&lang);
4570 bindtextdomain(PACKAGE, LOCALEDIR);
4571 textdomain(PACKAGE);
4572 bind_textdomain_codeset(PACKAGE, GNUBG_CHARSET);
4573
4574 /* parse command-line options */
4575 context = g_option_context_new("[file.sgf]");
4576 g_option_context_add_main_entries(context, ao, PACKAGE);
4577 #if defined(USE_GTK)
4578 g_option_context_add_group(context, gtk_get_option_group(FALSE));
4579 #endif
4580
4581 g_option_context_parse(context, &argc, &argv, &error);
4582 g_option_context_free(context);
4583 if (error) {
4584 outputerrf("%s\n", error->message);
4585 exit(EXIT_FAILURE);
4586 }
4587 if (argc > 1 && *argv[1])
4588 pchMatch = matchfile_from_argv(argv[1]);
4589
4590 if (!debug)
4591 g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, &null_debug, NULL);
4592
4593 if (prefsdir) {
4594 szHomeDirectory = prefsdir;
4595 }
4596 #ifdef WIN32
4597 /* data directory: initialise to the path where gnubg is installed */
4598 {
4599 const char *szDataDirectory = getDataDir();
4600 _chdir(szDataDirectory);
4601 }
4602 /* Create a mutex so install can check if it's running */
4603 CreateMutex(NULL, FALSE, "GNU Backgammon Mutex");
4604
4605 #endif
4606
4607 /* print version and exit if -v option given */
4608 VersionMessage();
4609 if (show_version)
4610 exit(EXIT_SUCCESS);
4611
4612 if (CreateGnubgDirectory())
4613 exit(EXIT_FAILURE);
4614
4615 init_autosave();
4616
4617 RenderInitialise();
4618
4619 #ifdef WIN32
4620 fNoTTY = TRUE;
4621 #endif
4622 #if defined(USE_GTK)
4623 /* -t option not given */
4624 if (!fNoX)
4625 InitGTK(&argc, &argv);
4626 if (fX) {
4627 fTTY = !fNoTTY && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO);
4628 fInteractive = fShowProgress = TRUE;
4629 if (fSplash)
4630 pwSplash = CreateSplash();
4631 } else
4632 #endif
4633 {
4634 fInteractive = isatty(STDIN_FILENO);
4635 fShowProgress = isatty(STDOUT_FILENO);
4636 }
4637
4638 if (fInteractive) {
4639 PortableSignal(SIGINT, HandleInterrupt, &shInterruptOld, FALSE);
4640 setup_readline();
4641 }
4642
4643 PushSplash(pwSplash, _("Initialising"), _("Random number generator"));
4644 init_rng();
4645
4646 PushSplash(pwSplash, _("Initialising"), _("match equity table"));
4647 met = BuildFilename2("met", "Kazaross-XG2.xml");
4648 InitMatchEquity(met);
4649 g_free(met);
4650
4651 PushSplash(pwSplash, _("Initialising"), _("neural nets"));
4652 init_nets(fNoBearoff);
4653
4654 PushSplash(pwSplash, _("Initialising"), _("initialising thread data"));
4655 glib_ext_init();
4656 MT_InitThreads();
4657
4658 #if defined(WIN32) && defined(HAVE_SOCKETS)
4659 PushSplash(pwSplash, _("Initialising"), _("Windows sockets"));
4660 init_winsock();
4661 #endif
4662
4663 #if defined(USE_PYTHON)
4664 PushSplash(pwSplash, _("Initialising"), "Python");
4665 PythonInitialise(argv[0]);
4666 #endif
4667
4668 SetExitSoundOff();
4669
4670 /* -r option given */
4671 if (!fNoRC) {
4672 PushSplash(pwSplash, _("Loading"), _("User Settings"));
4673 LoadRCFiles();
4674 }
4675
4676 strcpy(ap[0].szName, default_names[0]);
4677 strcpy(ap[1].szName, default_names[1]);
4678
4679 fflush(stdout);
4680 fflush(stderr);
4681
4682 #if defined(USE_MULTITHREAD)
4683 /* Make sure threads started */
4684 MT_StartThreads();
4685 #endif
4686
4687 /* start-up sound */
4688 playSound(SOUND_START);
4689
4690 #if defined(USE_GTK)
4691 if (fX) {
4692 if (!fTTY) {
4693 g_set_print_handler(>KOutput);
4694 g_set_printerr_handler(>KOutputErr);
4695 }
4696 RunGTK(pwSplash, pchCommands, pchPythonScript, pchMatch);
4697 Shutdown();
4698 exit(EXIT_SUCCESS);
4699 }
4700 #endif
4701
4702 if (pchMatch)
4703 CommandImportAuto(pchMatch);
4704
4705 /* -c option given */
4706 if (pchCommands) {
4707 fInteractive = FALSE;
4708 CommandLoadCommands(pchCommands);
4709 Shutdown();
4710 exit(EXIT_SUCCESS);
4711 }
4712
4713 /* -p option given */
4714 if (pchPythonScript) {
4715 #if defined(USE_PYTHON)
4716 fInteractive = FALSE;
4717 LoadPythonFile(pchPythonScript, FALSE);
4718 Shutdown();
4719 exit(EXIT_SUCCESS);
4720 #else
4721 outputerrf(_("GNU Backgammon build without Python."));
4722 exit(EXIT_FAILURE);
4723 #endif
4724 }
4725 run_cl();
4726 return (EXIT_FAILURE);
4727 }
4728
4729
4730 /*
4731 * Command: convert normalised money equity to match winning chance.
4732 *
4733 * The result is written to stdout.
4734 * FIXME: implement GTK version.
4735 * FIXME: allow more parameters (match score, match length)
4736 *
4737 * Input:
4738 * sz: string with equity
4739 *
4740 * Output:
4741 * none.
4742 *
4743 */
4744
4745 extern void
CommandEq2MWC(char * sz)4746 CommandEq2MWC(char *sz)
4747 {
4748
4749 float rEq;
4750 cubeinfo ci;
4751
4752 if (!ms.nMatchTo) {
4753 outputl(_("Command only valid in match play"));
4754 return;
4755 }
4756
4757
4758 rEq = ParseReal(&sz);
4759
4760 if (rEq == ERR_VAL)
4761 rEq = 0.0;
4762
4763 GetMatchStateCubeInfo(&ci, &ms);
4764
4765 outputf("%s = %+6.3f: %6.2f%%\n", _("MWC for equity"), -1.0, 100.0 * eq2mwc(-1.0, &ci));
4766 outputf("%s = %+6.3f: %6.2f%%\n", _("MWC for equity"), +1.0, 100.0 * eq2mwc(+1.0, &ci));
4767 outputf("%s:\n", _("By linear interpolation"));
4768 outputf("%s = %+6.3f: %6.2f%%\n", _("MWC for equity"), rEq, 100.0 * eq2mwc(rEq, &ci));
4769
4770 }
4771
4772
4773 /*
4774 * Command: convert match winning chance to normalised money equity.
4775 *
4776 * The result is written to stdout.
4777 * FIXME: implement GTK version.
4778 * FIXME: allow more parameters (match score, match length)
4779 *
4780 * Input:
4781 * sz: string with match winning chance
4782 *
4783 * Output:
4784 * none.
4785 *
4786 */
4787
4788 extern void
CommandMWC2Eq(char * sz)4789 CommandMWC2Eq(char *sz)
4790 {
4791
4792 float rMwc;
4793 cubeinfo ci;
4794
4795 if (!ms.nMatchTo) {
4796 outputl(_("Command only valid in match play"));
4797 return;
4798 }
4799
4800 GetMatchStateCubeInfo(&ci, &ms);
4801
4802 rMwc = ParseReal(&sz);
4803
4804 if (rMwc == ERR_VAL)
4805 rMwc = eq2mwc(0.0, &ci);
4806
4807 if (rMwc > 1.0f)
4808 rMwc /= 100.0f;
4809
4810 outputf("%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"), 100.0 * eq2mwc(-1.0, &ci), -1.0);
4811 outputf("%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"), 100.0 * eq2mwc(+1.0, &ci), +1.0);
4812 outputf("%s:\n", _("By linear interpolation"));
4813 outputf("%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"), 100.0 * rMwc, mwc2eq(rMwc, &ci));
4814
4815
4816 }
4817
4818 static void
swapGame(listOLD * plGame)4819 swapGame(listOLD * plGame)
4820 {
4821
4822 listOLD *pl;
4823 moverecord *pmr;
4824 int n;
4825 TanBoard anBoard;
4826
4827 for (pl = plGame->plNext; pl != plGame; pl = pl->plNext) {
4828
4829 pmr = pl->p;
4830
4831 switch (pmr->mt) {
4832 case MOVE_GAMEINFO:
4833
4834 /* swap score */
4835
4836 n = pmr->g.anScore[0];
4837 pmr->g.anScore[0] = pmr->g.anScore[1];
4838 pmr->g.anScore[1] = n;
4839
4840 /* swap winner */
4841
4842 if (pmr->g.fWinner > -1)
4843 pmr->g.fWinner = !pmr->g.fWinner;
4844
4845 /* swap statcontext */
4846
4847 /* recalculate statcontext later ... */
4848
4849 break;
4850
4851 case MOVE_DOUBLE:
4852 case MOVE_TAKE:
4853 case MOVE_DROP:
4854 case MOVE_NORMAL:
4855 case MOVE_RESIGN:
4856 case MOVE_SETDICE:
4857 case MOVE_SETCUBEVAL:
4858
4859 pmr->fPlayer = !pmr->fPlayer;
4860 break;
4861
4862 case MOVE_SETBOARD:
4863
4864 PositionFromKey(anBoard, &pmr->sb.key);
4865 SwapSides(anBoard);
4866 PositionKey((ConstTanBoard) anBoard, &pmr->sb.key);
4867 pmr->fPlayer = !pmr->fPlayer;
4868 break;
4869
4870 case MOVE_SETCUBEPOS:
4871
4872 if (pmr->scp.fCubeOwner > -1)
4873 pmr->scp.fCubeOwner = !pmr->scp.fCubeOwner;
4874 pmr->fPlayer = !pmr->fPlayer;
4875 break;
4876
4877 }
4878
4879 }
4880
4881 }
4882
4883
4884
4885 extern void
CommandSwapPlayers(char * UNUSED (sz))4886 CommandSwapPlayers(char *UNUSED(sz))
4887 {
4888 listOLD *pl;
4889 char *pc;
4890 int n;
4891
4892 /* swap individual move records */
4893
4894 for (pl = lMatch.plNext; pl != &lMatch; pl = pl->plNext) {
4895
4896 swapGame(pl->p);
4897
4898 }
4899
4900 /* swap player names */
4901
4902 pc = g_strdup(ap[0].szName);
4903 strcpy(ap[0].szName, ap[1].szName);
4904 strcpy(ap[1].szName, pc);
4905 g_free(pc);
4906
4907 /* swap player ratings */
4908
4909 pc = mi.pchRating[0];
4910 mi.pchRating[0] = mi.pchRating[1];
4911 mi.pchRating[1] = pc;
4912
4913 /* swap current matchstate */
4914
4915 if (ms.fTurn > -1)
4916 ms.fTurn = !ms.fTurn;
4917 if (ms.fMove > -1)
4918 ms.fMove = !ms.fMove;
4919 if (ms.fCubeOwner > -1)
4920 ms.fCubeOwner = !ms.fCubeOwner;
4921 n = ms.anScore[0];
4922 ms.anScore[1] = ms.anScore[0];
4923 ms.anScore[0] = n;
4924 SwapSides(ms.anBoard);
4925
4926 #if defined(USE_GTK)
4927 if (fX) {
4928 fJustSwappedPlayers = TRUE;
4929 GTKSet(ap);
4930 /* GTKSet(ap) already does this. This just adds flicker.
4931 * GTKRegenerateGames();
4932 */
4933 }
4934 #endif
4935
4936 ChangeGame(NULL);
4937
4938 ShowBoard();
4939 }
4940
4941
4942 extern int
confirmOverwrite(const char * sz,const int f)4943 confirmOverwrite(const char *sz, const int f)
4944 {
4945
4946 char *szPrompt;
4947 int i;
4948
4949 /* check for existing file */
4950
4951 if (f && !access(sz, F_OK)) {
4952
4953 szPrompt = (char *) malloc(50 + strlen(sz));
4954 sprintf(szPrompt, _("File \"%s\" exists. Overwrite? "), sz);
4955 i = GetInputYN(szPrompt);
4956 free(szPrompt);
4957 return i;
4958
4959 } else
4960 return TRUE;
4961
4962
4963 }
4964
4965 extern void
setDefaultFileName(char * path)4966 setDefaultFileName(char *path)
4967 {
4968 g_free(szCurrentFolder);
4969 g_free(szCurrentFileName);
4970 DisectPath(path, NULL, &szCurrentFileName, &szCurrentFolder);
4971 #if defined(USE_GTK)
4972 if (fX) {
4973 gchar *title = g_strdup_printf("%s (%s)", _("GNU Backgammon"), szCurrentFileName);
4974 gtk_window_set_title(GTK_WINDOW(pwMain), title);
4975 g_free(title);
4976 }
4977 #endif
4978 }
4979
4980 extern void
DisectPath(const char * path,const char * extension,char ** name,char ** folder)4981 DisectPath(const char *path, const char *extension, char **name, char **folder)
4982 {
4983 char *fnn, *pc;
4984 if (!path) {
4985 *folder = NULL;
4986 *name = NULL;
4987 return;
4988 }
4989 *folder = g_path_get_dirname(path);
4990 fnn = g_path_get_basename(path);
4991 pc = strrchr(fnn, '.');
4992 if (pc)
4993 *pc = '\0';
4994 *name = g_strconcat(fnn, extension, NULL);
4995 g_free(fnn);
4996 }
4997
4998
4999 /* ask for confirmation if this is a sub-optimal play
5000 * returns TRUE if player wants to re-think the move
5001 */
5002
5003 static int
GetAdviceAnswer(char * sz)5004 GetAdviceAnswer(char *sz)
5005 {
5006
5007 char *pch;
5008 #if defined(USE_GTK)
5009 if (fX)
5010 return GtkTutor(sz);
5011 #endif
5012
5013 if (fInterrupt)
5014 return FALSE;
5015
5016 while ((pch = GetInput(sz)) != 0) {
5017 if (pch)
5018 switch (*pch) {
5019 case 'y':
5020 case 'Y':
5021 free(pch);
5022 return TRUE;
5023 case 'n':
5024 case 'N':
5025 free(pch);
5026 return FALSE;
5027 default:
5028 free(pch);
5029 }
5030
5031 outputl(_("Please answer `y' or `n'."));
5032 }
5033 return FALSE;
5034 }
5035
5036 extern int
GiveAdvice(skilltype Skill)5037 GiveAdvice(skilltype Skill)
5038 {
5039
5040 char *sz;
5041
5042 /* should never happen */
5043 if (!fTutor)
5044 return FALSE;
5045
5046 switch (Skill) {
5047
5048 case SKILL_VERYBAD:
5049 sz = _("You may be about to make a very bad play");
5050 break;
5051
5052 case SKILL_BAD:
5053 sz = _("You may be about to make a bad play");
5054 break;
5055
5056 case SKILL_DOUBTFUL:
5057 sz = _("You may be about to make a doubtful play");
5058 break;
5059
5060 default:
5061 return (TRUE);
5062 }
5063
5064 if (Skill > TutorSkill)
5065 return (TRUE);
5066
5067 {
5068 int ret;
5069 char *buf = g_strdup_printf("%s. %s", sz, _("Are you sure?"));
5070 ret = GetAdviceAnswer(buf);
5071 g_free(buf);
5072 return ret;
5073 }
5074 }
5075
5076 extern void
TextToClipboard(const char * sz)5077 TextToClipboard(const char *sz)
5078 {
5079 #if defined(USE_GTK)
5080 if (fX)
5081 GTKTextToClipboard(sz);
5082 #else
5083 /* no clipboard: just write string */
5084 outputl(sz);
5085 #endif
5086 }
5087
5088 void
CommandDiceRolls(char * sz)5089 CommandDiceRolls(char *sz)
5090 {
5091 int n;
5092 char *pch;
5093 unsigned int anDice[2];
5094
5095 if ((pch = NextToken(&sz))) {
5096 n = ParseNumber(&pch);
5097
5098 while (n-- > 0) {
5099 RollDice(anDice, &rngCurrent, rngctxCurrent);
5100
5101 printf("%u %u\n", anDice[0], anDice[1]);
5102
5103 }
5104
5105 }
5106 }
5107
5108
5109 #if defined(HAVE_LIB_READLINE)
5110 extern void
CommandHistory(char * UNUSED (sz))5111 CommandHistory(char *UNUSED(sz))
5112 {
5113
5114 int i;
5115 HIST_ENTRY *phe;
5116
5117 for (i = 0; i < history_length; ++i) {
5118 phe = history_get(i + history_base);
5119 outputf("%6d %s\n", i + history_base, phe->line);
5120 }
5121
5122
5123 }
5124
5125 #endif /* HAVE_LIB_READLINE */
5126
5127 extern void
CommandClearHint(char * UNUSED (sz))5128 CommandClearHint(char *UNUSED(sz))
5129 {
5130 pmr_hint_destroy();
5131 outputl(_("Analysis used for `hint' has been cleared"));
5132 }
5133
5134
5135 /*
5136 * Description: Calculate Effective Pip Counts (EPC), either
5137 * by lookup in one-sided databases or by doing a
5138 * one-sided rollout.
5139 * Parameters :
5140 * Input anBoard (the board)
5141 * fOnlyRace: only calculate EPCs for race positions
5142 * Output arEPC (the calculate EPCs)
5143 * pfSource (source of EPC; 0 = database, 1 = OSR)
5144 *
5145 * Returns: 0 on success, non-zero otherwise
5146 *
5147 */
5148
5149 extern int
EPC(const TanBoard anBoard,float * arEPC,float * arMu,float * arSigma,int * pfSource,const int fOnlyBearoff)5150 EPC(const TanBoard anBoard, float *arEPC, float *arMu, float *arSigma, int *pfSource, const int fOnlyBearoff)
5151 {
5152
5153 const float x = (2 * 3 + 3 * 4 + 4 * 5 + 4 * 6 + 6 * 7 +
5154 5 * 8 + 4 * 9 + 2 * 10 + 2 * 11 + 1 * 12 + 1 * 16 + 1 * 20 + 1 * 24) / 36.0f;
5155
5156 if (isBearoff(pbc1, anBoard)) {
5157 /* one sided in-memory database */
5158 float ar[4];
5159 unsigned int n;
5160 int i;
5161
5162 for (i = 0; i < 2; ++i) {
5163 n = PositionBearoff(anBoard[i], pbc1->nPoints, pbc1->nChequers);
5164
5165 if (BearoffDist(pbc1, n, NULL, NULL, ar, NULL, NULL))
5166 return -1;
5167
5168 if (arEPC)
5169 arEPC[i] = x * ar[0];
5170 if (arMu)
5171 arMu[i] = ar[0];
5172 if (arSigma)
5173 arSigma[i] = ar[1];
5174
5175 }
5176
5177 if (pfSource)
5178 *pfSource = 0;
5179
5180 return 0;
5181
5182 } else if (isBearoff(pbcOS, anBoard)) {
5183 /* one sided in-memory database */
5184 float ar[4];
5185 unsigned int n;
5186 int i;
5187
5188 for (i = 0; i < 2; ++i) {
5189 n = PositionBearoff(anBoard[i], pbcOS->nPoints, pbcOS->nChequers);
5190
5191 if (BearoffDist(pbcOS, n, NULL, NULL, ar, NULL, NULL))
5192 return -1;
5193
5194 if (arEPC)
5195 arEPC[i] = x * ar[0];
5196 if (arMu)
5197 arMu[i] = ar[0];
5198 if (arSigma)
5199 arSigma[i] = ar[1];
5200
5201 }
5202
5203 if (pfSource)
5204 *pfSource = 0;
5205
5206 return 0;
5207
5208 } else if (!fOnlyBearoff) {
5209
5210 /* one-sided rollout */
5211
5212 int nTrials = 5760;
5213 float arMux[2];
5214 float ar[5];
5215 int i;
5216
5217 raceProbs(anBoard, nTrials, ar, arMux);
5218
5219 for (i = 0; i < 2; ++i) {
5220 if (arEPC)
5221 arEPC[i] = x * arMux[i];
5222 if (arMu)
5223 arMu[i] = arMux[i];
5224 if (arSigma)
5225 arSigma[i] = -1.0f; /* raceProbs cannot calculate sigma! */
5226 }
5227
5228 if (pfSource)
5229 *pfSource = 1;
5230
5231 return 0;
5232
5233 }
5234
5235 /* code not reachable */
5236 return -1;
5237 }
5238
5239 #ifdef WIN32
5240 /* WIN32 setlocale must be manipulated through putenv to be gettext compatible */
5241 char *
SetupLanguage(const char * newLangCode)5242 SetupLanguage(const char *newLangCode)
5243 {
5244 static char *org_lang = NULL;
5245 char *lang;
5246 if (!org_lang)
5247 org_lang = g_win32_getlocale();
5248 if (!newLangCode || !strcmp(newLangCode, "system") || !strcmp(newLangCode, ""))
5249 lang = g_strdup_printf("LANG=%s", org_lang);
5250 else
5251 lang = g_strdup_printf("LANG=%s", newLangCode);
5252 putenv(lang);
5253 g_free(lang);
5254 return (setlocale(LC_ALL, ""));
5255 }
5256 #else
5257 char *
SetupLanguage(const char * newLangCode)5258 SetupLanguage(const char *newLangCode)
5259 {
5260 static char *org_lang = NULL;
5261 char *result = NULL;
5262
5263 if (!org_lang) {
5264 org_lang = g_strdup(setlocale(LC_ALL, ""));
5265 if (!org_lang) {
5266 outputerrf(_("Locale in your environment not supported by " "C library. Falling back to C locale.\n"));
5267 org_lang = g_strdup("C");
5268 }
5269 }
5270
5271 if (newLangCode && *newLangCode && strcmp(newLangCode, "system") != 0) {
5272 g_setenv("LC_ALL", newLangCode, TRUE);
5273 result = setlocale(LC_ALL, newLangCode);
5274 return (result);
5275 }
5276 g_setenv("LC_ALL", org_lang, TRUE);
5277 result = setlocale(LC_ALL, org_lang);
5278 return (result);
5279 }
5280 #endif
5281
5282 void
asyncFindBestMoves(findData * pfd)5283 asyncFindBestMoves(findData * pfd)
5284 {
5285 if (FindnSaveBestMoves(pfd->pml, pfd->anDice[0], pfd->anDice[1], pfd->pboard,
5286 pfd->keyMove, pfd->rThr, pfd->pci, pfd->pec, pfd->aamf) < 0)
5287 MT_SetResultFailed();
5288
5289 RefreshMoveList(pfd->pml, NULL);
5290 }
5291
5292 void
asyncFindMove(findData * pfd)5293 asyncFindMove(findData * pfd)
5294 {
5295 if (FindnSaveBestMoves(pfd->pml, ms.anDice[0], ms.anDice[1], pfd->pboard,
5296 pfd->keyMove, pfd->rThr, pfd->pci, pfd->pec, pfd->aamf) < 0)
5297 MT_SetResultFailed();
5298 }
5299
5300 void
asyncDumpDecision(decisionData * pdd)5301 asyncDumpDecision(decisionData * pdd)
5302 {
5303 if (DumpPosition(pdd->pboard, pdd->szOutput, pdd->pec, pdd->pci,
5304 fOutputMWC, fOutputWinPC, pdd->n, MatchIDFromMatchState(&ms)) != 0)
5305 MT_SetResultFailed();
5306 }
5307
5308 void
asyncScoreMove(scoreData * psd)5309 asyncScoreMove(scoreData * psd)
5310 {
5311 if (ScoreMove(NULL, psd->pm, psd->pci, psd->pec, psd->pec->nPlies) < 0)
5312 MT_SetResultFailed();
5313 }
5314
5315 void
asyncEvalRoll(decisionData * pdd)5316 asyncEvalRoll(decisionData * pdd)
5317 {
5318 EvaluateRoll(pdd->aarOutput[0], ms.anDice[0], ms.anDice[1], pdd->pboard, pdd->pci, pdd->pec);
5319 }
5320
5321 void
asyncAnalyzeMove(moveData * pmd)5322 asyncAnalyzeMove(moveData * pmd)
5323 {
5324 if (AnalyzeMove(pmd->pmr, pmd->pms, plGame, NULL, pmd->pesChequer, pmd->pesCube, pmd->aamf, NULL, NULL) < 0)
5325 MT_SetResultFailed();
5326 }
5327
5328 void
asyncGammonRates(decisionData * pdd)5329 asyncGammonRates(decisionData * pdd)
5330 {
5331 if (getCurrentGammonRates(pdd->aarRates, pdd->aarOutput[0], pdd->pboard, pdd->pci, pdd->pec) < 0)
5332 MT_SetResultFailed();
5333 }
5334
5335 void
asyncMoveDecisionE(decisionData * pdd)5336 asyncMoveDecisionE(decisionData * pdd)
5337 {
5338 if (GeneralEvaluationE(pdd->aarOutput[0], pdd->pboard, pdd->pci, pdd->pec) < 0)
5339 MT_SetResultFailed();
5340 }
5341
5342 void
asyncCubeDecisionE(decisionData * pdd)5343 asyncCubeDecisionE(decisionData * pdd)
5344 {
5345 if (GeneralCubeDecisionE(pdd->aarOutput, pdd->pboard, pdd->pci, pdd->pec, pdd->pes) < 0)
5346 MT_SetResultFailed();
5347 }
5348
5349 void
asyncCubeDecision(decisionData * pdd)5350 asyncCubeDecision(decisionData * pdd)
5351 {
5352 if (GeneralCubeDecision(pdd->aarOutput, pdd->aarStdDev, pdd->aarsStatistics,
5353 pdd->pboard, pdd->pci, pdd->pes, NULL, NULL) < 0)
5354 MT_SetResultFailed();
5355 }
5356
5357 extern int
RunAsyncProcess(AsyncFun fun,void * data,const char * msg)5358 RunAsyncProcess(AsyncFun fun, void *data, const char *msg)
5359 {
5360 int ret;
5361 #if defined(USE_MULTITHREAD)
5362 Task *pt = (Task *) malloc(sizeof(Task));
5363 pt->pLinkedTask = NULL;
5364 pt->fun = fun;
5365 pt->data = data;
5366 MT_AddTask(pt, TRUE);
5367 #endif
5368
5369 ProgressStart(msg);
5370
5371 #if defined(USE_MULTITHREAD)
5372 ret = MT_WaitForTasks(Progress, 100, FALSE);
5373 #else
5374 asyncRet = 0;
5375 fun(data); /* Just call function in single threaded build */
5376 ret = asyncRet;
5377 #endif
5378
5379 ProgressEnd();
5380
5381 return ret;
5382 }
5383
5384 extern void
ProcessEvents(void)5385 ProcessEvents(void)
5386 {
5387 #if defined(USE_GTK)
5388 if (fX) {
5389 while (gtk_events_pending())
5390 gtk_main_iteration();
5391 } else
5392 #endif
5393 {
5394 while (g_main_context_pending(NULL))
5395 g_main_context_iteration(NULL, TRUE);
5396 }
5397 }
5398
5399 extern int
CheckGameExists(void)5400 CheckGameExists(void)
5401 {
5402 if (plGame) {
5403 return TRUE;
5404 } else {
5405 outputl(_("No game in progress."));
5406 return FALSE;
5407 }
5408 }
5409
5410 extern gboolean
save_autosave(gpointer UNUSED (unused))5411 save_autosave(gpointer UNUSED(unused))
5412 {
5413 int fd;
5414 FILE *pf;
5415 listOLD *pl;
5416
5417 g_return_val_if_fail(plGame, FALSE);
5418
5419 MT_Exclusive();
5420 if (autosave)
5421 g_unlink(autosave);
5422
5423 autosave = g_build_filename(szHomeDirectory, "backup", "XXXXXX.sgf", NULL);
5424
5425 fd = g_mkstemp(autosave);
5426 if (fd < 0) {
5427 g_free(autosave);
5428 autosave = NULL;
5429 MT_Release();
5430 return FALSE;
5431 }
5432
5433 close(fd);
5434 g_unlink(autosave);
5435
5436 pf = gnubg_g_fopen(autosave, "w");
5437 if (!pf) {
5438 autosave = NULL;
5439 MT_Release();
5440 return FALSE;
5441 }
5442
5443 for (pl = lMatch.plNext; pl != &lMatch; pl = pl->plNext)
5444 SaveGame(pf, pl->p);
5445
5446 fclose(pf);
5447 MT_Release();
5448 return TRUE;
5449 }
5450
5451 extern void
delete_autosave(void)5452 delete_autosave(void)
5453 {
5454 if (autosave) {
5455 g_unlink(autosave);
5456 g_free(autosave);
5457 autosave = NULL;
5458 }
5459 }
5460
5461 extern int
get_input_discard(void)5462 get_input_discard(void)
5463 {
5464 if (fInterrupt)
5465 return FALSE;
5466
5467 if (autosave && fAutoSaveConfirmDelete) {
5468 if (!GetInputYN(_("Are you sure you want to discard the current match and your existing autosave? ")))
5469 return FALSE;
5470 g_unlink(autosave);
5471 g_free(autosave);
5472 autosave = NULL;
5473 return TRUE;
5474 }
5475 if (ms.gs == GAME_PLAYING && fConfirmNew)
5476 return GetInputYN(_("Are you sure you want to discard the current match? "));
5477
5478 return TRUE;
5479 }
5480