/*  rollout.c

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

    This file is free source code; 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 <unistd.h>
#include <signal.h>
#include <strings.h>

#include <X11/IntrinsicP.h>
#include <X11/Xlib.h>

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

extern XtAppContext app_con;
extern FILE *endgame_database;

extern   void     Quit                ();
extern   void     CreateRolloutText   ();
extern   void     load                ();
extern   void     save                ();
extern   int      do_double           ();
extern   int      test_move           ();
extern   MOVE	  *find_best_move     ();
extern   int      end_of_game_test    ();

void   RollOut            ();
void   RolloutLoop        (void);
void   RolloutGame        (void);
void   RolloutMove        (void);
void   init_rollout       (void);
void   rollout_roll_dice  ();
void   exec_rollout       ();

void RollOut (void)
{
	Widget toplevel = Player[0].X11Set.toplevel;
	char *v[2], *flag = "m";
	XEvent event;
	int i;

	CreateRolloutText (toplevel);
	XtRealizeWidget   (toplevel);

	v[0] = flag;
	v[1] = gammon_resource.position_file;
	load (0L, 0L, v, 0);

	for (i=0; i<29; i++) {
		rollout_position[i].count = Pin[i].count;
		rollout_position[i].color = Pin[i].color;
	}

	rollout_save.turn          = turn;
	rollout_save.doubler_value = doubler.value;
	rollout_save.doubler_owner = doubler.owner;
	rollout_save.roll[0]       = roll[0];
	rollout_save.roll[1]       = roll[1];

	tournament.winning_point = gammon_resource.num_rollouts;
	AppendDialogText (LOWER, "xgammon rollout: ");
	sprintf (add_text, "rollout: %d games\n", gammon_resource.num_rollouts);
	AppendDialogText (LOWER, add_text);
	AppendDialogText (LOWER, "points: white 0, black 0\n");

	Player[0].points = 0;
	Player[1].points = 0;


	XSync (Player[0].X11Set.dpy, 0);
	RolloutLoop ();

	while (XtAppPending (app_con)) {	/* delete event queue */
		XtAppNextEvent  (app_con, &event);
		XtDispatchEvent (&event);
	}

	AppendDialogText (LOWER, "rollout done\n(any button or key event will quit.)\n\n");

	while (1) {
		XtAppNextEvent  (app_con, &event);
		if (event.type == ButtonPress || event.type == KeyPress)
			break;
		XtDispatchEvent (&event);
	}

	Quit  (toplevel, 0L, 0L);
}

void RolloutLoop (void)
{
	FILE *result_file;

	unsigned int won_games[3] ={0,0,0};

	while (tournament.game_number < tournament.winning_point) {
		init_rollout ();
		RolloutGame  ();
		Player[turn-1].points += doubler.value;
		won_games[turn] ++;
		sprintf (add_text, "done.. won games: black %d, white %d points: black %d, white %d\n",won_games[BLACK], won_games[WHITE], Player[0].points, Player[1].points);
		AppendDialogText (LOWER, add_text);
	}
	result_file = fopen (gammon_resource.position_file, "a");
	AppendDialogText(LOWER, "\nrollout complete. result:\n");

	fprintf (result_file, "\nrollout result of %d games:\n", tournament.winning_point);
	sprintf (add_text, "of %d games\n", tournament.winning_point);
	AppendDialogText (LOWER, add_text);

	sprintf (add_text, "   black won %d games and got %d points.\n", won_games[BLACK], Player[0].points);
	fprintf (result_file, add_text);
	AppendDialogText (LOWER, add_text);

	sprintf (add_text, "   white won %d games and got %d points.\n", won_games[WHITE], Player[1].points);
	fprintf (result_file, add_text);
	AppendDialogText (LOWER, add_text);

	sprintf (add_text, "   backgammons: black: %d white %d\n", Player[BLACK].backgammons, Player[WHITE].backgammons);
	fprintf (result_file, add_text);
	AppendDialogText (LOWER, add_text);

	sprintf (add_text, "   gammons: black: %d white %d\n", Player[BLACK].gammons, Player[WHITE].gammons);
	fprintf (result_file, add_text);
	AppendDialogText (LOWER, add_text);

	sprintf (add_text, "   in percent: black won %2.2f%%, white won %2.2f%%\n\n\n",  ((float) won_games[BLACK] * 100 / (float) tournament.winning_point), ((float) won_games[WHITE] * 100 / (float) tournament.winning_point));
	fprintf (result_file, add_text);
	AppendDialogText (LOWER, add_text);

	fclose (result_file);

	XSync (Player[0].X11Set.dpy, 0);	/* and 7 seconds */
}

void RolloutGame (void)
{
	end_of_game = 0;
	while (1) {
		RolloutMove();
		if (end_of_game) break;
		switch_turn();
	/*	RolloutMove();
		if (end_of_game) break;
		switch_turn(); */
	}
}

void RolloutMove (void)
{
	int i;
	if (gammon_resource.doubling) {
		if (doubler.owner != turn && doubler.value <= 64) {
		        doubler.owner = turn;
			if (!do_double (OFFER)) {
				end_of_game = 1;
				return;
			}
		}
	}
	rollout_roll_dice();
        if (test_move()) {
		MOVE *this_move;
		this_move = find_best_move ();
		for (i=0; i<to_move; i++) {
			Pin[(this_move+i)->from].count--;
			if (Pin[(this_move+i)->from].count == 0) Pin[(this_move+i)->from].color = 0;

			if ((this_move+i)->to == end_pin) {
				if ((end_of_game = end_of_game_test(turn)))
					return;
	        	}
			else if (Pin[(this_move+i)->to].color == other) {
				Pin[(this_move+i)->to].color = turn;
				Pin[OTHER_BAR].count++;
				Pin[OTHER_BAR].color = other;
			}
			else {
				Pin[(this_move+i)->to].count++;
				Pin[(this_move+i)->to].color = turn;
			}
		}
	}
}

void init_rollout (void)
{
	int i;

	for (i=0; i<29; i++) { 
		Pin[i].count = rollout_position[i].count;
		Pin[i].color = rollout_position[i].color;
	}

	turn          = rollout_save.turn;
	doubler.value = rollout_save.doubler_value;
	doubler.owner = rollout_save.doubler_owner;
	/*roll[0]       = rollout_save.roll[0];
	roll[1]       = rollout_save.roll[1];

        if (roll[0] != roll[1]) {
                roll[2] = 0;
                roll[3] = 0;
		to_move = 2;
		pash = 0;
	}
        else {
		roll[2] = roll[0];
		roll[3] = roll[1];
		to_move = 4;
		pash = 1;
	}*/

	rollout_roll_dice ();

	switch_turn(); switch_turn();	/* set vars */

	tournament.game_number++;
	sprintf(add_text,"rollout game number %d\n", tournament.game_number);
	AppendDialogText(LOWER, add_text);
}

void rollout_roll_dice (void)
{
	roll[0] = (rand()%6 + 1) * direction;
	roll[1] = (rand()%6 + 1) * direction;

        if (roll[0] != roll[1]) {
                roll[2] = 0;		/* sure and necessary */
                roll[3] = 0;
		to_move = 2;
		pash = 0;
	}
        else {
		roll[2] = roll[0];
		roll[3] = roll[1];
		to_move = 4;
		pash = 1;
	}
}

/* saves the current position in a (position) save file, and then
   execs a xgammon with approriate options.
   this is why:
   1. cause of some problems with X* libs xgammon will grow (fast)
      in memory usage. After about 650 games with certain optins,
      it will use about 24 Mb space. 
      (This is not an xgammon fault (!?!) Other programs also do,
      for example play (at least older versions of) xbombs and
      do a ps -m sometimes). But they are running only
      short times and don't use that much graphics).
      This may be fixed some day.
   2. you'll get bored with it and your eyes will hurt, and it's slow
   3. you can continue playing
 */

void exec_rollout (void)
{
	int pid;			/* execed process */
	int ppid = getpid();		/* this process */
	static int called_rollout = 0;	/* count to make files somewhat unequivocal */

	char *argv[8];
	char *v[3];
	char rollout_save_file[50];
	char nr[7];

	sprintf (nr, "%d", gammon_resource.num_rollouts);

	sprintf (rollout_save_file, "xgammon.rollout.%d.save", (ppid + called_rollout++));

	v[0] = "r";
	v[1] = rollout_save_file;
	v[2] = NULL;
	save (0L, 0L, v, 0);

	argv[0] = "xgammon";
	argv[1] = "-rollout";
	argv[2] = "-nr";
	argv[3] = nr;
	argv[4] = "-f";
	argv[5] = rollout_save_file;
	argv[6] = "-doubling";		/* yet necessary */
	argv[7] = NULL;

#ifdef SIGCHLD
	signal(SIGCHLD, SIG_IGN);
#else
	signal(SIGCLD,  SIG_IGN);
#endif

	if ((pid=fork()) == 0) {
		execvp ("xgammon", argv);
	}
	else if (pid>0) {
		return;
	}
	else {	/* should not happen */
		AppendDialogText(LOWER, "sorry, couldn't fork rollout process\n");
		return;
	}
}
