/*  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 <X11/Intrinsic.h>	/* for the BOARD */
#include "gammon.h"

/* check if a move is allowed */

int          max_depth, 		/* max of current_depth */
	     current_depth = 0, 	/* current depth of move */
	     written_incomplete;	/* flag if there are valid moves
					   before there is a move with all
					   dice used */
int          done_yet;			/* stones set yet */
extern int   done_hit;			/* stones hit (for UndoMove() only) */
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       (MOVE current_move[], 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 stop_pin,			/* last pin with stones of color */
        rest_dice = dice_to_set, 	/* are there dice to set */
        rest[4],			/* of dice to set */
        endgame = 0,			/* the game is in the endgame */
        hit,				/* hit some stones ? */
        diffdice, 			/* difference of dice values */
        maxpin;				/* first pin with stones of color */
    int i, j, k; 
    static MOVE latest[4] = {{0, 0}, {0, 0}, 
			     {0, 0}, {0, 0}}; /* latest move examined */

    if (actual_pin == end_pin) return dice_to_set;   /* all done */

    maxpin = start_pin;				     /* lookup first stone of color */
#ifdef DEBUG_POSSIBLE_MOVES
    printf("max- startpin: %d\n", maxpin);
#endif
    while (!(Pin[maxpin].count &&
	     Pin[maxpin].color == turn)) 
		maxpin += direction;
#ifdef DEBUG_POSSIBLE_MOVES
    printf("maxpin: %d ", maxpin);
#endif

    if      (turn == BLACK && maxpin > 18) endgame = 1;
    else if (turn == WHITE && maxpin < 7)  endgame = 1;


    if (Pin[BAR].count)				/* where to stop */
        stop_pin = BAR + direction;
    else stop_pin = end_pin;			/* stop_pin letzter besetzter von machen */
  
    if (actual_pin == stop_pin) return dice_to_set; /* at the end */ 

    current_depth ++;
  
    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;
        }
    }

#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 {
            if ( ((turn==BLACK) ? maxpin+w[0] >= end_pin : maxpin+w[0] <= end_pin) &&
                 ((turn==BLACK) ? maxpin+w[1] >= end_pin : maxpin+w[1] <= end_pin) ) 
	        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 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;
	           }
	      } else {					/* endgame move */
	           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. move is complete */
	          if (written_incomplete) {
	              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 decided 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 */
	              if (j==0)  rest_dice = create_possible_moves (dice_to_set-1, rest, i);
	              else {
	                 rest_dice = create_possible_moves (dice_to_set-1, rest, i+direction);
	              }
	              if ((current_depth-1) >= max_depth) { 
	                  written_incomplete = current_depth;   /* flag to delete this moves if dice can be set comlete later (try figure out why!) */
	                  max_depth          = current_depth-1; /* max number of dice set yet */
	                  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;
	              }
	          }
              }		/* have found a movable stone */
          }		/* for all dice */
      }		/* for the board */
      current_depth--;
      return rest_dice;
}

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

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

#ifdef WHATS_MAX_NUMBER
    {
    static int maxnumber=0;
    FILE *fid;   
    if ((list-possible_moves)/4 > maxnumber) {
        maxnumber = (list-possible_moves)/4;
        fid = fopen("maxpos.xgammon", "a");
        fprintf(fid," %d \n",maxnumber);
        fclose(fid);
    }
    }
#endif

#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 */

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

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

#ifdef DEBUG_TM
    fprintf (stderr, "to_move %d\n", to_move);
#endif

    /* 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;
	MOVE *m, *e = possible_moves;
	
	for (m=possible_moves; m!=list; m+=4) {
		if (m->to - m->from == bigger_value) {
			have_bigger = 1;
			break;
		}
	}

	if (have_bigger) {	
		for (m=possible_moves; m!=list; m+=4) {
			if (m->to - m->from == bigger_value) {
				e->from = m->from;
				e->to   = m->to;
				e += 4;
			}
		}
		list = e;
	}
    }

    possible_move_count = (list - possible_moves) / 4;

    return possible_move_count;
}

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

int current_in_rest (MOVE current_move[], 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)
{
    MOVE *m, rest_move[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
    int  old_done_yet = done_yet;
    int  allow = 0, from, i, j, k;

    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, that the move is allowed and 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;
}
