home *** CD-ROM | disk | FTP | other *** search
- /*
- * Spider
- *
- * (c) Copyright 1989, Donald R. Woods and Sun Microsystems, Inc.
- * (c) Copyright 1990, David Lemke and Network Computing Devices Inc.
- *
- * See copyright.h for the terms of the copyright.
- *
- * @(#)movelog.c 2.3 91/05/09
- *
- */
-
- /*
- * move logging code & save/restore
- */
-
- #include "defs.h"
- #include "globals.h"
- #include <string.h>
- #include <sys/file.h>
- #include <ctype.h>
- #ifdef SVR4
- #include <unistd.h>
- #endif
-
- #define CACHE_SIZE 50
- static int move_index = 0;
- static int *move_cache = NULL;
- static int cache_size = 0;
-
- int deck_cache[NUM_CARDS];
- int deck_index = 0;
-
- extern int cheat_count;
-
- make_deck_cache()
- {
- CardPtr tmp;
-
- deck_index = 0;
- tmp = deck->cards;
- while(tmp) {
- deck_cache[deck_index++] = card_to_int(tmp);
- tmp = tmp->next;
- }
- }
-
- init_cache()
- {
- if (cache_size)
- free((char *)move_cache);
- move_cache = (int *) calloc(CACHE_SIZE, sizeof(int));
- cache_size = CACHE_SIZE;
- move_index = 0;
- }
-
- grow_cache()
- {
- int *new;
-
- cache_size += CACHE_SIZE;
- new = (int *) realloc((char *)move_cache,
- (unsigned)(cache_size * sizeof(int)));
- if (new == (int *) NULL) {
- (void)fprintf(stderr,"realloc failed\n");
- exit(-1);
- }
- move_cache = new;
- }
-
- record(from, dest, num_cards, exposed)
- int from, dest, num_cards;
- Bool exposed;
- {
- int val;
-
- val = 11 * ( 11 * ((exposed ? 1 : 0) * 14 + num_cards) + dest) + from;
- move_cache[move_index++] = val;
- if (move_index >= cache_size)
- grow_cache();
- }
-
- unencode(val, from, dest, num_cards, exposed)
- int val;
- int *from, *dest, *num_cards;
- Bool *exposed;
- {
- *from = val % 11;
- val /= 11;
- *dest = val % 11;
- val /= 11;
- *num_cards = val % 14;
- *exposed = (val >= 14);
- }
-
- undo()
- {
- int val;
- int from, dest, num_cards;
- Bool exposed;
-
- if (move_index == 0) {
- show_message("No moves available to back up over.");
- spider_bell(dpy, 0);
- return;
- }
- val = move_cache[--move_index];
- if (val > 11 * 11 * 14) {
- show_message("Cheater!");
- cheat_count++;
- spider_bell(dpy, 0);
- }
- unencode(val, &from, &dest, &num_cards, &exposed);
-
- if (from == 0) { /* undo deal */
- undo_deal();
- show_message("Cheater!");
- cheat_count++;
- spider_bell(dpy, 0);
- } else {
- /*
- * note that cols are from 0-9, but saved as 1-10,
- * so we decrement here before doing the work
- */
- if (dest == 0) {
- undo_suit(from - 1, exposed);
- } else {
- undo_normal(from - 1, dest - 1, num_cards, exposed);
- }
- }
- }
-
- /*
- * undoes latest deal
- */
- undo_deal()
- {
- int i;
- CardPtr tmp;
-
- for (i = (NUM_STACKS - 1); i >= 0; i--) {
- tmp = last_card(stack[i]);
- remove_card(tmp);
- add_card(tmp, deck->cards, LOC_BEFORE, deck);
- flip_card(tmp, Facedown);
-
- /* fix up stack */
- if (stack[i]->card_delta != CARD_DELTA) {
- recompute_list_deltas(stack[i]);
- show_list(stack[i], stack[i]->cards);
- } else {
- show_list(stack[i], last_card(stack[i]));
- }
- }
- deal_number--;
- deck_index += 10;
- }
-
- /*
- * pulls suit back down from pile
- */
- undo_suit(from, exposed)
- int from;
- Bool exposed;
- {
- int i;
-
- if (exposed)
- flip_card(last_card(stack[from]), Facedown);
-
- for (i = (NUM_PILES - 1); i >= 0; i--) {
- if (piles[i]->cards) {
- move_to_list(piles[i]->cards, stack[from], False);
- break;
- }
- }
- }
-
- undo_normal(from, dest, num_cards, exposed)
- int from, dest, num_cards;
- Bool exposed;
- {
- CardPtr tmp;
-
- if (exposed)
- flip_card(last_card(stack[from]), Facedown);
- tmp = last_card(stack[dest]);
-
- /* get start of run to move */
- while (--num_cards) {
- tmp = tmp->prev;
- }
-
- move_to_list(tmp, stack[from], False);
- }
-
- /* file I/O */
-
- /*
- * this was teken from the NeWS version of spider, which copied
- * it verbatim from the Mesa version, so they'll all work together.
- * but the results of 2 language translations gets a bit messy...
- */
-
- static int hash_card();
- static int char_index = 0;
- static int prev_hash = 0;
-
- #define EOS 0xffff /* end of seq flag */
- #define FUS 0xeeee /* flag to say rest of seq is faceup */
-
- static int
- hash_card(val)
- int val;
- {
- int ret;
-
- char_index += 5;
-
- ret = ((val ^ prev_hash) ^ (char_index % 4) + (char_index * 4)) & 077;
-
- return (ret);
- }
-
- static int*
- read_sequence(str)
- char *str;
- {
- int i, len;
- int *seq, *seqp;
- int val;
-
- len = strlen(str);
- /* need number of cards plus EOS and FUS */
- seqp = seq = (int *)calloc((unsigned)(len + 2), sizeof(int));
- if (strchr(str, '/') == NULL) { /* if no '/', all are faceup */
- *seqp++ = FUS;
- }
- for (i = 0; i < len; i++) {
- if (str[i] == '/') { /* flag '/' */
- *seqp++ = FUS;
- continue;
- }
- if (!(str[i] >= '0' && str[i] <= 'o')) {
- return NULL;
- }
- val = (int) (str[i] - '0');
- val = hash_card(val);
- prev_hash = val;
- *seqp++ = ((13 - val/4) + ((3 - val % 4) * 13)) % 52;
- }
- *seqp = EOS;
- return (seq);
- }
-
- static void
- write_sequence(fp, seq)
- FILE *fp;
- int *seq;
- {
- int *seqp;
- char c;
- int val;
-
- seqp = seq;
- while (*seqp != EOS) {
- if (*seqp == FUS) {
- (void)fputc('/', fp);
- seqp++;
- continue;
- }
- val = *seqp;
- val = (3 - val/13) + ((13 - val % 13) * 4);
- c = (char) hash_card(val) + '0';
- prev_hash = val;
- assert (c >= '0' && c <= 'o');
- /* depend on file buffering to make this efficient */
- (void)fputc(c, fp);
- seqp++;
- }
- }
-
- Rank flip_ranks[NUM_RANKS] = {
- King, Queen, Jack, Ten, Nine, Eight, Seven,
- Six, Five, Four, Three, Deuce, Ace
- };
-
- static void
- int_to_card(val, suit, rank)
- int val;
- Suit *suit;
- Rank *rank;
- {
- *suit = val / 13;
- *rank = val % 13;
- *rank = flip_ranks[*rank];
- assert (*suit >= Spade && *suit <= Club);
- assert (*rank >= Ace && *suit <= King);
- }
-
- static int
- card_to_int(card)
- CardPtr card;
- {
- int val;
-
- val = card->suit * 13 + flip_ranks[card->rank];
- return (val);
- }
-
- static CardPtr
- find_card(cache, suit, rank)
- CardPtr *cache;
- Suit suit;
- Rank rank;
- {
- int i;
- CardPtr tmp = CARDNULL;
-
- for (i = 0; i < NUM_CARDS; i++) {
- if (cache[i] == CARDNULL)
- continue;
- else if ((cache[i]->suit == suit) &&
- (cache[i]->rank == rank)) {
- tmp = cache[i];
- cache[i] = CARDNULL;
- break;
- }
- }
- return (tmp);
- }
-
- /*
- * get all the cards back in the deck to recover from something
- * evil in the restore process
- */
- static void
- recover(cache)
- CardPtr *cache;
- {
- int i;
-
- for (i = 0; i < NUM_CARDS; i++) {
- if (cache[i]) {
- cache[i]->type = Facedown;
- add_card(cache[i], deck->cards, LOC_END, deck);
- }
- }
- }
-
- static int
- read_position(str)
- char *str;
- {
- char *tmp;
- char *card_str, *cards_left;
- int *seq, *seqp;
- CardPtr card;
- CardPtr cache[NUM_CARDS];
- Suit suit;
- Rank rank;
- Type type;
- int i, num_dealt, num_undealt;
- int dealt[NUM_CARDS], undealt[NUM_CARDS];
-
- prev_hash = 0;
- char_index = 0;
- deck_index = 0;
-
- /* stash all cards temporarily */
- remove_all_cards(cache);
-
- /* deck is everything up to a ' ' */
- tmp = strchr(str, ' ');
- if (tmp == NULL) { /* bad data */
- recover(cache);
- return (-1);
- }
- card_str = calloc((unsigned)(tmp - str + 1), 1);
- (void)strncpy(card_str, str, tmp - str);
-
- /*
- * first read in the hand. first come the undealt cards, then
- * the dealt cards. the first undealt is the next one that
- * will be dealt, and the first dealt card is the first card that
- * was dealt
- */
- seq = read_sequence(card_str);
- if (seq == NULL) {
- recover(cache);
- return (-1);
- }
- free(card_str);
- seqp = seq;
- num_undealt = 0;
- while (*seqp != FUS) { /* stop at dealt cards */
- undealt[num_undealt++] = *seqp;
- int_to_card(*seqp, &suit, &rank);
- card = find_card(cache, suit, rank);
- if (card == CARDNULL) {
- recover(cache);
- return (-1);
- }
- card->type = Facedown;
- add_card(card, deck->cards, LOC_END, deck);
- seqp++;
- }
- seqp++;
-
- num_dealt = 0;
- while (*seqp != EOS) {
- dealt[num_dealt++] = *seqp;
- seqp++;
- }
- assert (num_dealt + num_undealt == NUM_CARDS);
-
- /* reverse the dealt cards and stick them in cache */
- while (--num_dealt >= 0) {
- deck_cache[deck_index++] = dealt[num_dealt];
- }
- for (i = 0; i < num_undealt; i++) {
- deck_cache[deck_index++] = undealt[i];
- }
- assert(deck_index == NUM_CARDS);
- deck_index = num_undealt;
- /* reset deal_number correctly */
- deal_number = 6 - deck_index/10;
-
- free((char *)seq);
-
- /*
- * now deal with the stacks
- */
- cards_left = tmp;
- cards_left++; /* skip space */
-
- /* deal with each stack */
- for (i = 0; i < 10; i++) {
- stack[i]->card_delta = CARD_DELTA; /* restore delta */
- type = Facedown;
- tmp = strchr(cards_left, ' ');
-
- /* deal with '.' at end of string */
- if (tmp == NULL) {
- tmp = strchr(cards_left, '.');
- if (tmp == NULL) { /* bad data */
- recover(cache);
- return (-1);
- }
- }
- if (tmp == cards_left) { /* empty col */
- cards_left++;
- continue;
- }
-
- /* make local copy of stack */
- card_str = calloc((unsigned)((tmp - cards_left) + 1), 1);
- (void)strncpy(card_str, cards_left, tmp - cards_left);
- cards_left = tmp + 1;
-
- seqp = seq = read_sequence(card_str);
- if (seq == NULL) {
- recover(cache);
- return (-1);
- }
- while (*seqp != FUS && *seqp != EOS) {
- int_to_card(*seqp, &suit, &rank);
- card = find_card(cache, suit, rank);
- if (card == CARDNULL) {
- recover(cache);
- return (-1);
- }
- card->type = type;
- add_card(card, stack[i]->cards, LOC_END, stack[i]);
- seqp++;
- }
-
- /* handle faceup cards */
- if (*seqp != EOS) {
- seqp++; /* skip FUS */
- type = Faceup;
- while (*seqp != EOS) {
- int_to_card(*seqp, &suit, &rank);
- card = find_card(cache, suit, rank);
- if (card == CARDNULL) {
- recover(cache);
- return (-1);
- }
- card->type = type;
- add_card(card, stack[i]->cards, LOC_END,
- stack[i]);
- seqp++;
- }
- }
- free((char *)seq);
- free(card_str);
- }
-
- /* deal with remaining stuff -- should all be piles */
- if (*tmp != '.') {
- assert (*tmp == ' ');
- tmp = strchr(cards_left, '.');
- if (tmp == NULL) { /* bad data */
- recover(cache);
- return (-1);
- }
- /* make local copy of piles */
- card_str = calloc((unsigned)((tmp - cards_left) + 1), 1);
- (void)strncpy(card_str, cards_left, tmp - cards_left);
- cards_left = tmp + 1;
- type = Faceup;
-
- seqp = seq = read_sequence(card_str);
- if (seq == NULL) {
- recover(cache);
- return (-1);
- }
- i = 0;
- while (*seqp != EOS) {
- if (*seqp == FUS) {
- seqp++;
- continue;
- }
- int_to_card(*seqp, &suit, &rank);
- assert(rank == Ace);
- for (rank = Ace; rank <= King; rank++) {
- card = find_card(cache, suit, rank);
- if (card == CARDNULL) {
- recover(cache);
- return (-1);
- }
- card->type = type;
- add_card(card, piles[i]->cards, LOC_START,
- piles[i]);
- }
- seqp++;
- i++;
- }
- }
-
- return (0);
- }
-
- static void
- read_moves(str)
- char *str;
- {
- char *s;
-
- s = str;
- while (*s != '.') {
- move_cache[move_index++] = (int)(*s - '0') * 64 +
- (int)(*(s+1) - '0');
- assert (move_cache[move_index - 1] >= 0);
- if (move_index >= cache_size)
- grow_cache();
- s += 2;
- }
- }
-
- static void
- write_moves(fp)
- FILE *fp;
- {
- int i;
- int val;
-
- for (i = 0; i < move_index; i++) {
- val = move_cache[i];
- (void)fputc((char)(val/64 + '0'), fp);
- (void)fputc((char)(val%64 + '0'), fp);
- }
- }
-
- write_file(fname, confirmer)
- char *fname;
- Bool (*confirmer)();
- {
- FILE *fp;
- char buf[512];
- int i, num;
- int seq[1024], *seqp;
- CardPtr tmp;
-
- fname = remove_newlines(fname);
-
- if (access(fname, F_OK) == 0) {
- if ((*confirmer)()) {
- show_message("Overwriting existing file.");
- } else {
- show_message("Cancelling save.");
- return;
- }
- }
-
- if ((fp = fopen(fname, "w")) == NULL) {
- (void)sprintf(buf, "Can't open output file \"%s\".", fname);
- show_message(buf);
- return;
- }
-
- seqp = seq;
-
- prev_hash = 0;
- char_index = 0;
-
- num = NUM_CARDS - deck_index;
- for (i = num; i < NUM_CARDS; i++) {
- *seqp++ = deck_cache[i];
- }
- *seqp++ = FUS;
- for (i = (num - 1); i >= 0; i--) {
- *seqp++ = deck_cache[i];
- }
-
- *seqp = EOS;
- write_sequence(fp, seq);
- seqp = seq;
-
-
- for (i = 0; i < NUM_STACKS; i++) {
- (void)fputc(' ', fp);
- tmp = stack[i]->cards;
- seqp = seq;
- if (tmp) {
- while (tmp->type == Facedown) {
- *seqp++ = card_to_int(tmp);
- tmp = tmp->next;
- }
- if (seqp != seq) {
- *seqp++ = FUS;
- }
-
- while (tmp) {
- *seqp++ = card_to_int(tmp);
- tmp = tmp->next;
- }
-
- if (seqp != seq) {
- *seqp = EOS;
- write_sequence(fp, seq);
- }
- }
- }
-
- /* save piles */
- seqp = seq;
- for (i = 0; i < NUM_PILES; i++) {
- if (piles[i]->cards) {
- *seqp++ = card_to_int(last_card(piles[i]));
- }
- }
- if (seqp != seq) {
- *seqp = EOS;
- (void)fputc(' ', fp);
- write_sequence(fp, seq);
- }
- fputs(".\n", fp);
-
- /* write out the moves */
- write_moves(fp);
- fputs(".\n\n", fp);
-
- /* write human readable version */
- write_human(fp);
- (void)fclose(fp);
- (void)sprintf(buf, "Position saved to file \"%s\".", fname);
- show_message(buf);
- }
-
- write_human(fp)
- FILE *fp;
- {
- CardPtr tmps[NUM_STACKS];
- int i;
- int done = 0;
- Bool toprow = True;
-
- for (i = 0; i < NUM_STACKS; i++) {
- tmps[i] = stack[i]->cards;
- }
-
- while (done < NUM_STACKS) {
- done = 0;
- for (i = 0; i < NUM_STACKS; i++) {
- if (tmps[i] == CARDNULL) {
- /* empty stack */
- if (toprow) {
- fputs("(sp)\t", fp);
- } else {
- (void)fputc('\t', fp);
- }
- done++;
- continue;
- } else {
- if (tmps[i]->type == Facedown) {
- fputs(" --", fp);
- } else {
- (void)fprintf(fp, "%2s%c",
- rnk_name(tmps[i]->rank),
- tolower(*suit_name(tmps[i]->suit)));
- }
- }
- (void)fputc('\t', fp);
- tmps[i] = tmps[i]->next;
- }
- (void)fputc('\n', fp);
- toprow = False;
- }
- }
-
- read_selection(buf)
- char *buf;
- {
- char *moves;
-
- buf = remove_newlines(buf);
- moves = strchr(buf, '.');
- if (moves)
- moves++;
- if (moves && !strchr(moves, '.'))
- moves = NULL;
- if (restore_game(buf, moves) != 0) {
- show_message("Bogus data in selection.");
- restart = True;
- } else {
- show_message("Position loaded from selection.");
- }
- free(buf);
- }
-
- read_file_or_selection(fname)
- char *fname;
- {
- if (access(fname, R_OK) == -1) {
- read_selection(fname);
- } else {
- read_file(fname);
- }
- }
-
- read_file(fname)
- char *fname;
- {
- FILE *fp;
- char buf[1024], buf2[1024];
- char *dp;
-
- fname = remove_newlines(fname);
- if ((fp = fopen(fname, "r")) == NULL) {
- (void)sprintf(buf, "Can't open file \"%s\" for loading.",
- fname);
- show_message(buf);
- return;
- }
-
- /* read card string */
- dp = buf;
- while ((*dp = (char)fgetc(fp)) != EOF) {
- if (*dp == '\n') /* ignore any CR */
- continue;
- if (*dp == '.')
- break;
- dp++;
- }
- *++dp = '\0';
-
- /* read moves string */
- dp = buf2;
- while ((*dp = (char)fgetc(fp)) != EOF) {
- if (*dp == '\n') /* ignore any CR */
- continue;
- if (*dp == '.')
- break;
- dp++;
- }
- *++dp = '\0';
-
- if (restore_game(buf, buf2) == 0) {
- (void)sprintf(buf, "Saved game \"%s\" loaded.", fname);
- } else {
- (void)sprintf(buf, "Bogus save file \"%s\".", fname);
- restart = True;
- }
-
- show_message(buf);
- }
-
- static int
- restore_game(str, str2)
- char *str, *str2;
- {
-
- if (read_position(str) != 0) {
- return (-1);
- }
-
- init_cache(); /* clear out the move cache */
-
- if (str2)
- read_moves(str2);
-
- return (0);
- }
-
- /*
- * play the same deck again
- */
- int
- replay()
- {
- CardPtr cache[NUM_CARDS];
- CardPtr card;
- int i;
- Rank rank;
- Suit suit;
-
- remove_all_cards(cache);
-
- /* reset card spacing */
- for (i = 0; i < NUM_STACKS; i++) {
- stack[i]->card_delta = CARD_DELTA;
- }
-
- for (i = 0; i < NUM_CARDS; i++) {
- int_to_card(deck_cache[i], &suit, &rank);
- card = find_card(cache, suit, rank);
- if (card == CARDNULL) {
- show_message("Old deck is corrupted -- can't replay.");
- recover(cache);
- return -1;
- }
- card->type = Facedown;
- add_card(card, deck->cards, LOC_END, deck);
- }
- /* wipe the old stuff */
- XClearArea(dpy, table, 0, 0, table_width, table_height, False);
-
- /* force piles and deck to redraw -- stacks will paint in deal */
- XClearArea(dpy, table, 0, 0, table_width, DECK_Y + CARD_HEIGHT, True);
-
- deck_index = NUM_CARDS;
- deal_number = 0;
- deal_cards();
-
- return 0;
- }
-
-
- /*
- * replay removing a suit
- */
- static void
- show_suit(from)
- int from;
- {
- CardPtr tmp;
- int i;
-
- tmp = last_card(stack[from]);
- assert (tmp->rank == Ace);
- while (tmp && tmp->rank != King) {
- tmp = tmp->prev;
- }
- assert (tmp->rank == King);
-
- for (i = 0; i < NUM_PILES; i++) {
- if (piles[i]->cards == CARDNULL) {
- break;
- }
- }
- assert (i < NUM_PILES);
-
- move_to_list(tmp, piles[i], False);
- }
-
- /*
- * replay a normal move
- */
- static void
- show_normal(from, dest, num)
- int from, dest, num;
- {
- CardPtr tmp;
-
- tmp = last_card(stack[from]);
-
- while (--num) {
- tmp = tmp->prev;
- }
-
- move_to_list(tmp, stack[dest], False);
- }
-
- #ifdef XVIEW
- get_move_index()
- {
- return(move_index);
- }
-
- Bool
- show_n_moves(place, number)
- int place, number;
- {
- int val;
- int from, dest, num, exposed;
- int i;
-
- for (i=0; i < number; i++) {
- if ((i+place) > move_index)
- return (False);
- val = move_cache[place + i];
- unencode(val, &from, &dest, &num, &exposed);
-
- if (from == 0) {
- deal_next_hand(False);
- } else {
- if (dest == 0) {
- show_suit(from - 1);
- } else {
- show_normal(from - 1, dest - 1, num);
- }
- }
- }
- return (True);
- }
- #else
-
- /*
- * show the moves made so far
- *
- * start is the position into the movelog, num is the number of moves
- * (0 specifies all)
- * event_check is the routine that checks for an abort and handles
- * damage, etc. delay_func pauses as necessary
- */
- void
- show_play(start, num_moves, event_check, delay_func)
- int start, num_moves;
- Bool (*event_check)();
- void (*delay_func)();
- {
- int i;
- int val;
- int from, dest, num, exposed;
- Bool aborted = False;
-
- if (replay() == -1)
- return;
-
- /* handle any events from reseting */
- (void) show_play_events();
-
- show_message("Showing all moves -- hit any key or button to abort");
-
- if (num_moves == 0) /* show all case */
- num_moves = move_index;
-
- for (i = start; (i < move_index) && (i < (start + num_moves)); i++) {
- val = move_cache[i];
- unencode(val, &from, &dest, &num, &exposed);
-
- if (from == 0) {
- deal_next_hand(False);
- } else {
- if (dest == 0) {
- show_suit(from - 1);
- } else {
- show_normal(from - 1, dest - 1, num);
- }
- }
- if ((*event_check)() == False) {
- aborted = True;
- /* restore to a known state */
- (void) replay();
- break;
- }
- (*delay_func)();
- }
- if (aborted) {
- show_message("Aborted -- cleaning up");
- } else {
- show_message("Replay finished");
- }
- }
- #endif
-