
/*  
    xgammon version 0.97a, a backgammon program

    Copyright (C) 1994	Lambert Klasen & Detlef Steuer
			klasen@asterix.uni-muenster.de
			steuer@amadeus.statistik.uni-dortmund.de

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
    COPYING for more details.
 */


#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <X11/IntrinsicP.h>
#include <X11/Shell.h>
#include <X11/Composite.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>

#include <X11/Xaw/Simple.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Cardinals.h>

#include "xgammon.h"
#include "gammon.h"

#include "icon.h"

/* allow.c */
extern int test_move       ();
extern int move_is_allowed ();

/* desicion.c */
extern MOVE *find_best_move ();
extern float evaluate       ();
extern int   do_double      ();
extern void  set_binom      ();
extern void  set_naufm      ();

/* drawing.c */
extern void DrawBoard         ();
extern void DrawEmptyBoard    ();
extern void DrawDice          ();
extern void DeleteDice        ();
extern void DrawEmptyDice     ();
extern void DrawDiceValues    ();
extern void DrawDoubler       ();
extern void DrawStone         ();
extern void RedrawAllStones   ();
extern void CreatePixmaps     ();
extern void FreePixmaps       ();

/* edit.c */
extern void EditPosition ();

/* filemenu.c */
extern void File             ();
extern void CreateSaveDialog (void);

/* popup.c */
extern void Info                         ();
extern void CreateXGammonPopup           ();
extern void ShowComputerAnswerToDoubling ();
extern int  DoublePopup			 ();

/* misc.c */
extern void   init_game         ();
extern void   RollDice          ();
extern int    end_of_game_test  (int color);
extern int    complete_blockade (void);
extern void   AppendMoveString  ();
extern void   AddResult         (int whom);
static void   PipCount          ();
extern int    EventToPin        ();
extern XPoint PinToPosition     (int pin);
extern void   maildump          ();
extern void   open_protokol     ();

/* load.l */
extern void load        ();
extern void load_config ();

/* save.c */
extern void save        ();
extern void save_config ();
extern void sig_save    ();

/* rollout.c */
extern void RollOut      ();
extern void exec_rollout ();

/* xgammon.c */
void Redraw	   ();
void restart	   ();
void Resign	   (int how);
void ReplayGame	   ();
void Quit	   ();
void MenuSelect	   ();
void UndoMove	   ();
void set_game_kind ();

static void  CompiFinish      ();
static void  setup_gc         ();
static void  cp_back          ();
static void  PopupButtonShell ();
static void  ResizeBoard      ();
static void  CreateBoard      ();
static void  checkcmdline     (int argc, char *argv[]);
static void  PlayerInit       ();
static void  TournamentInit   ();
static void  ButtonMove       ();
static void  TakeStone        ();
static void  MoveStone        ();
static void  PlaceStone       ();
static int   HandleHumanDoubling ();
static int   HandleCompiDoubling ();
static void  GetHumanMoveEvents  ();
       void  HumanStoneMove      ();
static void  CompiLoop           ();
       void  HumanLoop           ();
static void  BearOff             (void);
void ShowCompiMove              ();
void XGammonGameLoop            ();
void XGammonAppTournamentLoop   ();


#ifdef IRIX	/* ?? */
void usleep (unsigned long delay) {
	sginap (80*delay);
}
#endif

#ifdef SUNOS4   /* ?? */
#define usleep(a) sleep((a/10))
#endif



XtAppContext	app_con;

int		width, height;			/* of the board */
int		stone_width, stone_height;
static int      old_place_x, old_place_y;	/* for moving stones */

Doubler		doubler;
BOARD		board;

unsigned long	delaytime;		/* gammon_resource.delaytime * 100000 (microsec / tsec) */
int		font_width;

static int	get_moves = 0,         took_one = 0;		/* glabal flags */
int		end_of_tournament = 0, end_of_game;
int		initialize = 0,        break_loop = 0,   doubling_done = 1;
int		replaying  = 0;

int		turn, other, pash, to_move, roll[4];
int		direction, start_pin, end_pin;

int		from_pin, pip_count_request = 0;
int		done_hit, current_hit[4];	/* for UndoMove () */
extern int	done_yet;
extern MOVE	current_move[];
MOVE		*compi_choice;

FILE*		protokol_file = NULL;

char * greetings = "Welcome to xgammon  version 0.98\n   (C) 1994-99   Lambert Klasen   Detlef Steuer\n    We hope you enjoy it\n\n";

XtActionsRec gammon_actions[] = {
	{"PipCount",		PipCount},
	{"CompiFinish",		CompiFinish},
	{"Redraw",		Redraw},
	{"UndoMove",		UndoMove},
	{"ResizeBoard",		ResizeBoard},
	{"Quit",		Quit},
	{"save",		save},
	{"load",		load},
	{"restart",		restart},
};

static XtResource gammon_resources[] = {
#define offset(field)   XtOffsetOf(struct _gammon_resource, field)
{"otherdisplay",    "OtherDisplay",    XtRString,  sizeof (char *),  offset (other_display),     XtRString, (caddr_t) NULL},
{"boardGeometry",   "Geometry",        XtRString,  sizeof (char *),  offset (board_geometry),    XtRString, (caddr_t) &gammon_resource.board_geometry},
{"boardColor",      "Background",      XtRPixel,   sizeof (Pixel),   offset (board_Pixel),       XtRPixel,  (caddr_t) &gammon_resource.board_Pixel},
{"boardColor",      "Background",      XtRString,  sizeof (char *),  offset (board_color),       XtRString, (caddr_t) NULL},
{"lightColor",      "Background",      XtRPixel,   sizeof (Pixel),   offset (light_Pixel),       XtRPixel,  (caddr_t) &gammon_resource.light_Pixel},
{"lightColor",      "Background",      XtRString,  sizeof (char *),  offset (light_color),       XtRString, (caddr_t) NULL},
{"darkColor",       "Background",      XtRPixel,   sizeof (Pixel),   offset (dark_Pixel),        XtRPixel,  (caddr_t) &gammon_resource.dark_Pixel},
{"darkColor",       "Background",      XtRString,  sizeof (char *),  offset (dark_color),        XtRString, (caddr_t) NULL},
{"barColor",        "Foreground",      XtRPixel,   sizeof (Pixel),   offset (bar_Pixel),         XtRPixel,  (caddr_t) &gammon_resource.bar_Pixel},
{"barColor",        "Foreground",      XtRString,  sizeof (char *),  offset (bar_color),         XtRString, (caddr_t) NULL},
{"whiteColor",      "Background",      XtRPixel,   sizeof (Pixel),   offset (white_Pixel),       XtRPixel,  (caddr_t) &gammon_resource.white_Pixel},
{"whiteColor",      "Background",      XtRString,  sizeof (char *),  offset (white_color),       XtRString, (caddr_t) NULL},
{"blackColor",      "Foreground",      XtRPixel,   sizeof (Pixel),   offset (black_Pixel),       XtRPixel,  (caddr_t) &gammon_resource.black_Pixel},
{"blackColor",      "Foreground",      XtRString,  sizeof (char *),  offset (black_color),       XtRString, (caddr_t) NULL},
{"smallFont",       "Font",            XtRString,  sizeof (char *),  offset (small_font),        XtRString, (caddr_t) "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*"},
{"doublerFont",     "Font",            XtRString,  sizeof (char *),  offset (doubler_font),      XtRString, (caddr_t) "-*-helvetica-medium-r-normal-*-24-*-*-*-*-*-*-*"},
{"humanStones",     "String",          XtRString,  sizeof (char *),  offset (human_stone),       XtRString, (caddr_t) "black"},
{"gamekind",        "Gamekind",        XtRString,  sizeof (char *),  offset (gamekind),          XtRString, (caddr_t) "hvc"},
{"protokol",        "Protokol",        XtRBoolean, sizeof (int),     offset (protokol),          XtRString, "0"},
{"stonesteps",      "Stonesteps",      XtRInt,     sizeof (int),     offset (stone_steps),       XtRString, "5"},
{"delaytime",       "delaytime",       XtRInt,     sizeof (int),     offset (delaytime),         XtRString, "10"},
{"watchmove",       "Watchmove",       XtRBoolean, sizeof (int),     offset (watchmove),         XtRString, "1"},
{"autoplay",        "Autoplay",        XtRBoolean, sizeof (int),     offset (autoplay),          XtRString, "1"},
{"doubling",        "Doubling",        XtRBoolean, sizeof (int),     offset (doubling),          XtRString, "1"},
{"getdice",         "Getdice",         XtRBoolean, sizeof (int),     offset (getdice),           XtRString, "0"},
{"moneygame",       "Moneygame",       XtRBoolean, sizeof (int),     offset (moneygame),         XtRString, "1"},
{"winat",           "Winat",           XtRInt,     sizeof (int),     offset (winat),             XtRString, "0"},
{"numberOfGames",   "NumberOfGames",   XtRInt,     sizeof (int),     offset (number_of_games),   XtRString, (caddr_t) &gammon_resource.number_of_games},
{"mutations",       "Mutations",       XtRInt,     sizeof (int),     offset (mutations),         XtRString, (caddr_t) &gammon_resource.mutations},
{"rollout",         "Rollout",         XtRBoolean, sizeof (int),     offset (rollout),           XtRString, "0"},
{"numrollouts",     "NumRollouts",     XtRInt,     sizeof (int),     offset (num_rollouts),      XtRString, "100"},
{"positionfile",    "PositionFile",    XtRString,  sizeof (char *),  offset (position_file),     XtRString, "xgammon.save"},
{"database",        "Database",        XtRString,  sizeof (char *),  offset (database),          XtRString, "lib/xgammon.db"},
{"server",          "Server",          XtRString,  sizeof (char *),  offset (server),            XtRString, "fraggel65.mdstud.chalmers.se"},
{"port",            "Port",            XtRInt,     sizeof (int),     offset (port),              XtRString, "4321"},
{"buttonmove",      "ButtonMove",      XtRBoolean, sizeof (int),     offset (button_move),       XtRString, "0"}

#undef offset
};

static XrmOptionDescRec options[] = {
	{"-otherdisplay",    ".otherdisplay",    XrmoptionSepArg,  NULL},
	{"-boardgeometry",   ".boardGeometry",   XrmoptionSepArg,  NULL},
	{"-boardcolor",      ".boardColor",      XrmoptionSepArg,  NULL},
	{"-bc",              ".boardColor",      XrmoptionSepArg,  NULL},
	{"-darkcolor",       ".darkColor",       XrmoptionSepArg,  NULL},
	{"-dc",              ".darkColor",       XrmoptionSepArg,  NULL},
	{"-lightcolor",      ".lightColor",      XrmoptionSepArg,  NULL},
	{"-lc",              ".lightColor",      XrmoptionSepArg,  NULL},
	{"-barcolor",        ".barColor",        XrmoptionSepArg,  NULL},
	{"-b",               ".barColor",        XrmoptionSepArg,  NULL},
	{"-whitecolor",      ".whiteColor",      XrmoptionSepArg,  NULL},
	{"-blackcolor",      ".blackColor",      XrmoptionSepArg,  NULL},
	{"-doublerfont",     ".doublerFont",     XrmoptionSepArg,  NULL},
	{"-smallfont",       ".smallFont",       XrmoptionSepArg,  NULL},
	{"-h",               ".humanStones",     XrmoptionSepArg,  NULL},
	{"-g",               ".gamekind",        XrmoptionSepArg,  NULL},
	{"-gamekind",        ".gamekind",        XrmoptionSepArg,  NULL},
	{"-learn",           ".gamekind",        XrmoptionNoArg,   "3"},
	{"-play",            ".gamekind",        XrmoptionNoArg,   "4"},
	{"-winat",           ".winat",           XrmoptionSepArg,  NULL},
	{"-rollout",         ".rollout",         XrmoptionNoArg,   "1"},
	{"-n",               ".numberOfGames",   XrmoptionSepArg,  "10"},
	{"-m",               ".mutations",       XrmoptionSepArg,  "100"},
	{"-nr",              ".numrollouts",     XrmoptionSepArg,  NULL},
	{"-f",               ".positionfile",    XrmoptionSepArg,  NULL},
	{"-stonesteps",      ".stonesteps",      XrmoptionSepArg,  NULL},
	{"-delaytime",       ".delaytime",       XrmoptionSepArg,  NULL},
	{"-protokol",        ".protokol",        XrmoptionNoArg,   "1"},
	{"-moneygame",       ".moneygame",       XrmoptionNoArg,   "0"},
	{"+moneygame",       ".moneygame",       XrmoptionNoArg,   "1"},
	{"-watchmove",       ".watchmove",       XrmoptionNoArg,   "0"},
	{"+watchmove",       ".watchmove",       XrmoptionNoArg,   "1"},
	{"-autoplay",        ".autoplay",        XrmoptionNoArg,   "0"},
	{"+autoplay",        ".autoplay",        XrmoptionNoArg,   "1"},
	{"-getdice",         ".getdice",         XrmoptionNoArg,   "1"},
	{"-doubling",        ".doubling",        XrmoptionNoArg,   "0"},
	{"+doubling",        ".doubling",        XrmoptionNoArg,   "1"},
	{"-database",        ".database",        XrmoptionSepArg,  NULL},
	{"-server",          ".server",          XrmoptionSepArg,  NULL},
	{"-port",            ".port",            XrmoptionSepArg,  NULL},
	{"-buttonmove",      ".buttonmove",      XrmoptionNoArg,   "0"},
	{"+buttonmove",      ".buttonmove",      XrmoptionNoArg,   "1"},
	{"-bm",              ".buttonmove",      XrmoptionNoArg,   "0"},
	{"+bm",              ".buttonmove",      XrmoptionNoArg,   "1"},
};

int main (int argc, char **argv)
{
	signal (SIGHUP,  sig_save);
	signal (SIGINT,  sig_save);
	signal (SIGQUIT, sig_save);
	signal (SIGSEGV, sig_save);
	signal (SIGFPE,  sig_save);
	signal (SIGPIPE, sig_save);
	signal (SIGTERM, sig_save);	/* for debugging */

	Player[0].X11Set.toplevel = XtAppInitialize (&app_con, "XGammon", options, XtNumber (options),
			           &argc, argv, NULL, NULL, ZERO);

	XtGetApplicationResources (Player[0].X11Set.toplevel, (caddr_t) &gammon_resource, gammon_resources, XtNumber (gammon_resources), NULL, (Cardinal) 0);
	XtAppAddActions (app_con, gammon_actions, XtNumber (gammon_actions));

	Player[0].X11Set.dpy  = XtDisplay (Player[0].X11Set.toplevel);

	checkcmdline (argc, argv);
								/* initialize */
	/*load_config ();*/						/* decision config */
	set_binom   ();						/* decision ... */
	set_naufm   ();

	srand (time (NULL));					/* rolling */

	delaytime = ((unsigned long) gammon_resource.delaytime) * 100000;

	endgame_database = fopen (gammon_resource.database, "rb");
	if (!endgame_database) fprintf (stderr, "endgame database not found\nyou should create one\n");

	if (gammon_resource.protokol) open_protokol ();

	if (gammon_resource.rollout)  RollOut ();

	CreateBoard        (0);
	CreateXGammonPopup (&(Player[0].X11Set));

	XtRealizeWidget    (Player[0].X11Set.toplevel);
	PopupButtonShell   (Player[0].X11Set.toplevel, Player[0].X11Set.button_shell);

	Player[0].X11Set.board.window = XtWindow (Player[0].X11Set.board.widget);

	setup_gc      (&Player[0].X11Set);

	{
		Arg arg[1];
		Pixmap icon;
		icon = XCreateBitmapFromData (Player[0].X11Set.dpy, RootWindowOfScreen (XtScreen (Player[0].X11Set.toplevel)),
					      xgammon_bits, xgammon_width, xgammon_height);
		XtSetArg    (arg[0], XtNiconPixmap, icon);
		XtSetValues (Player[0].X11Set.toplevel, arg, 1);
	}


	if (gammon_resource.other_display) {

	/* XtToolkitInitialize (); is already done */ /* app_con = XtCreateApplicationContext (); exists already */

	Player[1].X11Set.dpy = XtOpenDisplay (app_con, gammon_resource.other_display, argv[0], argv[0],  options, XtNumber (options), &argc, argv);

	Player[1].X11Set.toplevel = XtAppCreateShell (argv[0], "applicationShellWidgetClass", applicationShellWidgetClass, Player[1].X11Set.dpy, NULL, 0);

	CreateBoard        (1);
	CreateXGammonPopup (&(Player[1].X11Set));

	XtRealizeWidget    (Player[1].X11Set.toplevel);
	PopupButtonShell   (Player[1].X11Set.toplevel, Player[1].X11Set.button_shell);

	Player[1].X11Set.board.window = XtWindow (Player[1].X11Set.board.widget);

	setup_gc      (&Player[1].X11Set);

	{
		Arg arg[1];
		Pixmap icon;
		icon = XCreateBitmapFromData (Player[1].X11Set.dpy, RootWindowOfScreen (XtScreen (Player[1].X11Set.toplevel)),
					      xgammon_bits, xgammon_width, xgammon_height);
		XtSetArg    (arg[0], XtNiconPixmap, icon);
		XtSetValues (Player[1].X11Set.toplevel, arg, 1);
	}
	}

	CreatePixmaps (0);
	if (gammon_resource.other_display) CreatePixmaps (1);

	AppendDialogText (UPPER, greetings);

	PlayerInit     ();
	TournamentInit ();

	if (!gammon_resource.other_display) {
		Player[1].X11Set = Player[0].X11Set;
	}

	while (1) {
		XGammonAppTournamentLoop ();
		restart ();
	}

	return 0;
}

/*	XGammonAppTournamentLoop:
	the main event loop for xgammon 
 */

void XGammonAppTournamentLoop (void)
{
	end_of_tournament = 0;
	while (!end_of_tournament) {
		init_game ();
		XGammonGameLoop ();
		if (initialize != EDITED_POSITION &&
		    initialize != LOADED_POSITION &&
		    initialize != REPLAY_GAME) AddResult (turn);

		if (tournament.winning_point && gammon_resource.winat) {
			if (Player[0].points >= tournament.winning_point ||
			    Player[1].points >= tournament.winning_point) {
				end_of_tournament = 1;
				sleep (3);
			}
		}
	}
}

/*	XGammonGameLoop:
	event loop for a single game 
 */

void XGammonGameLoop (void)
{
	end_of_game = 0;

	if (initialize != EDITED_POSITION &&
	    initialize != LOADED_POSITION) {
		if (tournament.game_number == 1 &&	/* make startup look a little nicer */
		   ((Player[0].beginner_of_game && Player[0].type == COMPUTER) ||
		    (Player[1].beginner_of_game && Player[1].type == COMPUTER))) {
			while (1) {
				XEvent event;
				XtAppNextEvent(app_con, &event);
				if(event.type == Expose && event.xexpose.window == Player[turn-1].X11Set.board.window) {
					XtDispatchEvent(&event);
					XSync (Player[0].X11Set.dpy, 0);
					if (gammon_resource.other_display)
						XSync (Player[1].X11Set.dpy, 0);
					break;
				}
				XtDispatchEvent(&event);
				XSync (Player[0].X11Set.dpy, 0);
				if (gammon_resource.other_display)
					XSync (Player[1].X11Set.dpy, 0);
			}
		}
		Player[turn-1].MoveFunction (FIRSTMOVE);
	} else {
		DrawDice (turn);
		DrawDoubler (doubler.value, doubler.owner);
		initialize = NORMAL_GAME;
		Player[turn-1].MoveFunction (FIRSTMOVE);
	}

	while (!end_of_game) {
		switch_turn ();
		Player[turn-1].MoveFunction (DOUBLE);
	}
}

/*	HumanLoop:
	event loop for a human player 
 */

void HumanLoop (int with_double) 
{
	if (Pin[BAR].count && complete_blockade ()) {
		sprintf (add_text, "%s can't move.\n\n", Player[turn-1].name);
		AppendDialogText (LOWER, add_text);
		return;
	}

	if (with_double != FIRSTMOVE && gammon_resource.doubling) {
		doubling_done = 0;
		if (doubler.owner != turn) {
			DeleteDice ();
			DrawEmptyDice (turn);
			do {
				if (pip_count_request) pip_count_request = 0;
				if (HandleHumanDoubling () == RESIGN) {
					end_of_game = 1;
					return;
				}
			} while (pip_count_request);
		}
		doubling_done = 1;
	}

	if (with_double != FIRSTMOVE) 
		RollDice ();

	HumanStoneMove ();
}

static int HandleHumanDoubling (void)
{
	int double_result;
	int p = 0;

	if (doubler.owner == turn || doubler.value == 64) return 0;

	while (1) {
		XEvent event;
		XtAppNextEvent (app_con, &event);
		if (event.type == ButtonRelease) {	/* not Press */
			if (event.xbutton.window == Player[turn-1].X11Set.board.window) {
				p = EventToPin   (event.xbutton.x, event.xbutton.y);
				break;		/* other can't break */
			}
			XtDispatchEvent (&event);
		}
		else if (event.type == KeyPress) {
			KeySym k;
			Modifiers m;
			k = XtGetActionKeysym (&event, &m);
			if (k == 'd') p = DOUBLER;
			XtDispatchEvent (&event);
			if (event.xkey.window == Player[turn-1].X11Set.board.window)
				break;
		}
		XtDispatchEvent (&event);
	}

	if (p == DOUBLER) {

		doubler.owner = turn;

		if (Player[other-1].type == COMPUTER) {		/* check opponent */
			double_result = do_double (ANSWER);
			ShowComputerAnswerToDoubling (double_result);
			if (!double_result) return RESIGN;
		} else {
			return DoublePopup ();		/* human doubles human */
		}
	}

	if (end_of_game) return RESIGN;	/* resign ... menu pressed prevents rolling */
	return 0;
}

void GetHumanMoveEvents (void)
{
	get_moves = 1;
	while (roll[0]) {
		XEvent event;
		XtAppNextEvent (app_con, &event);
		if (event.type == KeyPress) {
			KeySym k;
			Modifiers m;
			k = XtGetActionKeysym (&event, &m);
			if (k == 'o' || k == ' ' || k == '\n') {	/* want to move off */
				BearOff ();
			}
		}
		XtDispatchEvent (&event);
		if (break_loop) {	/* computer shall finish */
			break_loop = 0;
			break;
		}
	}
	get_moves = 0;
}

void HumanStoneMove (void)
{
	int mc;

	DrawDice (turn);
	if ((mc = test_move () )) {
		if (mc == 1 && gammon_resource.autoplay) {
			compi_choice = possible_moves;
			ShowCompiMove ();
			AppendMoveString (compi_choice);
		}
		else GetHumanMoveEvents ();
	}
	else AppendMoveString (NULL);
}

/*	CompiLoop:
	event loop for a computer player 
 */

void CompiLoop (int with_double)
{
	if (Pin[BAR].count && complete_blockade ()) {
		AppendMoveString (NULL);
		return;
	}

	if (with_double && gammon_resource.doubling) {
		if (HandleCompiDoubling () == RESIGN) {	/* other resigned */
			end_of_game = 1;
			return;
		}

	}

	if (with_double != FIRSTMOVE) 
		RollDice ();
	DrawDice (turn);
	XSync (Player[0].X11Set.dpy, 0);
	if (gammon_resource.other_display)
		XSync (Player[1].X11Set.dpy, 0);

	if (!gammon_resource.watchmove) usleep (delaytime);
	if (test_move ()) {
		compi_choice = find_best_move ();
		ShowCompiMove ();
		AppendMoveString (compi_choice);
	}
	else AppendMoveString (NULL);
}

static int HandleCompiDoubling (void)
{
	int ret = 0;

	if (doubler.owner == turn || doubler.value == 64) return 0;

	if (! do_double (OFFER)) return 0;

	doubler.owner = turn;

	if (Player[other-1].type == HUMAN)	/* check opponent */
		return DoublePopup ();
	else {					/* opponent is computer */
		if (! do_double (ANSWER)) {	/* resigned */
			sprintf (add_text, "%s gives up. %s wins %d point\n\n", Player[other-1].name, Player[turn-1].name, doubler.value);
			ret = RESIGN;
		} else {			/* accepted */
			doubler.value *= 2;
			DrawDoubler (doubler.value, doubler.owner);
			sprintf (add_text,"%s accepts the double.\n\n", Player[other-1].name);
			ret = ACCEPT;
		}
		AppendDialogText (LOWER, add_text);
	}

	return ret;
}

void ShowCompiMove (void)
{
	XPoint   move_start, move_end;
	int	 x, y, dx, dy, steps;
	int      i, j;

	for (i=0; i<to_move; i++) {
	RemoveStone  ((compi_choice+i)->from);

	if (gammon_resource.watchmove) {
		move_start = PinToPosition   ((compi_choice+i)->from);
		if ((compi_choice+i)->to != end_pin)
			move_end   = PinToPosition   ((compi_choice+i)->to);
		else
			move_end   = PinToPosition   (FINISHED);

		x = move_start.x;
		y = move_start.y;

		if (abs ((move_start.x - move_end.x)) > abs ((move_start.y - move_end.y)))
			steps = abs (move_start.x - move_end.x)/gammon_resource.stone_steps;
		else    steps = abs (move_start.y - move_end.y)/gammon_resource.stone_steps;

		DrawStone  (x,y);
		old_place_x = x;
		old_place_y = y;

		dx = (move_start.x - move_end.x) * -1 / steps;
		dy = (move_start.y - move_end.y) * -1 / steps;

		for (j=0; j<steps; j++) {
			x += dx;
			y += dy;
			cp_back (old_place_x, old_place_y);
			DrawStone  (x, y, turn);
			XSync (Player[0].X11Set.dpy, 0);
			if (gammon_resource.other_display)
				XSync (Player[1].X11Set.dpy, 0);
			old_place_x = x;
			old_place_y = y;
		}
		cp_back (old_place_x, old_place_y);
	}			/* watchmove */

	if ((compi_choice+i)->to == end_pin) {	/* endpin check must be first cause of removing other from endpin, i.e. bar */
		PutStone  (turn, FINISHED);
		if ((end_of_game = end_of_game_test (turn))) return;
	}
	else if (Pin[ (compi_choice+i)->to].color == other) {
		current_hit[i] = (compi_choice+i)->to;
		RemoveStone  ((compi_choice+i)->to); 
		PutStone  (other, OTHER_BAR);
		PutStone  (turn, (compi_choice+i)->to);
	}
	else PutStone  (turn, (compi_choice+i)->to);

	XSync (Player[0].X11Set.dpy, 0);
	if (gammon_resource.other_display)
		XSync (Player[1].X11Set.dpy, 0);
	if (!gammon_resource.watchmove) usleep (delaytime);

	/* look for events happened while moving */
	while (XtAppPending (app_con)) {
		XEvent event;
		XtAppNextEvent (app_con, &event);
		XtDispatchEvent (&event);
	}
	}
}

void cp_back (int x, int y)
{
	Display *dpy = Player[0].X11Set.dpy;
	Pixmap   p   = Player[0].X11Set.board.pixmap;
	Window   w   = Player[0].X11Set.board.window;
	GC       gc  = Player[0].X11Set.board.gc;

	XCopyArea (dpy, p, w, gc, x, y, stone_width, stone_width, x, y);

	if (gammon_resource.other_display) {
		dpy = Player[1].X11Set.dpy;
		p   = Player[1].X11Set.board.pixmap;
		w   = Player[1].X11Set.board.window;
		gc  = Player[1].X11Set.board.gc;

		XCopyArea (dpy, p, w, gc, x, y, stone_width, stone_width, x, y);
	}
}

static void CreateBoard (int player_index)
{
	Display    *dpy = Player[player_index].X11Set.dpy;
	WidgetClass board_class;
	Arg	    args[2];
	int	    screen_num = DefaultScreen (dpy);

	static XtActionsRec board_actions[] = {
		{"Redraw",		Redraw},
		{"ResizeBoard",		ResizeBoard},
		{"TakeStone",		TakeStone},
		{"MoveStone",		MoveStone},
		{"PlaceStone",		PlaceStone},
		{"ButtonMove",	        ButtonMove},
		{NULL, NULL}
	};


	static String no_bm_translations = "#override     \n\
		<Expose>:    		Redraw()          \n\
		<ConfigureNotify>:      ResizeBoard()     \n\
		Button1<Btn1Motion>:    MoveStone()       \n\
		<Btn1Up>:               PlaceStone()      \n\
		<Btn1Down>:             TakeStone()       \n\
	";

	static String bm_translations = "#override        \n\
		<Expose>:    		Redraw()          \n\
		<ConfigureNotify>:      ResizeBoard()     \n\
		<ButtonPress>:          ButtonMove()      \n\
	";

	static String translations;

	if (gammon_resource.button_move)
		translations = bm_translations;
	else
		translations = no_bm_translations;

/* 
   the board width x height is arbitraryly 15 x 12 

   that fast I couldn't get separated board geometry and normal Xt -geometry
   option, so there is this strange and ugly boardgeometry,
   the reason is, that there is no toplevel width and height before XtRealize.
   further XParseGeometry doesn't parse very low values like 10x10 or 0x0
 */

{
	int dummy_x, dummy_y; 		/* of no interest, at least yet */
	 (void) XParseGeometry (gammon_resource.board_geometry,
			      &dummy_x, &dummy_y, &width, &height);
	if (width < 150 || height < 120) {
		width = 150; height = 120;
		fprintf (stderr,"boardsize should at least be 150x120 pixel\ngeometry values changed\n");
	}
}

	if (width  > DisplayWidth (dpy, screen_num) - 50 || height > DisplayHeight (dpy, screen_num) - 50) {
		width  = DisplayWidth  (dpy, screen_num) - 50;
		height = DisplayHeight (dpy, screen_num) - 50;
	}

	stone_width = stone_height = width/15;

	if (height < stone_width * 12) stone_width = height/12;		/* some kinda screens there are */

	width  = stone_width * 15;
	height = stone_width * 12;

	XtSetArg (args[0], XtNwidth,  width);
	XtSetArg (args[1], XtNheight, height);

	Player[player_index].X11Set.board.widget = XtCreateManagedWidget ("board", simpleWidgetClass, Player[player_index].X11Set.toplevel, args, 2);

	if (player_index == 0) {	/* first players displays board */
		XtOverrideTranslations (Player[player_index].X11Set.board.widget, XtParseTranslationTable (translations));
		XtAppAddActions (app_con, board_actions, XtNumber (board_actions));
	} else {			/* other players displays board */
		Arg t[1];
		XtTranslations first_board_translations;

		/* this ensures both board have the same base of translations (via .Xdefaults) */
		XtSetArg (t[0], XtNtranslations, first_board_translations);
		XtGetValues (Player[0].X11Set.board.widget, t, 1);
		XtSetValues (Player[1].X11Set.board.widget, t, 1);

		/* now add the hard wired ones of this function */
		XtOverrideTranslations (Player[player_index].X11Set.board.widget, XtParseTranslationTable (translations));
		XtAppAddActions (app_con, board_actions, XtNumber (board_actions));
	}

	board_class = (WidgetClass) XtClass (Player[player_index].X11Set.board.widget);
	board_class->core_class.compress_exposure = XtExposeCompressMaximal;
	board_class->core_class.compress_motion = True;
}

static void PlayerInit (void)
{
	extern char *UserName();

	if      (!strcmp (gammon_resource.gamekind, "hvc")) {
		if      (!strcmp (gammon_resource.human_stone, "black")) {
			Player[0].type = HUMAN;
			Player[1].type = COMPUTER;
			Player[0].MoveFunction = HumanLoop;
			Player[1].MoveFunction = CompiLoop;
			strcpy (Player[0].name, UserName());
			strcpy (Player[1].name, "xgammon");
		}
		else if (!strcmp (gammon_resource.human_stone, "white")) {
			Player[0].type = COMPUTER;
			Player[1].type = HUMAN;
			Player[0].MoveFunction = CompiLoop;
			Player[1].MoveFunction = HumanLoop;
			strcpy (Player[0].name, "xgammon");
			strcpy (Player[1].name, UserName());
		}
	}
	else if (!strcmp (gammon_resource.gamekind, "cvc")) {
		Player[0].type = COMPUTER;
		Player[1].type = COMPUTER;
		Player[0].MoveFunction = CompiLoop;
		Player[1].MoveFunction = CompiLoop;
		strcpy (Player[0].name, "black");
		strcpy (Player[1].name, "white");
		initialize = COMPUTER_TOURNAMENT;
	}
	else if (!strcmp (gammon_resource.gamekind, "hvh")) {
		Player[0].type = HUMAN;
		Player[1].type = HUMAN;
		Player[0].MoveFunction = HumanLoop;
		Player[1].MoveFunction = HumanLoop;
		strcpy (Player[0].name, UserName());
		strcpy (Player[1].name, "other");
	}

	Player[0].color = BLACK;
	Player[1].color = WHITE;
}


/* TournamentInit;
   a little too big a name, only set a label and set the cursor */

static void TournamentInit ()
{
	Display    *dpy = Player[0].X11Set.dpy;
	Arg        c[1];
	char       *s;
	static int once = 0;

	if      (gammon_resource.moneygame) {
		s = "money-game";
		tournament.winning_point = 0;
		sprintf (add_text, "%s and %s start a %s match.\n", Player[0].name, Player[1].name, s);
	}

	if (gammon_resource.winat)  {
		s = "points";
		tournament.winning_point = gammon_resource.winat;
		gammon_resource.moneygame = 0;
		sprintf (add_text, "%s and %s start a %d %s match.\n", Player[0].name, Player[1].name, tournament.winning_point, s);
	}

	AppendDialogText (UPPER, add_text);

	if (once) return;

	/* prepare the cursor, looks like the stones you move */
	Player[0].X11Set.fg_col.pixel = gammon_resource.black_Pixel;
	Player[0].X11Set.bg_col.pixel = gammon_resource.white_Pixel;

	XQueryColor (dpy, DefaultColormapOfScreen (DefaultScreenOfDisplay (dpy)), &Player[0].X11Set.fg_col);
	XQueryColor (dpy, DefaultColormapOfScreen (DefaultScreenOfDisplay (dpy)), &Player[0].X11Set.bg_col);

	XtSetArg (c[0], XtNcursor, &Player[0].X11Set.cursor);
	XtGetValues (Player[0].X11Set.board.widget, c, 1);

	if (!gammon_resource.other_display) {
		if      (Player[0].type == HUMAN) XRecolorCursor (dpy, Player[0].X11Set.cursor, &Player[0].X11Set.fg_col, &Player[0].X11Set.bg_col);
		else if (Player[1].type == HUMAN) XRecolorCursor (dpy, Player[0].X11Set.cursor, &Player[0].X11Set.bg_col, &Player[0].X11Set.fg_col);
		else				  XRecolorCursor (dpy, Player[0].X11Set.cursor, &Player[0].X11Set.fg_col, &Player[0].X11Set.bg_col);
	} else {
		XRecolorCursor (dpy, Player[0].X11Set.cursor, &Player[0].X11Set.fg_col, &Player[0].X11Set.bg_col);

		dpy = Player[1].X11Set.dpy;
		Player[1].X11Set.bg_col.pixel = gammon_resource.black_Pixel;
		Player[1].X11Set.fg_col.pixel = gammon_resource.white_Pixel;

		XQueryColor (dpy, DefaultColormapOfScreen (DefaultScreenOfDisplay (dpy)), &Player[1].X11Set.fg_col);
		XQueryColor (dpy, DefaultColormapOfScreen (DefaultScreenOfDisplay (dpy)), &Player[1].X11Set.bg_col);

		XtSetArg (c[0], XtNcursor, &Player[1].X11Set.cursor);
		XtGetValues (Player[1].X11Set.board.widget, c, 1);

		XRecolorCursor (Player[1].X11Set.dpy, Player[1].X11Set.cursor, &Player[1].X11Set.fg_col, &Player[1].X11Set.bg_col);
	}

	once = 1;
}

/* Quit is both, Callback and Action routine.
   I'm too lazy to invent an extra Callback */
void Quit (Widget w, XEvent *e, String *vector, Cardinal *count)
{
	if (endgame_database)         fclose (endgame_database);
	if (protokol_file)	      fclose (protokol_file);

	XtDestroyApplicationContext (XtWidgetToApplicationContext (w));

	exit (0);
}

static void checkcmdline (int argc, char *argv[])
{
	int option;

	for (option=1; option<argc; option++) {
		if (strcmp (argv[option], "-?") == 0 ||
		    strcmp (argv[option], "-help") == 0) {
puts ("xgammon [-otherdisplay (display)]
        [-boardgeometry (string)]
        [-boardcolor (color)] [-bc (color)]
        [-darkcolor  (color)] [-dc (color)]
        [-lightcolor (color)] [-lc (color)]
        [-barcolor   (color)] [-b  (color)]
        [-whitecolor (color)]        the stone color for \"white\"
        [-blackcolor (color)]        the stone color for \"black\"
        [-doublerfont (font)]        sets the (big) font for the doubler dice
        [-doubling   +doubling]      sets if you wish the computer to double
        [-h (black|white)]           set the color for the human player
                                     (implies a human versus computer game)
        [-gamekind -g (hvc|cvc|hvh)] sets the gamekind to either
                                     human vs. human computer vs. computer
                                     or human vs. computer (default)
        [-winat      (points)]       play a tournament up to \"points\" points
        [-watchmove  +wachmove]      if you wish to see the flying stones
        [-buttonmove +buttonmove]    set stones by simply pressing button
        [-autoplay   +autoplay]      automatic play when only one move
                                     is possible
        [-stonesteps (number)]       sets the stepsize the stones fly
                                     in pixel (implies +watchmove)
        [-delaytime (tsec.)]         sets the time the cumputer waits
                                     after each turn (implies -watchmove)
        [-?] [-help]                 print this string");
		exit (0);
		} else {
			fprintf (stderr,"xgammon: unknown option %s (ignored)\n", argv[option]);
		}
	}
        if (!gammon_resource.stone_steps) {
                gammon_resource.watchmove = 0;
                fprintf (stderr, "stonesteps has to be > 0 with watchmove\n");
        }
}

void setup_gc (X11SET *X11Set)
{
	XGCValues values;
	XtGCMask  valuemask;

	valuemask = GCForeground|GCBackground|GCFunction;

	values.function   = GXcopy;
	values.foreground = gammon_resource.black_Pixel;
	values.background = gammon_resource.board_Pixel;
	X11Set->gc	   = XtGetGC (X11Set->board.widget, valuemask, &values);

	values.foreground = gammon_resource.board_Pixel;
	values.background = gammon_resource.board_Pixel;
	X11Set->board.gc    = XtGetGC (X11Set->board.widget, valuemask, &values);

	values.foreground = gammon_resource.black_Pixel;	/* black stone */
	values.background = gammon_resource.white_Pixel;
	X11Set->stoneGC[0] = XtGetGC (X11Set->board.widget, valuemask, &values);

	values.foreground = gammon_resource.white_Pixel;
	values.background = gammon_resource.black_Pixel;
	X11Set->stoneGC[1] = XtGetGC (X11Set->board.widget, valuemask, &values);

	valuemask ^= GCLineWidth|GCCapStyle|GCFont;

	values.foreground = gammon_resource.black_Pixel;
	values.background = gammon_resource.white_Pixel;
	values.line_width = 3;
	values.cap_style  = CapRound;

	/* the fonts actually used */
	if ((X11Set->doubler_font = XLoadQueryFont (X11Set->dpy, gammon_resource.doubler_font)) == NULL) {
		fprintf (stderr, "couldn't load doubler dice font, using default, sorry\n");
	}
	else values.font = X11Set->doubler_font->fid;

	X11Set->diceGC     = XtGetGC (X11Set->board.widget, valuemask, &values);

	X11Set->small_font = XLoadQueryFont (X11Set->dpy, gammon_resource.small_font);
}

void Redraw (Widget w, XEvent *e, String *vector, Cardinal *count)
{
	DrawEmptyBoard    ();
	DrawBoard         ();
	RedrawAllStones   ();
	DrawEmptyDice     (turn);
	if (doubling_done) DrawDiceValues (turn);
	DrawDoubler (doubler.value, doubler.owner);
}

static void ResizeBoard (Widget w, XConfigureEvent *e, String *vector, Cardinal *count)
{
	Arg arg[2];
	Dimension new_stone_width, new_stone_height;
	Dimension new_width, new_height;

	if (gammon_resource.other_display) return;

	XClearArea (Player[0].X11Set.dpy, Player[0].X11Set.board.window, 0, 0, width, height, 0);
/*	if (gammon_resource.other_display)
		XClearArea (Player[0].X11Set.dpy, Player[1].X11Set.board.window, 0, 0, width, height, 0);
*/
	new_stone_width = new_stone_height = e->width/15;
	new_width       = new_stone_width * 15;

	if (e->height < new_stone_width * 12) {
		new_stone_width = e->height/12;
		new_height = new_stone_width * 12;
		new_width  = new_stone_width * 15;
	}
	else new_height = new_stone_width * 12;

	width  += (new_width-width);
	height += (new_height-height);
	stone_width = new_stone_width;

	XtSetArg (arg[0], XtNwidth, width);
	XtSetArg (arg[1], XtNheight, height);
	XtSetValues (Player[0].X11Set.board.widget, arg, 2);

	FreePixmaps   ();
	CreatePixmaps (0);
	if (gammon_resource.other_display) CreatePixmaps (1);

	Redraw (0L, 0L, (char**) 0L, 0);
}

void MenuSelect (Widget w, XtPointer junk, XtPointer garbage)
{
	XEvent event;
	char   name[64];
	char   *v[] = {"p", NULL};

	strcpy (name, XtName (w));

	if      (strncmp (name, "save position", 13) == 0) save (w, 0L, v, 0);
	else if (strncmp (name, "load position", 13) == 0) load (w, 0L, v, 0);
	else if (strncmp (name, "load game",      9) == 0) {
		v[0] = "g";
		load (w, 0L, v, 0);
	}
	else if (strncmp (name, "save game",      9) == 0 ) { 
		v[0] = "g";
		save (w, 0L, v, 0);
	}
	else if (strcmp  (name, "save as .."       ) == 0) CreateSaveDialog ();
	else if (strcmp  (name, "load .."          ) == 0) File             ();
	else if (strncmp (name, "restart",        7) == 0) restart          ();
	else if (strncmp (name, "undo move",      9) == 0) UndoMove         (0L, 0L, (char **) 0L, 0);
	else if (strcmp  (name, "compi vs. compi"  ) == 0) set_game_kind    (COMPI_VS_COMPI);
	else if (strcmp  (name, "human vs. compi"  ) == 0) set_game_kind    (HUMAN_VS_COMPI);
	else if (strcmp  (name, "human vs. human"  ) == 0) set_game_kind    (HUMAN_VS_HUMAN);
	else if (strcmp  (name, "computer finish"  ) == 0) CompiFinish      ();
	else if (strcmp  (name, "rollout"          ) == 0) exec_rollout     ();
	else if (strcmp  (name, "edit position"    ) == 0) EditPosition     ();
	else if (strcmp  (name, "mail dump"        ) == 0) maildump         ();
	else if (strcmp  (name, "replay game"      ) == 0) ReplayGame       ();
	else if (strcmp  (name, "resign normal"    ) == 0) Resign           (NORMAL);
	else if (strcmp  (name, "resign gammon"    ) == 0) Resign           (GAMMON);
	else if (strcmp  (name, "resign backgammon") == 0) Resign           (BACKGAMMON);

	while (XtAppPending (app_con)) XtAppNextEvent (app_con, &event);
}

void ButtonMove (Widget w, XButtonEvent *e)
{
	int p, dp, to;

	if (!gammon_resource.button_move) return;

	if (!get_moves) return;

	p = EventToPin (e->x, e->y);

	if ((Pin[BAR].count && p != BAR) ||
	    ! (Pin[p].count && Pin[p].color == turn)) return;

	if (pash) {
		if (turn==BLACK) to = (p+roll[1] < end_pin) ? p+roll[1] : end_pin;
		else             to = (p+roll[1] > end_pin) ? p+roll[1] : end_pin;
		if (move_is_allowed (p, to)) {   /* roll[0] is used as a flag (sorry) */
			RemoveStone  (p);
			if (to != end_pin) {
				if (Pin[to].color == other) {
					RemoveStone  (to);
					current_hit [done_hit] = to;
					done_hit++;
					PutStone  (other, OTHER_BAR);
				}
				PutStone  (turn, to);
			}
			else	PutStone  (turn, FINISHED);
		}
	}
	else {
		if      (e->button == 1) {
			dp = (roll[0] > roll[1]) ? roll[1] : roll[0];
			if (turn==BLACK) { to = (p+dp < end_pin) ? p+dp : end_pin; }
			else             { to = (p+dp > end_pin) ? p+dp : end_pin; }
			if (move_is_allowed (p, to)) {
				RemoveStone  (p);
				if (to != end_pin) {
					if (Pin[to].color == other) {
						RemoveStone  (to);
						current_hit [done_hit] = to;
						done_hit++;
						PutStone  (other, OTHER_BAR);
					}
					PutStone  (turn, to);
				}
				else	PutStone  (turn, FINISHED);
			}
		}
		else if (e->button == 3) {
			dp = (roll[0] > roll[1]) ? roll[0] : roll[1];
			if (turn==BLACK) to = ( p+dp < end_pin) ? p+dp : end_pin;
			else             to = ( p+dp > end_pin) ? p+dp : end_pin;
			if (move_is_allowed (p, to)) {
				RemoveStone  (p);
				if (to != end_pin) {
					if (Pin[to].color == other) {
						RemoveStone  (to);
						current_hit [done_hit] = to;
						done_hit++;
						PutStone  (other, OTHER_BAR);
					}
					PutStone  (turn, to);
				}
				else	PutStone  (turn, FINISHED);
			}
		}
		else if (e->button == 2) {
		}

	}
}

void TakeStone (Widget w, XButtonEvent *e)
{
	int p;

	if (!get_moves) return;

	if (gammon_resource.button_move) return;

	if (gammon_resource.other_display) {
		if (Player[turn-1].X11Set.board.window != e->window) return;
	}

	p = EventToPin   (e->x, e->y);

	if (Pin[BAR].count && p != BAR) {
		from_pin = 0;
		return;
	}

	if (Pin[p].count && Pin[p].color == turn) {
		from_pin = p;
		RemoveStone  (p);
		old_place_x = e->x - stone_width/2;
		old_place_y = e->y - stone_width/2;
		DrawStone  (old_place_x, old_place_y, turn);
		took_one = 1;
	} else {
		from_pin = 0;
	}

}

void MoveStone (Widget w, XButtonEvent *e)
{
	int x, y;

	if (!took_one) return;

	if (gammon_resource.button_move) return;

	x = e->x - stone_width/2;
	y = e->y - stone_width/2;

	if (x<0) x = 0;
	else if (x > width - stone_width)  x = width  - stone_width;

	if (y<0) y = 0;
	else if (y > height - stone_width) y = height - stone_width;

	cp_back (old_place_x, old_place_y);
	DrawStone  (x, y, turn);
	old_place_x = x;
	old_place_y = y;
}

void PlaceStone (Widget w, XButtonEvent *e)
{
	int p;

	if (!get_moves) return;

	if (gammon_resource.button_move) return;

	if (!took_one) return;
	took_one = 0;

	cp_back (old_place_x, old_place_y);

	p = EventToPin   (e->x, e->y);

	if (!move_is_allowed (from_pin, p)) {
		PutStone  (turn, from_pin);
		return;
	}

	if (p == end_pin) {		/* check this first, cause possible removing other from endpin (other bar) */
		PutStone  (turn, FINISHED);
		if ((end_of_game = end_of_game_test (turn))) return;
	}
	else if (Pin[p].color == other) {
		RemoveStone  (p);
		current_hit [done_hit] = p;
		done_hit++;
		PutStone  (other, OTHER_BAR);
		PutStone  (turn, p);
	}
	else {
		PutStone  (turn, p);
	}
}

void UndoMove (Widget w, XEvent *e, String *vector, Cardinal *count)
{
	int i;

	for (i=done_yet-1; i> -1; i--) {
		if (current_move[i].to == end_pin)
			RemoveStone  (FINISHED);
		else	RemoveStone  (current_move[i].to);

		PutStone (turn, current_move[i].from);
	}

	for (i=0; i<done_hit; i++) {
		PutStone    (other, current_hit[i]);
		RemoveStone (OTHER_BAR);
	}

	done_yet = 0;
	done_hit = 0;
}

static void CompiFinish (Widget w, XEvent *e, String *vector, Cardinal *count)
{
	int d;

	if (done_yet)
		Info ("Please finish your move,\nI don't want to complete it\n");
	else {
		set_game_kind (COMPI_VS_COMPI);

		d = gammon_resource.doubling;
		gammon_resource.doubling = 0;
		if (!doubling_done) {
			doubling_done = 1;
			RollDice ();
			DrawDice (turn);
		}

		if (!test_move ()) {
			AppendMoveString (NULL);
		} else {
			compi_choice = find_best_move ();
			ShowCompiMove    ();
			AppendMoveString (compi_choice);
		}

		while (!end_of_game) {
			switch_turn ();
			Player[turn-1].MoveFunction (DOUBLE);
		}

		set_game_kind (HUMAN_VS_COMPI);
		gammon_resource.doubling = d;

		break_loop = 1;
	}
}

void ReplayGame (Widget w, XEvent *e, String *vector, Cardinal *count)
{
	get_moves = 0;
	replaying = 1;

	AppendDialogText (UPPER, "\n\nReplay a game saved in FIBS or xgammon format.\nChoose file to replay\n\nPress button (any time) to continue...");
	
	File ();
}

static void BearOff (void)
{
	int can_off = 0;
	MOVE *m = possible_moves;

	if (done_yet) return;	/* no completition of moves */

	while (m != list) {
		if (m->to == end_pin && (m+1)->to == end_pin) {
			if (pash && (m+2)->to == end_pin && (m+3)->to == end_pin) {
				can_off = 1; break;
			} else {
				can_off = 1; break;
			}
		}
		m += 4;
	}

	if (can_off) {
		compi_choice = m;
		ShowCompiMove ();
		AppendMoveString (compi_choice);
		roll[0] = 0;
		done_yet = 0;
		done_hit = 0;
	}
}

static void PipCount (Widget w, XEvent *e, String *vector, Cardinal *count)
{
	extern void simple_pipcount ();
	float pip_count[3];

	simple_pipcount (&pip_count);

	sprintf (add_text, " picount:\n black: %d, white: %d ", abs ((int) pip_count[BLACK]), abs ((int) pip_count[WHITE]));
	Info (add_text);

	pip_count_request = 1;
}

void PopupButtonShell (Widget parent, Widget button_shell)
{
	Position x, y;
	Dimension w;
	Arg args[2];

	XtSetArg (args[0], XtNwidth,  &w);
	XtGetValues (parent, args, 1);

	XtSetArg (args[0], XtNx, &x);
	XtSetArg (args[1], XtNy, &y);
	XtGetValues (parent, args, 2);

	x += (Position) (w + 40);
	if (x<0) x=0;
	if (y<0) y=0;
	XtSetArg (args[0], XtNx, x);
	XtSetArg (args[1], XtNy, y);
	XtSetValues (button_shell, args, 2);
	XtPopup     (button_shell, XtGrabNone);
}

void restart (void)
{
	tournament.game_number = 1;
	Player[0].points = 0;
	Player[1].points = 0;

	AppendDialogText (UPPER, "tournament restart:\n");

	end_of_tournament = 1;
	end_of_game = 1;
	initialize = NORMAL_GAME;
	break_loop = 1;

	TournamentInit ();
}

void Resign (int how)
{
	sprintf (add_text, "%s wants to resign\n", Player[turn-1].name);
	AppendDialogText (LOWER, add_text);

	Info ("accept");

	AppendDialogText (LOWER, "xgammon accepts\n\n");

	switch_turn ();		 /* for AddResult () */
	end_of_game = how;       /* for AddResult () */
	initialize = NORMAL_GAME;
	break_loop = 1;
}

void set_game_kind (int to)
{
	if      (to == HUMAN_VS_COMPI) {
		Player[0].type = HUMAN;
		Player[1].type = COMPUTER;
		Player[0].MoveFunction = HumanLoop;
		Player[1].MoveFunction = CompiLoop;
	}
	else if (to == COMPI_VS_COMPI) {
		Player[0].type = COMPUTER;
		Player[1].type = COMPUTER;
		Player[0].MoveFunction = CompiLoop;
		Player[1].MoveFunction = CompiLoop;
	}
	else if (to == HUMAN_VS_HUMAN) {
		Player[0].type = HUMAN;
		Player[1].type = HUMAN;
		Player[0].MoveFunction = HumanLoop;
		Player[1].MoveFunction = HumanLoop;
	}
}
