/* $Id: decision.c,v 1.2 1994/08/24 09:23:25 gammon Exp gammon $ */
/*  decision.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 <math.h>
#include "gammon.h"

extern struct _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);

float point_values[25] =	/* Wertetabelle fuer die 24 pins*/
{0.0,
 0.0, 1.0, 3.0, 8.0, 10.0, 10.0, 7.0, 6.0, 5.0, 4.0, 4.0, 6.0,
 6.0, 4.0, 4.0, 5.0, 6.0, 7.0, 10.0, 10.0, 8.0, 3.0, 1.0, 0.0
};

float expected_waitingtime[7] =
/* Diese Tabelle enthaelt die exakten erwarteten Wartezeiten -1
   bis ein Stein wieder eingewuerfelt ist                    */
{
  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, number_of_homepins[3] =
  {0, 0, 0};

  /* bestimmung der bestzten felder im Homefeld */
  for (i = 1; i < 7; i++)
  {
    if (Pin[i].color == WHITE && Pin[i].count >= 2)
      number_of_homepins[WHITE]++;
    if (Pin[25 - i].color == BLACK && Pin[25-i].count >= 2)
      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 < 25 && Pin[i].color == BLACK && Pin[i].count > 1))
      {
	prime_length++;
	zwischensumme += point_values[i];
      }
      zwischensumme *= prime_length_factor[prime_length];
      pc[BLACK] += zwischensumme;

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

    }

  }

}

float
evaluate(int turn)
     /* Diese Funktion soll eine Abschaetzung der Gewinnwahrscheinlichkeit
   fuer turn liefern. Im Endspiel wird diese exakt berechnet, sofern
   die Datenbank vorhanden ist                                      */
{
  float pipcount[3] =
  {0.0, 0.0, 0.0};
  int Runninggame = 1;
  float value=0.0;
  
  Runninggame = is_run();

  if (!Runninggame)
  {				/* zunaechst ein grober pipcount*/
    simple_pipcount(pipcount);

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

    /* Anpassung an turn */

    pipcount[other] += 8.1666;

    /* anpassen an BAR Besetzung*/

    adjust_pipcount(pipcount);

    /* addieren der point_values */

    add_prime_values(pipcount);
#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.4) return( 999.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]);
}


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);
}


void
simple_pipcount(float *pc)
     /* Diese Funktion liefert den einfachen Pipcount von color
   ohne beruecksichtigung von turn
   ohne Anpassung fuer Steine auf der Bar
   pc muss array[3] sein                      */
{
  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
doubler(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];
 float offset,e_value=0.0;
 int nummer,i;

   nummer = PositionNumber(0, turn);
   
if (nummer == 0L)
    return (0.0);
     
 fseek(endgame_database, (nummer - 1) * 120, 0);
   fread(distribution, 4, 30, endgame_database);
   
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)
     /* diese Funktion berechnet -- im endspiel exakt --
   gewinnwahrscheinlichkeiten von turn              */
{
  float t_array[31], other_array[31], temp[31];
  float turn_offset, other_offset, chance = 0, rest_prob;
  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) * 120, 0);
  fread(t_array, 4, 30, endgame_database);
  /* dann fuer den gegner */

  nummer = PositionNumber(0, other);
  fseek(endgame_database, (nummer - 1) * 120, 0);
  fread(other_array, 4, 30, endgame_database);


  t_array[30] = 0.0;
  other_array[30] = 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 etsprechend dem offset
   transformiert                                                */
  if (turn_offset)
  {
    for (i = 0; i < 31; i++)
      temp[i] = 0;
    for (i = 0; i < 30; 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 < 31; i++)
      t_array[i] = temp[i];
  }
  if (other_offset)
  {
    for (i = 0; i < 31; i++)
      temp[i] = 0;
    for (i = 0; i < 30; 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 < 31; i++)
      other_array[i] = temp[i];
  }
  t_off = (int) floor(turn_offset);
  o_off = (int) floor(other_offset);

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