/*****************************************************************************/
/*									     */
/*									     */
/*	X patience - hints.c						     */
/*									     */
/*	written by Heiko Eissfeldt and Michael Bischoff			     */
/*									     */
/*	24-Feb-1993: First release (0.1)				     */
/*									     */
/*									     */
/*****************************************************************************/
#include "xpat.h"

#define BONUS_EMPTY_SLOT	100	/* Bonus for clearing a slot */
#define BONUS_TURN_CARD		10	/* Bonus for making a new card visible */

#define IS_SLOT(i)		(game.piletype[i] == Slot)

static const char *rank_names[] = {
    "Ace", "Deuce", "Three", "Four", "Five", "Six", "Seven",
    "Eight", "Nine", "Ten", "Jack", "Queen", "King" };

const char *rank_name(int rank)
{
    assert(rank >= 0 && rank <= 12);
    return rank_names[rank];
}

static const char *suit_names[] = {
    "Clubs", "Spades", "Hearts", "Diamonds" };

const char *suit_name(int suit)
{
    assert(suit >= 0 && suit <= 3);
    return suit_names[suit];
}



static Move lasthint;

void do_last_hint(void)
{   if (lasthint == NO_MOVE) {
	show_message("no hint was given!");
    } else {
	store_move(do_move(SRCIND(lasthint), DSTPILE(lasthint)));
    }
}

#define MAXHINTS 256
static struct hintstruct {
    int srcind, dstpile;
    int value;
} hinttab[MAXHINTS];
static int hints, showhint;	/* hints is number of hints in table (-1: unknown), showhint is index */

static int hintcmp(const void *a, const void *b)
{   return ((const struct hintstruct *)b)->value - ((const struct hintstruct *)a)->value;
}

/* ind == -1 => no marked cards, show all possible moves
    otherwise show move for block ind  */

static void collect_hints(Cardindex ind)
{
    int begsrcpile, endsrcpile;
    int pile;

    if (ind != ALL_HINTS) {
	begsrcpile = endsrcpile = getpile(ind);
    } else {
	begsrcpile = 0;
	endsrcpile = game.numpiles-2;	/* avoid facedown deck as source */
    }
    
    /* gather all possibilities, if necessary */
    hints = 0;
    for (pile = begsrcpile; pile <= endsrcpile; pile++) {
	int dstpile, uppercard, stacksrc;

	/* check all moves which go from this pile */
	/* if pile is empty, no moves are possible */
	if (EMPTY(pile))
	    continue;

	/* if there is a block selected already, use it */
	/* otherwise, take the maximum number of movable cards */
	uppercard = (ind == ALL_HINTS) ? select_max(pile, 0) : ind;
	if (game.piletype[pile] == Stack)
	    stacksrc = -1;	/* avoid stack to stack moves */
        else {
	    stacksrc = (*rules.stackable)(pile);
	    if (ind != ALL_HINTS && stacksrc != uppercard)
		stacksrc = -1;	/* block differs => no moves to stack */
	}

	for (dstpile = 0; dstpile < game.numpiles; ++dstpile) {
	    /* in a first step, reject some moves */
	    int srcindex;
	    if (pile == dstpile)
		continue;
	    switch (game.piletype[dstpile]) {
	    case Slot:
		if (EMPTY(dstpile)) {
		    if (!(rules.variant & EMPTY_KINGS))
			continue;  /* don't fill empty slots, if allowed for all */
		    /* now: ugly code! */
		    /* kings may be moved, if they don't come from a stack */
		    /* and they don't leave an empty slot */
		    if (game.piletype[pile] == Stack && ind == ALL_HINTS)
			continue;
		    if (game.piletype[pile] == Slot && game.ind[pile] == uppercard)
			continue;
		    /* king's move allowed! */
		}
		if (IS_JOKER(game.cards[INDEX_OF_LAST_CARD(dstpile)]))
		    continue;	    /* don't move to jokers */
		/* fall through */
		srcindex = uppercard;
		break;
	    case Tmp:
	    case FaceupDeck:
	    case FacedownDeck:
		continue;
	    case Stack:	/* for stacks, choose another srcindex */
		if (stacksrc == -1)
		    continue;
		srcindex = stacksrc;
		break;
	    default:
		srcindex = 0;	/* to keep compiler happy */
		exit(1);	/* cannot happen! */
	    }
	    if (move_valid(srcindex, dstpile)) {
		/* hint found. store source and destination, if acceptable  */
		if ((hinttab[hints].value = (*rules.good_hint)(srcindex, dstpile))) {
		    if (hinttab[hints].value < 2 && ind == ALL_HINTS) {
			/* don't suggest bad moves */
			continue;
		    }
		    hinttab[hints].srcind = srcindex;
		    hinttab[hints].dstpile = dstpile;
		    /* now some rule-independent stuff is added to value */
		    if (IS_SLOT(pile)) {
			if (game.ind[pile] == srcindex)
			    hinttab[hints].value += BONUS_EMPTY_SLOT;
			else if (!game.visible[srcindex-1])
			    hinttab[hints].value += BONUS_TURN_CARD;
		    }
		    if (++hints == MAXHINTS)
			break;
		}
	    }
	}
    }
    /* sort hints by relevance */
    qsort(hinttab, (size_t)hints, sizeof(hinttab[0]), hintcmp);
    showhint = hints;	/* will show either first or last hint */
}

/* this function returns a value (the higher the better) of the move done (or 0, if no move) */

int generic_automove(Cardindex srcindex)	/* pile is "movable" */
{   Pileindex i, srcpile;

    hint(RESET_HINTS);
    collect_hints(srcindex);
    if (hints) {
	store_move(do_move(hinttab[0].srcind, hinttab[0].dstpile));
	return hinttab[0].value;
    }
    /* no move to non-empty piles possible, try to move to empty slot */
#if 0
    if (rules.variant & EMPTY_KINGS) {
	Card src = game.cards[srcindex];
	if (!IS_JOKER(src) && RANK(src) != King)
	    return 0;
    }
#endif
    srcpile = getpile(srcindex);
    for (i = game.numpiles - 1; i >= 0; --i) {
	if (i == srcpile)
	    continue;
	if (game.piletype[i] == Tmp && game.piletype[srcpile] == Tmp)
	    continue;
	if (move_valid(srcindex, i)) {
	    store_move(do_move(srcindex, i));
	    return 1;
	}
#if 0
	switch (game.piletype[i]) {
	case Slot:
	    if (!EMPTY(i) && IS_JOKER(game.cards[INDEX_OF_LAST_CARD(i)])) {
		store_move(do_move(srcindex, i));
		return 1;	    /* move to jokers */
	    }
	case Tmp:
	    if (EMPTY(i)) {
		store_move(do_move(srcindex, i));
		return 1;	    /* move to jokers */
	    }
	    break;
	default:
	    ;
	}
#endif
    }
    hint(RESET_HINTS);	/* this should fix the hint bug! */
    return 0;	/* no move => extremely ugly */
}




static void display_hint(struct hintstruct *p)
{
    int dstpile, srcpile, srcval;
    char buf[180], srcbuf[20];

    dstpile = p->dstpile;
    srcpile = getpile(p->srcind);
    srcval = game.cards[p->srcind];

    switch (game.piletype[srcpile]) {
    case FaceupDeck:
	sprintf(srcbuf, "the deck");
	break;
    case Slot:
	sprintf(srcbuf, "slot %d", srcpile - rules.numstacks + 1);
	break;
    case Stack:
	sprintf(srcbuf, "stack %d", srcpile + 1);
	break;
    case Tmp:
	sprintf(srcbuf, "temp %d", srcpile - rules.numstacks - rules.numslots + 1);
	break;
    default:
	abort();
    }
    switch (game.piletype[dstpile]) {
    case Stack:
        sprintf(buf, "You can move %s (%s %s) to stack %d",
                     srcbuf,
                     rank_names[RANK(srcval)], suit_names[SUIT(srcval)],
                     dstpile + 1);
	break;
    case Slot:
        sprintf(buf, "You can move %s (%s %s) to slot %d",
                     srcbuf,
                     rank_names[RANK(srcval)], suit_names[SUIT(srcval)],
                     dstpile - rules.numstacks + 1);
	break;
    case Tmp:
        sprintf(buf, "You can move %s (%s %s) to temp %d",
                     srcbuf,
                     rank_names[RANK(srcval)], suit_names[SUIT(srcval)],
                     dstpile - rules.numstacks -rules.numslots + 1);
	break;
    default:
	abort();
    }
    /* sprintf(buf+strlen(buf), ", value %d", value); */
    show_message(buf);

    game.arrow_srcind = p->srcind;
    game.arrow_dstpile = p->dstpile;
    show_arrow(True);

    lasthint = MOVE(p->srcind, p->dstpile);
}


static int forward = 1;

int hint(Cardindex ind)
{
    show_arrow(False);	/* if any arrow visible, clear it */

    if (ind == RESET_HINTS) {
	hints = showhint = -1;
	lasthint = NO_MOVE;
	return 0;
    }
    if (hints < 0)
	collect_hints(ind);

    if (!hints) {
        show_message("no hints");
	return 0;
    } else {
	showhint = (forward ? (showhint + 1) : (showhint + hints)) % (hints+1);
	if (showhint == hints)
	    show_message("(wrapping)");
	else
            display_hint(hinttab + showhint);
	return 1;
    }
}


void do_all_moves(void)
{   forward = 1;
    hint(RESET_HINTS);
    while (hint(ALL_HINTS)) {
	/* XFlush(dpy); */
	do_last_hint();
	/* XFlush(dpy); */
    }
}


void do_give_hints(void)	/* show next hint in queue */
{   forward = 1;
    hint(game.srcind);		/* since (-1) is ALL_HINTS */
}

void do_give_prev_hint(void)	/* show previous hint */
{   forward = 0;
    hint(game.srcind);		/* since (-1) is ALL_HINTS */
}
