/*****************************************************************************/ /* */ /* */ /* X patience version 2 -- module hints.c */ /* */ /* Functions for the ``display hint'' part */ /* written by Heiko Eissfeldt and Michael Bischoff */ /* see COPYRIGHT.xpat2 for Copyright details */ /* */ /* */ /*****************************************************************************/ #include "xpatgame.h" #define RESET_HINTS (-2) #define ALL_HINTS (-1) static Move lasthint; void cmd_DoHint(void) { if (lasthint == NO_MOVE) { show_message(TXT_NOHINTGIVEN); } 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-1; } /* gather all possibilities, if necessary */ hints = 0; for (pile = begsrcpile; pile <= endsrcpile; pile++) { int dstpile, uppercard, stacksrc, havestack; /* check all moves which go from this pile */ /* if pile is empty, no moves are possible */ if (EMPTY(pile)) continue; if (pile == IDECK && !(rules.variant & DECK_VISIBLE)) continue; /* if there is a block selected already, use it */ /* otherwise, take the maximum number of movable cards */ uppercard = (ind == ALL_HINTS) ? maxsequence(pile, INDEX_OF_FIRST_CARD(pile)) : ind; if (game.piletype[pile] == Stack) stacksrc = -1; /* avoid stack to stack moves */ else { stacksrc = stackable(pile); if (ind != ALL_HINTS && stacksrc != uppercard) stacksrc = -1; /* block differs => no moves to stack */ } havestack = 0; for (dstpile = 0; dstpile < game.numpiles; ++dstpile) { /* in a first step, reject some moves */ int srcindex; if (rules.variant & HINTS_LESSER) if (pile <= dstpile) break; if (pile == dstpile) continue; switch (game.piletype[dstpile]) { case Slot: if (EMPTY(dstpile)) { /* Hints for Blue Moon */ if ((rules.move_bits & ES_MASK) == ES_NONE) { srcindex = uppercard; break; } if ((rules.move_bits & ES_MASK) != ES_KINGS && (rules.move_bits & ES_MASK) != ES_CLUBS) 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 || havestack) continue; srcindex = stacksrc; break; default: srcindex = 0; /* to keep compiler happy */ exit(EXIT_FAILURE); /* cannot happen! */ } if (move_valid(srcindex, dstpile)) { /* hint found. store source and destination, if acceptable */ hinttab[hints].value = rules.good_hint ? (*rules.good_hint)(srcindex, dstpile) : 100; /* 100 is default value */ if (hinttab[hints].value) { if (hinttab[hints].value < 2 && ind == ALL_HINTS) { /* don't suggest bad moves */ continue; } /* accept the hint */ if (game.piletype[dstpile] == Stack && !(rules.variant & STACKS_MULTI)) havestack = 1; hinttab[hints].srcind = srcindex; hinttab[hints].dstpile = dstpile; 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; cmd_ResetHints(); 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 */ 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; } } cmd_ResetHints(); /* MUST reset the hints! */ return 0; /* no move => extremely ugly */ } static void mysprintf(char *p, const char *fmt, ...) { va_list args; va_start(args, fmt); while (*fmt) { if (*fmt != '%') *p++ = *fmt++; else { int arg; arg = va_arg(args, int); switch (*++fmt) { case 'R': strcpy(p, TXT_RANK[RANK(arg)]); p += strlen(p); break; case 'S': strcpy(p, TXT_SUIT[SUIT(arg)]); p += strlen(p); break; case 'P': switch(game.piletype[arg]) { case FaceupDeck: strcpy(p, TXT_DECK); break; case FacedownDeck: strcpy(p, TXT_IDECK); break; case Slot: strcpy(p, TXT_SLOT); p += strlen(p); sprintf(p, " %d", arg - rules.numstacks + 1); break; case Stack: strcpy(p, TXT_STACK); p += strlen(p); sprintf(p, " %d", arg + 1); break; case Tmp: strcpy(p, TXT_TMP); p += strlen(p); sprintf(p, " %d", arg-rules.numstacks-rules.numslots + 1); break; } p += strlen(p); break; default: *p++ = *fmt; } ++fmt; } } va_end(args); *p = '\0'; } static void display_hint(struct hintstruct *p) { int dstpile, srcpile, srcval; char buf[180]; dstpile = p->dstpile; srcpile = getpile(p->srcind); srcval = game.cards[p->srcind]; mysprintf(buf, TXT_CANMOVE, srcpile, srcval, srcval, dstpile); show_message(buf); show_arrow(1); } static int forward = 1; static int hint(Cardindex ind, boolean dodisplay) { show_arrow(0); /* if any arrow visible, clear it */ if (ind == RESET_HINTS) { hints = showhint = -1; game.arrow_srcind = -1; game.arrow_dstpile = -1; lasthint = NO_MOVE; return 0; } /* printf("hints = %d\n", hints); */ if (hints < 0) collect_hints(ind); if (!hints) { if (dodisplay) show_message(TXT_NOHINTS); return 0; } else { showhint = (forward ? (showhint + 1) : (showhint + hints)) % (hints+1); if (showhint == hints) show_message(TXT_WRAPPING); else { struct hintstruct *p; p = hinttab + showhint; game.arrow_srcind = p->srcind; game.arrow_dstpile = p->dstpile; lasthint = MOVE(p->srcind, p->dstpile); if (dodisplay) display_hint(p); } return 1; } } void cmd_ResetHints(void) { hint(RESET_HINTS, 0); } void cmd_AllMoves(void) { forward = 1; hint(RESET_HINTS, 0); while (hint(ALL_HINTS, 0)) { /* XFlush(dpy); */ cmd_DoHint(); /* XFlush(dpy); */ } } void cmd_NextHint(void) { /* show next hint in queue */ forward = 1; if (game.srcind < 0) hint(ALL_HINTS, 1); else hint(game.srcind, 1); } void cmd_PreviousHint(void) { /* show previous hint */ forward = 0; if (game.srcind < 0) hint(ALL_HINTS, 1); else hint(game.srcind, 1); }