/* $Id: decision.c,v 1.2 1999/10/08 20:09:59 delle Exp delle $ */
/*  decision.c

		Copyright (C) 1994, 1999  Lambert Klasen & Detlef Steuer
			                  klasen@asterix.uni-muenster.de
			                  Detlef.Steuer@gmx.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 <math.h>
#include <X11/Intrinsic.h>	/* for the BOARD */
#include "gammon.h"

extern MOVE *find_best_move	    ();
extern long int PositionNumber      (int STARTPIN, int color);

int   is_run                        ();
float calculate_winning_percentage  (int kind);
float calculate_expected_time       ();
float evaluate                      ();
void  simple_pipcount               (float *pc);
void  adjust_pipcount               (float *pc);
void  sub_lossage_when_hit          (float *pc);
void  Analyse_Doublet               (int pin, int *set);
void  Analyse_No_Doublet            (int pin, int *set);


int  Bar[3]                = { 0,  0, 25 };
int  Direction[3]          = { 0,  1, -1 };
int  Startpin[3]           = { 0,  1, 24 };
int  number_of_homepins[3] = { 0,  0,  0 };
int  Endpin[3]             = { 0, 24,  1 };
float Probtable[25];

float point_values[25] = {	/* Wertetabelle fuer die 24 pins*/
		0.0,                   /* 0 is dummy */
		0.0, 1.0, 3.0, 8.0, 10.0, 10.0,  7.0,  6.0, 5.0, 4.0, 4.0, 6.0, /*  1 - 12 */
		6.0, 4.0, 4.0, 5.0,  6.0,  7.0, 10.0, 10.0, 8.0, 3.0, 1.0, 0.0  /* 13 - 25 */
};

/* Diese Tabelle enthaelt die exakten erwarteten Wartezeiten -1
	 bis ein Stein wieder eingewuerfelt ist                    */
float expected_waitingtime[7] = {
	 0.0,
	 1.0 / 35.0,
	 1.0 /  8.0,
	 1.0 /  3.0,
	 4.0 /  5.0,
	25.0 / 11.0,
	 0.0
};

float prime_length_factor[8] =
{
	0.0,  1.0,  1.0,  1.2,  1.4,  1.7,  2.0,  2.0
};


void adjust_pipcount (float *pc)
{
	int i;

	/* bestimmung der bestzten felder im Homefeld */
	number_of_homepins[BLACK] = number_of_homepins[WHITE] = 0;
	for (i = 1; i < 7; i++) {
		if (Pin[   i].color == WHITE && Pin[   i].count>1) number_of_homepins[WHITE]++;
		if (Pin[25-i].color == BLACK && Pin[25-i].count>1) number_of_homepins[BLACK]++;
	}

	/*
	   Verfeinerter pip_count (Steine auf bar als 25+x)
	   Es wird die vereinfachung gemacht, das es n-mal solange dauert
	   n Steine wieder hineinzuwuerfeln wie einen Stein hineinzuwuerfeln
	 */

	if (Pin[0].count) {
		if (number_of_homepins[WHITE] < 6)
			pc[BLACK] -= Pin[0].count * 8.1667 * expected_waitingtime[number_of_homepins[WHITE]];
		else {
			for (i=6; i<26; i++) {
				if (Pin[i].color == WHITE)
					pc[WHITE] += Pin[i].count * (i - 5);
			}
			pc[BLACK] -= Pin[0].count * 8.1667 * expected_waitingtime[5];
		}
	}
	if (Pin[25].count) {
		if (number_of_homepins[BLACK] < 6)
			pc[WHITE] -= Pin[25].count * 8.1667 * expected_waitingtime[number_of_homepins[BLACK]];
		else {
			for (i=0; i<20; i++) {
				if (Pin[i].color == BLACK) pc[BLACK] += Pin[i].count * (20 - i);
			}
			pc[WHITE] -= Pin[25].count * 8.1667 * expected_waitingtime[5];
		}
	}
#ifdef DEBUG_EVALUATE
	printf("Verfeinerter Pipcount %f %f \n", pc[BLACK], pc[WHITE]);
#endif
}


void add_prime_values(float *pc)
{
	int i;

	for (i = 1; i < 25; i++) {
		if (Pin[i].color == BLACK && Pin[i].count > 1) {
			float zwischensumme = point_values[i];
			int prime_length = 1;
			while (((i + 1) < 25 && Pin[i + 1].color == BLACK && Pin[i + 1].count > 1)) {
				i++;
				prime_length++;
				zwischensumme += point_values[i];
			}
			zwischensumme *= prime_length_factor[prime_length];
			pc[BLACK] += zwischensumme;
		}
	}
	for (i = 1; i < 25; i++) {
		if (Pin[i].color == WHITE && Pin[i].count > 1) {
			float zwischensumme = point_values[i];
			int prime_length = 1;
			while (((i + 1) < 25 && Pin[i + 1].color == WHITE && Pin[i + 1].count > 1)) {
				i++;
				prime_length++;
				zwischensumme += point_values[i];
			}
			zwischensumme *= prime_length_factor[prime_length];
			pc[WHITE] += zwischensumme;
		}
	}
}


/* Diese Funktion soll eine Abschaetzung der Gewinnwahrscheinlichkeit
   fuer turn liefern. Im Endspiel wird diese exakt berechnet, sofern
   die Datenbank vorhanden ist                                      */

float evaluate(int turn)
{
	float pipcount[3] =
	{0.0, 0.0, 0.0};
	int Runninggame = 1;
	float value = 0.0;

	Runninggame = is_run();
	if(!endgame_database) Runninggame=0; /* Falls database nicht existiert*/
	if (!Runninggame) {				/* zunaechst ein grober pipcount*/
		simple_pipcount(pipcount);

#ifdef DEBUG_EVALUATE
		printf("Einfacher Pipcount %f %f \n", pipcount[BLACK], pipcount[WHITE]);
#endif

		pipcount[other] += 8.1666    /* Anpassung an turn */;

		adjust_pipcount  (pipcount)    /* anpassen an BAR Besetzung*/;

		add_prime_values (pipcount)    /* addieren der point_values */;



		/*sub_lossage_when_hit (pipcount); */    /* eventuelles geschlagenwerden */

#ifdef DEBUG_EVALUATE
		printf("Pipcount mit point_values %f %f \n", pipcount[BLACK], pipcount[WHITE]);
#endif

	} else { /* Runninggame */

		value = calculate_winning_percentage(ANSWER);
		/* die naechsten Zeilen garantieren, dass nur ins runninggame
			 gewechselt wird wenn echt siegchance bestehen              */
		if (value >= 0.99) {
			value = calculate_expected_time();
			return (1000.0 - value);
		}
		if (value >= 0.4 && value < 0.99) return ( 980.0 + value);
		if (value > 0.01)		  return (-899.0 + value);

		value = calculate_expected_time();
		/* im Falle _sehr_ geringer Chancen wird als zweitbewertung der
			 Erwartungswert herangezogen (hauptsaechlich Optik)           */
		return (-900.0 - value);
	}

#ifdef DEBUG_EVALUATE
	printf("Wert der Position: %f \n", pipcount[turn] - pipcount[other]);
#endif

	return (pipcount[turn] - pipcount[other]);
}


void sub_lossage_when_hit(float *pc)
{
	int diceset[4];
	int w1, w2, i, j;
	float lossage;

	for (i=0; i<25; i++) Probtable[i] = 0.0;
	for (i=0; i< 4; i++) diceset[i] = 0;

	/* first the non-doublets */
	for (w1=   1; w1<6; w1++) {
	for (w2=w1+1; w2<7; w2++) {
		diceset[0] = w1;
		diceset[1] = w2;
		diceset[2] = w1;
		diceset[3] = 0;

		if (Pin[Bar[other]].count) { /* The enemies bar ist occupied */
			if (Pin[Bar[other]].count > 1) diceset[2] = 0;
			Analyse_No_Doublet (Bar[other], diceset);
			if (Pin[Bar[other]].count> 1) diceset[0] = 0;
			else {
				int tp1, tp2, ways = 0, wp = 0;
				tp1 = Bar[other] + diceset[0] * Direction[other];
				tp2 = Bar[other] + diceset[1] * Direction[other];
	
				if (Pin[tp1].count < 2 || Pin[tp1].color == other) {
					ways++;
					wp = tp2;
				}
				if (Pin[tp2].count < 2 || Pin[tp2].color == other) {
					ways++;
					wp = tp1;
				}
				if (ways == 0) diceset[0] = 0;
				if (ways == 1) diceset[0] = wp;
				diceset[1] = 0;
				if (ways == 2) diceset[2] = 0;
			}
		}				/* Bar analyse zuende */
		for (i=Startpin[other]; i!=Endpin[other]; i+=Direction[other]) {
			if (Pin[i].color == other) Analyse_No_Doublet(i, diceset);
			}
		}
	}

	/* the doublets */
	for (w1 = 1; w1 < 7; w1++) {
		diceset[0] = w1;
		diceset[1] = w1;
		diceset[2] = w1;
		diceset[3] = w1;

		if (Pin[Bar[other]].count) {	/* The enemies bar ist occupied */
			int tp;

			if (Pin[Bar[other]].count > 1) diceset[3] = 0;
			if (Pin[Bar[other]].count > 2) diceset[2] = 0;
			if (Pin[Bar[other]].count > 3) diceset[1] = 0;
			Analyse_Doublet(Bar[other], diceset);

			tp = Bar[other] + diceset[0] * Direction[other];
			if (Pin[tp].color == turn && Pin[tp].count > 1) diceset[0] = 0;
			else {
				if (Pin[Bar[other]].count > 1) diceset[2] = 0;
				if (Pin[Bar[other]].count > 2) diceset[1] = 0;
				if (Pin[Bar[other]].count > 3) diceset[0] = 0;
			}
		}				/* Bar analyse zuende */
		for (i = Startpin[other]; i != Endpin[other]; i += Direction[other]) {
			if (Pin[i].color == other) Analyse_Doublet(i, diceset);
		}
	}
	/* Probability table is completly setup */

	for (i = Startpin[turn]; i != Bar[other]; i += Direction[turn]) {
		if (Pin[i].count == 1 && Pin[i].color == turn && Probtable[i]) {
			lossage = 0.0;
			lossage += (i - Bar[turn]) * Direction[turn];
			if (number_of_homepins[other] < 6)
	lossage += 8.16667 * expected_waitingtime[number_of_homepins[other]];
			else {
	if (Pin[Bar[turn]].count) lossage += 8.16667 * expected_waitingtime[5];
	else {
		for (j = Bar[other]; j != Bar[other] + 20 * Direction[other]; j += Direction[other]) {
			if (Pin[j].color == other) {
			  lossage += Pin[j].count * (Bar[other] + 20 * Direction[other] - j) * Direction[other];
			}
		}
		lossage += 8.1667 * expected_waitingtime[5];
	}
			}
#ifdef DEBUG_PROB
			printf("Blot auf %d, Wkeit = %f, Wert = %f \n", i, Probtable[i], lossage);
#endif
			pc[turn] -= lossage * Probtable[i];
		}
	}
}

void Analyse_Doublet(int pin, int *set)
{
	int j;

	j = pin + set[0] * Direction[other];
	if (j > 0 && j < 25) {
		Probtable[j] += 1.0 / 36.0;
		if (set[1] && (Pin[j].count < 2 || Pin[j].color == other)) {
			j += set[1] * Direction[other];
			if (j > 0 && j < 25) {
	Probtable[j] += 1.0 / 36.0;
	if (set[2] && (Pin[j].count < 2 || Pin[j].color == other)) {
		j += set[2] * Direction[other];
		if (j > 0 && j < 25) {
			Probtable[j] += 1.0 / 36.0;
			if (set[3] && (Pin[j].count < 2 || Pin[j].color == other)) {
			  j += set[3] * Direction[other];
			  if (j > 0 && j < 25)
		Probtable[j] += 1.0 / 36.0;
			}
		}
	}
			}
		}
	}
}

void Analyse_No_Doublet(int pin, int *set)
{
	int j, alset = 0;

	j = pin + set[0] * Direction[other];
	if (j > 0 && j < 25)
	{
		Probtable[j] += 1.0 / 18.0;
		if (set[2] && (Pin[j].count < 2 || Pin[j].color == other))
		{
			j = j + set[1] * Direction[other];
			if (j > 0 && j < 25)
			{
	Probtable[j] += 1.0 / 18.0;
	alset = 1;
			}
		}
		j = pin + set[1] * Direction[other];
		if (j > 0 && j < 25)
		{
			Probtable[j] += 1.0 / 18.0;
			if (set[2] && (Pin[j].count < 2 || Pin[j].color == other) && !alset)
			{
	j = j + set[0] * Direction[other];
	if (j > 0 && j < 25)
		Probtable[j] += 1.0 / 18.0;
			}
		}
	}
}

int
	is_run()
{
	int i, smallest_white = 0, smallest_black = 26;

	for (i = 0; i < 26; i++)
	{
		if (Pin[i].color == BLACK)
		{
			smallest_black = i;
			break;
		}
	}

	for (i = 25; i > -1; i--)
	{
		if (Pin[i].color == WHITE)
		{
			smallest_white = i;
			break;
		}
	}

	if (smallest_black < smallest_white)
		return (0);
	else
		return (1);
}


/* simple_pipcount:
	 returns the simple pipcount for color
	 not concerning turn, no special treatment of bar
	 pc must be array[3]                              */

void simple_pipcount(float *pc)
{
	int i, count[3] = { 0, 0, 0 };

	for (i = 0; i < 26; i++) {
		if (Pin[i].color == BLACK) count[BLACK] -= Pin[i].count * (25 - i);
		if (Pin[i].color == WHITE) count[WHITE] -= Pin[i].count * i;
	}
	pc[BLACK] = (float) count[BLACK];
	pc[WHITE] = (float) count[WHITE];
}

int do_double (int kind)
{
	float pipcount[3] = { 0.0, 0.0, 0.0 };
	int Runninggame = 1;
	float chance;

	Runninggame = is_run();
	if (!endgame_database)
		Runninggame = 0;		/* Falls keine Datenbank existiert
			                                     spielt er zumindest            */
	if (!Runninggame)
	{
		/* zunaechst ein grober pipcount*/

		simple_pipcount(pipcount);

		/* Anpassung nach BAR*/
		adjust_pipcount(pipcount);

		/* Anpassung entsprechend turn */

		if (kind == ANSWER)
			pipcount[turn] += 8.1666;
		else
			pipcount[other] += 8.1666;

		if (pipcount[BLACK] > 0.0)
			pipcount[BLACK] = -0.5;
		if (pipcount[WHITE] > 0.0)
			pipcount[WHITE] = -0.5;


		if (kind == ANSWER)
		{
			chance = pipcount[turn] / pipcount[other];

#ifdef DEBUG_EVALUATE
			printf("Doubler: chance = %f kind = %d turn =%d \n", chance, kind, turn);
#endif

			if (chance > 0.75)
	return 1;
			else
	return 0;
		}
		else
		{
			chance = pipcount[other] / pipcount[turn];

#ifdef DEBUG_EVALUATE
			printf("Doubler: chance = %f kind = %d turn =%d \n", chance, kind, turn);
#endif
			if (chance >= 1.16)
	return 1;
			else
	return 0;
		}

	}
	else
	{
		chance = calculate_winning_percentage(kind);
#ifdef DEBUG_EVALUATE
		printf("Doubler: chance = %f kind = %d turn =%d \n", chance, kind, turn);
#endif
		if (kind == ANSWER)
		{
			if (chance >= 0.25)
	return 1;
			else
	return 0;
		}
		else
		{
			if (chance >= 0.65)
	return 1;
			else
	return 0;
		}

	}
}


float
	calc_endgame_offset(int color)
{
	int i;
	float offset = 0.0;

	if (color == BLACK)
	{
		for (i = 1; i < 19; i++)
			if (Pin[i].color == BLACK)
			{
	offset += (19 - i) * Pin[i].count;	/* Dies ist der reine Offset */

	if (i < 7)
		offset += 4.0833333 * Pin[i].count;	/* Extra punkte fuer crossovers */
	if (i < 13)
		offset += 4.0833333 * Pin[i].count;
	offset += 4.0833333 * Pin[i].count;
			}
	}
	else
	{
		for (i = 24; i > 6; i--)
			if (Pin[i].color == WHITE)
			{
	offset += (i - 6) * Pin[i].count;
	if (i > 18)
		offset += 4.0833333 * Pin[i].count;
	if (i > 12)
		offset += 4.0833333 * Pin[i].count;
	offset += 4.0833333 * Pin[i].count;
			}
	}
	if (color == turn)
		offset += (19 + 16.3333333) * Pin[BAR].count;
	else
		offset += (19 + 16.3333333) * Pin[OTHER_BAR].count;
#ifdef DEBUG_EVALUATE
	printf(" Offset = %f fuer Farbe %d \n", offset, color);
#endif
	return (offset);
}


float calculate_expected_time()
{
	float distribution[31];
	unsigned short readarray[31];
	float offset, e_value = 0.0;
	int nummer, i;

	nummer = PositionNumber(0, turn);

	if (nummer == 0L)
		return (0.0);

	fseek(endgame_database, (nummer - 1) * 60, 0);
	fread( readarray, 2, 30, endgame_database);

for (i=0;i<30;i++) distribution[i]= ((float) readarray[i])/65535.0;

	offset = calc_endgame_offset(turn) / 8.16667;

	for (i = 0; i < 30; i++)
		e_value += ((float) i + 1.0 + offset) * distribution[i];
#ifdef DEBUG_EVALUATE
	printf(" EWert = %f  \n", e_value);
#endif

	return (e_value);
}

float calculate_winning_percentage(int kind)
{
	float t_array[19], other_array[19], temp[19],sum;
	float turn_offset, other_offset, chance = 0, rest_prob;
	unsigned short readarray[19];
	int t_off, o_off;
	int nummer, i, j;


	/* zunaechst fuer den turn einlesen */

	nummer = PositionNumber (0, turn);

	if (nummer == 0L) return (1.0);
	fseek (endgame_database, (nummer - 1) * 36, 0);
	fread (readarray, 2, 18, endgame_database);
	sum=0.0;
	for (i=0; i<18; i++) 
	{
	t_array[i] = ((float) readarray[i])/65535.0;
	sum+=t_array[i];
	}
    for (i=0; i<=18;i++) t_array[i]/=sum;

	/* dann fuer den gegner */

	nummer = PositionNumber(0, other);
	fseek (endgame_database, (nummer - 1) * 36, 0);
	fread (readarray, 2, 18, endgame_database);
	sum=0.0;
	for (i=0; i<18; i++) 
	{
	other_array[i] = ((float) readarray[i])/65535.0;
    sum+=other_array[i];
	}
	for (i=0; i<=18;i++) other_array[i]/=sum;
	t_array[18]     = 0.0;
	other_array[18] = 0.0;

	/* Abstand zum Endgame mit einbeziehen  */

	turn_offset  = calc_endgame_offset (turn)  / 8.16667;
	other_offset = calc_endgame_offset (other) / 8.16667;


	/* Danach werden die Verteilungsfunktionen entsprechend dem offset
	   transformiert                                                */
	if (turn_offset) {
		for (i = 0; i < 19; i++) temp[i] = 0;
		for (i = 0; i < 18; i++) {
			temp[i  ] += (ceil(turn_offset) - turn_offset)  * t_array[i];
			temp[i+1] += (turn_offset - floor(turn_offset)) * t_array[i];
		}
		for (i = 0; i < 19; i++) t_array[i] = temp[i];
	}
	if (other_offset) {
		for (i = 0; i < 19; i++) temp[i] = 0;
		for (i = 0; i < 18; i++) {
			temp[i  ] += (ceil(other_offset) - other_offset)  * other_array[i];
			temp[i+1] += (other_offset - floor(other_offset)) * other_array[i];
		}
		for (i = 0; i < 19; i++) other_array[i] = temp[i];
	}
	t_off = (int) floor(turn_offset);
	o_off = (int) floor(other_offset);

	if (kind == ANSWER) {
		/* diese frage soll herausfinden ob der andere am zug ist oder nicht Tut sie das?  */
		if ((turn == Player[0].color && Player[0].type == HUMAN) ||
	 	    (turn == Player[1].color && Player[1].type == HUMAN)) {
			  for (i = 0; i < 19; i++) {
				rest_prob = 0.0;
				j = 0;
				while (j+o_off < i+t_off && j<31) j++;
				while (j < 19) {
					rest_prob += other_array[j];
					j++;
				}
				chance += t_array[i] * rest_prob;
			  }
			  chance = 1 - chance;
		} else {
			for (i = 0; i < 19; i++) {
				rest_prob = 0.0;
				j = 0;
				while (j + t_off < i + o_off && j < 19) j++;
				while (j < 19) {
					rest_prob += t_array[j];
					j++;
				}
				chance += rest_prob * other_array[i];
			}
			chance = 1 - chance;
		}
	} else {
		for (i = 0; i < 19; i++) {
			rest_prob = 0.0;
			j = 0;
			while (j + o_off < i + t_off && j < 19) j++;
			while (j < 19) {
				rest_prob += other_array[j];
				j++;
			}
			chance += t_array[i] * rest_prob;
		}
	}

#ifdef DEBUG_EVALUATE
	printf(" Gewinnwahrscheinlichkeit fuer den Compi %f \n", chance);
#endif

	return (chance);
}
