/*  allow.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 "gammon.h"

/* check if a move is allowed */

int max_depth, current_depth = 0, written_incomplete;
int done_yet;
extern int done_hit;	/* for UndoMove() only */
struct _Move current_move[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; /* global only for un_do() */

int test_move (void);
int create_possible_moves (int dice_to_set, int *w, int actual_pin);
int move_is_allowed (int from_pin, int to_pin);
int current_in_rest(struct _Move current_move[], struct _Move rest_move[]);
void print_possible_moves (void);

extern void AppendMoveString ();;
extern int have_to_hit ();

#define OFF_BOARD ((turn==BLACK) ? i+w[j] >= end_pin : i+w[j] <= end_pin)
#define aim i+w[j]

int 
create_possible_moves (int dice_to_set, int *w, int actual_pin)
{
  int i, j, k, rest_dice = dice_to_set, rest[4];
  int stop_pin;
  int endgame = 0, hit;
  int diffdice, maxpin;
  static struct _Move latest[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};

  current_depth++;

  maxpin = start_pin;
#ifdef DEBUG_POSSIBLE_MOVES
  printf("maxpin: %d ", maxpin);
#endif
  while (!(Pin[maxpin].count && Pin[maxpin].color == turn)) maxpin += direction;
  if      (turn == BLACK && maxpin > 18) endgame = 1;
  else if (turn == WHITE && maxpin < 7)  endgame = 1;
#ifdef DEBUG_POSSIBLE_MOVES
  printf("maxpin: %d\n", maxpin);
#endif

  while (!(Pin[actual_pin].count && Pin[actual_pin].color == turn)) {
    actual_pin += direction;
    if (actual_pin == end_pin) {
      current_depth --;		/* don't forget */
      return dice_to_set;
      }
  }

  if (Pin[BAR].count) stop_pin = BAR + direction;
  else stop_pin = end_pin;	/* stop_pin letzter besetzter von machen */

#ifdef DEBUG_POSSIBLE_MOVES
  printf("stop pin: %d\n", stop_pin);
#endif

  diffdice = dice_to_set;
  if (dice_to_set > 2) diffdice = 1;
  if (dice_to_set == 2) {
    if (w[0] == w[1]) diffdice = 1;
    else diffdice = 2;
  }

  for (i = actual_pin; i != stop_pin; i += direction) {
    int break_flag = 0;
    while (Pin[i].color != turn) {
      i += direction;
      if (i == stop_pin) {
	break_flag = 1;
	break;
      }
    }
    if (break_flag) break;

    for (j = 0; j < diffdice; j++) {	/* set the stones from there */
      hit = 0;
      if ((!OFF_BOARD && !(Pin[aim].color == other && Pin[aim].count > 1))	/* no other color there */
	 ||(OFF_BOARD && ((endgame && i == maxpin) || (endgame && aim == end_pin)))) {	/* case not on board a finish? */
	/* have a move */
	if (!OFF_BOARD) {			/* no endgame */
	  if (Pin[aim].color == other) {	/* hit other */
	    hit = 1;
	    Pin[aim].color = turn;
	    Pin[i].count--;
	    if (Pin[i].count == 0) Pin[i].color = 0;
	  }
	  else {				/* no hit */
	    Pin[aim].count++;
	    Pin[aim].color = turn;
	    Pin[i].count--;
	    if (Pin[i].count == 0) Pin[i].color = 0;
	  }
	}					/* endgame move */
	else {
	  Pin[i].count--;
	  if (Pin[i].count == 0) Pin[i].color = 0;
	}

	for (k = 0; k < 4; k++) {		/* delete dice */
	  if (k < j) rest[k] = w[k];
	  if (k > j) rest[k - 1] = w[k];
	}
	rest[3] = 0;

	latest[to_move - dice_to_set].from = i;	/* possible entry in possible_moves */
	if (!OFF_BOARD) latest[to_move - dice_to_set].to = aim;
	else            latest[to_move - dice_to_set].to = end_pin;

	if (dice_to_set == 1) {			/* last dice set i.e. a move is complete */
	  if (written_incomplete) {
#ifdef DEBUG_POSSIBLE_MOVES
  printf("delete move cause of written_incomplete\n");
#endif
	    list = possible_moves;
	    written_incomplete = 0;
	  }
	  for (k = 0; k < to_move; k++) {	/* enter in list */
	    list->from = latest[k].from;
	    list->to   = latest[k].to;
	    list++;
	  }
	  for (k = to_move; k < 4; k++) {	/* we dicided that a move has alaways 4 parts so case no pash append zeros */
	    list->from = 0;
	    list->to = 0;
	    list++;
	  }
	  rest_dice = 0;
	  max_depth = to_move;
	}
	else {					/* move still incomplete */
	  rest_dice = create_possible_moves (dice_to_set - 1, rest, i);
	  if ((current_depth-1) >= max_depth) { 
	    written_incomplete = current_depth; /* flag to delete this moves if dice can be set comlete later
						   (if you understand this shit, try figure out why!) */
	    max_depth = current_depth-1;
	    for (k = 0; k < current_depth; k++) { /*incomplete possible_move entry */
	      list->from = latest[k].from;
	      list->to = latest[k].to;
	      list++;
	    }
	    for (k = current_depth; k < 4; k++) {
	      list->from = 0;
	      list->to = 0;
	      list++;
	    }
	  }
	}			/* end incomplete move */

	Pin[i].count++;		/* restore the board */
	if (Pin[i].count == 1) Pin[i].color = turn;
	if (!OFF_BOARD) {
	  if (hit) {
	    Pin[aim].color = other; 
	  }
	  else {
	    Pin[aim].count--;
	    if (Pin[aim].count == 0) Pin[aim].color = 0;
	  }
	}

      }				/* end of have found a movable stone */

    }				/* for all dice */
  }				/* for the board */
  current_depth--;
  return (rest_dice);
}

int 
test_move (void)
{
  int move_depth;
  int k;
  int possible_move_count;

  max_depth = 0;
  current_depth = 0;
  written_incomplete = 0;
  list = possible_moves;
  move_depth = create_possible_moves (to_move, roll, start_pin);

#ifdef DEBUG_POSSIBLE_MOVES
  printf("maxdepth: %d\n", max_depth);
  printf("movedepth: %d\n", move_depth);
  print_possible_moves ();
#endif

/* check how many dice can be used
   this value should be returned by create_possible_moves() but doesn't */

  k = 0;
  while (k < 4) {
    if ((possible_moves + k)->from == 0 && (possible_moves + k)->to == 0) break;
    k++;
  }

  if (k < to_move) {
    to_move = k;
    if (k == 0) roll[0] = 0;		/* global move done */
  }

  /* check for use of greater dice value */
  if (to_move == 1 && !pash) {
	int bigger_value = (abs(roll[0]) > abs(roll[1])) ? roll[0] : roll[1];
	int have_bigger = 0;
	struct _Move *m = possible_moves, *e = possible_moves;
	
	while ( m!= list) {
		if (m->to - m->from == bigger_value) {
			have_bigger = 1;
			break;
			}
		m+=4;
		}

	if (have_bigger) {	
#ifdef DEBUG_POSSIBLE_MOVES
  printf("delete move cause of use bigger value\n");
#endif
		m = possible_moves;
		while ( m!= list) {
			if (m->to - m->from == bigger_value) {
				e->from = m->from;
				e->to   = m->to;
				e += 4;
			}
			m += 4;
		}
		list = e;
	}
  }

  possible_move_count = (list - possible_moves) / 4;


  return possible_move_count;
}

void print_possible_moves(void)
{
  int k;
  struct _Move *d;
  d = possible_moves;
  while (d != list) {
    for (k = 0; k < 4; k++) {
      printf ("%2d -> %2d  ", d->from, d->to);
      d++;
    }
    printf ("\n");
  }
  printf ("count = %d\n", (list - possible_moves) / 4);

}

int current_in_rest(struct _Move current_move[], struct _Move rest_move[])
{
 int i, j, possible = 1;

 for (i=0; i<done_yet; i++) {
  possible = 0;
  for (j=0; j<to_move; j++) {
   if ( current_move[i].from == rest_move[j].from && current_move[i].to == rest_move[j].to ) {
    rest_move[j].from = rest_move[j].to = 0;
    possible = 1;
    break;
   }
  }
  if (!possible) break;
 }
return possible;
}

int move_is_allowed (int from_pin, int to_pin)
{
  struct _Move *m;
  int old_done_yet = done_yet;
  struct _Move rest_move[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
  int i,j, k, allow = 0, from;

  if (to_pin == from_pin)		 return 0;
  if (Pin[BAR].count && from_pin != BAR) return 0;	/* have a stone there */
  if (to_pin == BAR)			 return 0;	/* want to  set there (doesn't happen) */

  m = possible_moves;
  while (m != list && !allow) { /* this loop checks not compound moves */

    for (k=0;k<to_move;k++) {
     if ((m+k)->from == from_pin && (m+k)->to == to_pin) {
      for (j=0;j<to_move;j++) if (j != k)  rest_move[j] = *(m+j); 
      if (current_in_rest(current_move,rest_move)) {
        allow = 1;
        current_move[done_yet].from = (m+k)->from;
        current_move[done_yet].to   = (m+k)->to;
        done_yet++;
       } 

       break; 
     }
    }
    m += 4;
  }

if (!allow) {
m = possible_moves;


while (!allow && m!= list) {		/* test compound move */

 from = from_pin;
 for (k=0; k<to_move; k++) {
  if (!allow && (m+k)->from == from) {
   rest_move[k].from = rest_move[k].to = 0;
   if ((m+k)->to == to_pin) {
    allow = 1;
   }
   else from = (m+k)->to;
  }
  else {
    rest_move[k].from = (m+k)->from;
    rest_move[k].to   = (m+k)->to;
  }
 }
 /* at this point it is clear, if the move is allowed and if there
    is anything left over to move */

 if (allow) {
   allow = current_in_rest(current_move,rest_move);
 }

 /* if current_in_rest() returns true, there is a possibible_move containing
    the already moved stones and the current move, so we can break 
    the while loop  */

 if (!allow) m+=4;
 } /* while !allow && list */

  if (allow ) {
   if (have_to_hit (from_pin, to_pin)) return 0; /* have a compound move and on between to hit a stone */
   from = from_pin;
   for (k=0;k<to_move;k++) {
    if ((m+k)->from == from) {
     current_move[done_yet].from = (m+k)->from;
     current_move[done_yet].to   = (m+k)->to; 
     done_yet++;
     if ((m+k)->to == to_pin) { break; }
     else from = (m+k)->to;
    }
   }
  }

 } /* test compound move */

if (allow) {
    if (done_yet == to_move) {
      AppendMoveString (current_move);
      for (i = 0; i < 4; i++) {
	current_move[i].from = 0;
	current_move[i].to = 0;
      }
      done_yet = 0;
      done_hit = 0;
      roll[0] = 0;		/* reckon global */
      return 1;
    }

  }
  else {	/* !allow */
    done_yet = old_done_yet;
  }

  return allow;
}
