1 /*
2 * Spider
3 *
4 * (c) Copyright 1989, Donald R. Woods and Sun Microsystems, Inc.
5 * (c) Copyright 1990, David Lemke and Network Computing Devices Inc.
6 *
7 * See copyright.h for the terms of the copyright.
8 *
9 * @(#)spider.c 2.4 91/05/09
10 *
11 */
12
13 /*
14 * Spider card logic
15 */
16
17 #define IN_MAIN
18
19 #include "defs.h"
20 #include "globals.h"
21 #include <ctype.h>
22
23 static void fix_coords();
24
25 int deltamod = 0;
26 Bool squish = True;
27 int cheat_count = 0;
28
29 /*
30 * build all the cards, stacks, piles and the original deck
31 */
card_init()32 card_init()
33 {
34 int i;
35 Suit suit;
36 Rank rank;
37 CardPtr tmp, tmp2;
38
39 for (i = 0; i < NUM_STACKS; i++) {
40 stack[i] = (CardList) malloc(sizeof(CardListStruct));
41 stack[i]->place = i + 11;
42 stack[i]->cards = CARDNULL;
43 stack[i]->card_delta = CARD_DELTA;
44 stack[i]->x = STACK_LOC_X(stack[i]->place);
45 stack[i]->y = STACK_LOC_Y;
46 }
47
48 for (i = 0; i < NUM_PILES; i++) {
49 piles[i] = (CardList) malloc(sizeof(CardListStruct));
50 piles[i]->place = i + 1;
51 piles[i]->cards = CARDNULL;
52 piles[i]->card_delta = 0;
53 piles[i]->x = PILE_LOC_X(piles[i]->place);
54 piles[i]->y = PILE_LOC_Y;
55 }
56 deck = (CardList) malloc(sizeof(CardListStruct));
57 deck->place = DECK;
58 deck->x = DECK_X;
59 deck->y = DECK_Y;
60 deck->card_delta = 0;
61 deck->cards = CARDNULL;
62 tmp2 = CARDNULL;
63 for (i = 0; i < NUM_DECKS; i++) {
64 for (suit = Spade; suit <= Club; suit++) {
65 for (rank = Ace; rank <= King; rank++) {
66 tmp = (CardPtr) calloc(sizeof(CardStruct), 1);
67 add_card(tmp, tmp2, LOC_BEFORE, deck);
68 tmp->rank = rank;
69 tmp->suit = suit;
70 tmp->type = Facedown;
71 tmp2 = tmp;
72 }
73 }
74 }
75 deck->cards = tmp;
76 srandom(getpid());
77 shuffle_cards();
78 }
79
80 /*
81 * randomizes order of deck list
82 */
83
84 struct shuffle {
85 CardPtr card;
86 long value;
87 } shuffled_cards[NUM_CARDS];
88
89 compare(a1, a2)
90 struct shuffle *a1, *a2;
91 {
92 return ((a2->value) - (a1->value));
93 }
94
95 /*
96 * removes all the cards from the table and stcks them in a cache
97 */
remove_all_cards(cache)98 remove_all_cards(cache)
99 CardPtr cache[NUM_CARDS];
100 {
101 CardPtr tmp;
102 int i, j;
103
104 i = 0;
105 while (deck->cards) {
106 tmp = deck->cards;
107 remove_card(tmp);
108 cache[i++] = tmp;
109 }
110
111 for (j = 0; j < NUM_PILES; j++) {
112 while (piles[j]->cards) {
113 tmp = piles[j]->cards;
114 remove_card(tmp);
115 cache[i++] = tmp;
116 }
117 }
118
119 for (j = 0; j < NUM_STACKS; j++) {
120 while (stack[j]->cards) {
121 tmp = stack[j]->cards;
122 remove_card(tmp);
123 cache[i++] = tmp;
124 }
125 }
126
127 assert(i == NUM_CARDS);
128 }
129
130 /*
131 * shuffle the cards
132 */
shuffle_cards()133 shuffle_cards()
134 {
135 int i;
136 CardPtr cache[NUM_CARDS];
137 extern long random();
138
139 remove_all_cards(cache);
140
141 for (i = 0; i < NUM_CARDS; i++) {
142 shuffled_cards[i].card = cache[i];
143 shuffled_cards[i].value = random();
144 }
145
146 qsort((char *) shuffled_cards, NUM_CARDS, sizeof(struct shuffle),
147 compare);
148
149 for (i = 0; i < NUM_CARDS; i++) {
150 shuffled_cards[i].card->type = Facedown;
151 add_card(shuffled_cards[i].card, deck->cards, LOC_START, deck);
152 }
153
154 /* save the deal in the save cache */
155 make_deck_cache();
156
157 /* reset card spacing */
158 for (i = 0; i < NUM_STACKS; i++) {
159 stack[i]->card_delta = CARD_DELTA;
160 }
161
162 /* force things to get fixed up */
163 XClearArea(dpy, table, 0, 0, 0, 0, True);
164
165 deal_number = 0;
166 restart = False;
167 cheat_count = 0;
168
169 /* reset move log */
170 init_cache();
171 }
172
173 /*
174 * does orginal deal
175 */
deal_cards()176 deal_cards()
177 {
178 int i, j;
179 int num;
180
181 /*
182 * this is the way the original does deals -- weird, but
183 * thats compatibility
184 */
185 for (i = 0; i < NUM_STACKS; i++) {
186 CardPtr tmp[6];
187
188 /* stacks 1, 4, 7, and 10 have 1 extra card */
189 if (((i+1) % 3) == 1) {
190 num = 5;
191 } else {
192 num = 4;
193 }
194
195 /* faceup first */
196 tmp[num] = deck->cards;
197 remove_card(tmp[num]);
198 tmp[num]->type = Faceup;
199
200 for (j = (num - 1); j >= 0; j--) {
201 tmp[j] = deck->cards;
202 remove_card(tmp[j]);
203 tmp[j]->type = Facedown;
204 }
205 for (j = 0; j <= num; j++) {
206 add_card(tmp[j], stack[i]->cards, LOC_END, stack[i]);
207 }
208 }
209 deck_index -= 54;
210 deal_number = 1;
211
212 /*
213 * show the deal
214 */
215 for (i = 0; i < NUM_STACKS; i++) {
216 show_list(stack[i], stack[i]->cards);
217 }
218 }
219
220 /*
221 * deal hand of 10
222 */
deal_next_hand(log)223 deal_next_hand(log)
224 Bool log;
225 {
226 int i;
227 CardPtr tmp;
228 char buf[128];
229 int old_delta;
230
231 /* be sure all the spaces are filled */
232 for (i = 0; i < NUM_STACKS; i++) {
233 if (stack[i]->cards == CARDNULL) {
234 show_message("Can't deal until all spaces are filled");
235 spider_bell(dpy, 0);
236 return;
237 }
238 }
239 if (deck->cards == CARDNULL) {
240 /* dealt them all */
241 show_message("No more cards in deck");
242 spider_bell(dpy, 0);
243 return;
244 }
245 /* deal face up cards */
246 for (i = 0; i < NUM_STACKS; i++) {
247 old_delta = stack[i]->card_delta;
248 tmp = deck->cards;
249 remove_card(tmp);
250 tmp->type = Faceup;
251 add_card(tmp, stack[i]->cards, LOC_END, stack[i]);
252 if (old_delta != stack[i]->card_delta) {
253 show_list(stack[i], stack[i]->cards);
254 } else {
255 show_card(tmp);
256 }
257 }
258
259 deck_index -= 10;
260
261 /* force deck to repaint itself if its empty */
262 if (deck->cards == CARDNULL)
263 redraw_deck(0, 0, table_width, table_height);
264
265 assert (deal_number >= 1);
266
267 if (log)
268 record (0, 0, 0, True);
269 (void)sprintf(buf, "Dealt hand %d of 5", deal_number);
270 show_message(buf);
271
272 assert((deal_number < 5) || (deck_index == 0));
273
274 deal_number++;
275 }
276
277 /*
278 * change the state of a card
279 */
flip_card(card,state)280 flip_card(card, state)
281 CardPtr card;
282 Type state;
283 {
284 card->type = state;
285 show_card(card);
286 }
287
288 /*
289 * place a card on a list
290 *
291 * expects 'new' to have been removed from any list
292 */
add_card(new,old,location,list)293 add_card(new, old, location, list)
294 CardPtr new, old;
295 int location;
296 CardList list;
297 {
298
299 assert(new->prev == CARDNULL);
300 assert(new->next == CARDNULL);
301 assert((location == LOC_BEFORE) || (location == LOC_AFTER)
302 || (location == LOC_END) || (location == LOC_START));
303 assert ((old == NULL) || (old->list == list));
304
305
306 if (location == LOC_END) {
307 old = last_card(list);
308 location = LOC_AFTER;
309 /* let the later code do the work */
310 } else if (location == LOC_START) {
311 old = list->cards;
312 location = LOC_BEFORE;
313 /* let the later code do the work */
314 }
315
316 assert((location == LOC_BEFORE) || (location == LOC_AFTER));
317
318 /* fix the list */
319 if (list->cards == CARDNULL)
320 list->cards = new;
321 new->list = list;
322
323 if (location == LOC_BEFORE) {
324 if (old) {
325 if (list->cards == old)
326 list->cards = new;
327 if (old->prev)
328 old->prev->next = new;
329 new->prev = old->prev;
330 new->next = old;
331 old->prev = new;
332 } else {
333 new->next = old;
334 }
335 } else { /* LOC_AFTER */
336 if (old) {
337 if (old->next)
338 old->next->prev = new;
339 new->prev = old;
340 new->next = old->next;
341 old->next = new;
342 } else {
343 new->prev = old;
344 }
345 }
346
347 fix_coords(new, list, False);
348 }
349
350 /*
351 * see if cards exist, are faceup, and part of the same run
352 */
353 #define in_sequence(a, b) \
354 ((a) && (b) && \
355 ((a)->type == Faceup) && ((b)->type == Faceup) && \
356 ((a)->suit == (b)->suit) && ((a)->rank == (b)->rank + 1))
357
358 /*
359 * fix up the inter-card spacing for a stack
360 */
361 static void
fix_coords(new,list,display)362 fix_coords(new, list, display)
363 CardPtr new;
364 CardList list;
365 Bool display;
366 {
367 /* fix the coords */
368 new->x = list->x;
369 if (new->prev) { /* added to bottom */
370 if ((new->prev->y + list->card_delta + CARD_HEIGHT) >
371 table_height) {
372 recompute_list_deltas(list);
373 if (display)
374 show_list(list, list->cards);
375 }
376 /* runs are coallesced */
377 if (squish && in_sequence(new->prev, new) &&
378 in_sequence(new->prev->prev, new->prev)) {
379 new->y = new->prev->y + (list->card_delta >> 2);
380 } else {
381 new->y = new->prev->y + list->card_delta;
382 }
383 } else {
384 new->y = list->y;
385 }
386 }
387
388 /*
389 * compute the inter-card spacing for a stack
390 */
391 void
recompute_list_deltas(list)392 recompute_list_deltas(list)
393 CardList list;
394 {
395 CardPtr tmp;
396 int delta, num = 0;
397
398 assert (list->place >= STACK_1);
399
400 tmp = list->cards;
401 while (tmp) {
402 num++;
403 tmp = tmp->next;
404 }
405
406 /* don't do anything if 1 or fewer cards */
407 if (num <= 1) {
408 delta = CARD_DELTA;
409 return;
410 }
411
412 /* adjust 'size' of stack to limit the amount of redrawing */
413 if (deltamod)
414 num = (num + deltamod - 1)/deltamod * deltamod;
415
416 delta = (table_height - (STACK_LOC_Y + 10 + CARD_HEIGHT))/(num - 1);
417
418 if (delta > CARD_DELTA)
419 delta = CARD_DELTA;
420
421 if (list->card_delta != delta) {
422 list->card_delta = delta;
423 tmp = list->cards;
424 while (tmp) {
425 fix_coords(tmp, list, False);
426 tmp = tmp->next;
427 }
428 }
429 }
430
431 /*
432 * remove a card from a list
433 */
remove_card(card)434 remove_card(card)
435 CardPtr card;
436 {
437 /* fix card pointers */
438 if (card->prev)
439 card->prev->next = card->next;
440 if (card->next)
441 card->next->prev = card->prev;
442
443 /* fix up card list */
444 if (card->prev == CARDNULL)
445 card->list->cards = card->next;
446
447 /* clear pointers */
448 card->next = CARDNULL;
449 card->prev = CARDNULL;
450 card->list = CARDLISTNULL;
451 }
452
453 /*
454 * move an entire sublist to another list
455 */
456 void
move_to_list(card,list,log)457 move_to_list(card, list, log)
458 CardPtr card;
459 CardList list;
460 Bool log;
461 {
462 CardPtr tmp;
463 int from, dest;
464 int count = 0;
465 Bool exposed = False;
466 int delta;
467
468 /* fix old list */
469 if (card->prev) {
470 card->prev->next = CARDNULL;
471 if (card->prev->type == Facedown) {
472 exposed = True;
473 card->prev->type = Faceup;
474 }
475 } else {
476 card->list->cards = CARDNULL;
477 }
478
479 /* shrink stack if necessary */
480 if (card->list->place >= STACK_1 &&
481 card->list->card_delta != CARD_DELTA) {
482 recompute_list_deltas(card->list);
483 show_list(card->list, card->list->cards);
484 } else {
485 show_list(card->list, card->prev);
486 }
487 from = STACK_INDEX(card->list->place) + 1;
488
489 #ifdef DEBUG
490 validate_card_list(card->list);
491 #endif
492
493 tmp = last_card(list);
494 if (tmp) {
495 assert(tmp->next == CARDNULL);
496 tmp->next = card;
497 card->prev = tmp;
498 } else {
499 list->cards = card;
500 card->prev = CARDNULL;
501 }
502 tmp = card;
503 while (tmp) {
504 count++;
505 tmp->list = list;
506 delta = list->card_delta;
507 fix_coords(tmp, list, True);
508 /* only show card if fix_coords() didn't */
509 if (delta == list->card_delta)
510 show_card(tmp);
511 tmp = tmp->next;
512 }
513
514 #ifdef DEBUG
515 validate_card_list(list);
516 #endif
517 if (log) {
518 dest = (IS_PILE(list)) ? 0 : STACK_INDEX(list->place) + 1;
519 record(from, dest, count, exposed);
520 }
521 }
522
523 #ifdef DEBUG
print_list(list)524 print_list(list)
525 CardList list;
526 {
527 CardPtr tmp;
528
529 tmp = list->cards;
530
531 while (tmp) {
532 (void) fprintf(stderr,"card is %s of %s (%s)\n",
533 rank_name(tmp->rank),
534 suit_name(tmp->suit),
535 type_name(tmp->type));
536 tmp = tmp->next;
537 }
538 }
539
validate_card_list(list)540 validate_card_list(list)
541 CardList list;
542 {
543 CardPtr tmp;
544
545 tmp = list->cards;
546 if (tmp == CARDNULL)
547 return;
548 if (tmp->prev != CARDNULL) {
549 (void) fprintf(stderr,
550 "validate list: first card has non-null prev\n");
551 }
552 while (tmp->next) {
553 if (tmp->next->prev != tmp)
554 (void) fprintf(stderr,"validate list: bad link\n");
555 if (tmp->list != list)
556 (void) fprintf(stderr,
557 "validate list: card/list mismatch\n");
558 tmp = tmp->next;
559 }
560 }
561 #endif
562
563 /*
564 * rank & suit value->string roputines
565 */
566
567
568 static char *rnk_names[] = {
569 "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"
570 };
571
572 /*
573 * shortened version for save files and info
574 */
575 char *
rnk_name(rank)576 rnk_name(rank)
577 Rank rank;
578 {
579 assert(rank >= Ace && rank <= King);
580
581 return (rnk_names[rank]);
582 }
583
584 static char *rank_names[] = {
585 "Ace", "Deuce", "Three", "Four", "Five", "Six", "Seven",
586 "Eight", "Nine", "Ten", "Jack", "Queen", "King"
587 };
588
589 char *
rank_name(rank)590 rank_name(rank)
591 Rank rank;
592 {
593 assert(rank >= Ace && rank <= King);
594
595 return (rank_names[rank]);
596 }
597
598 static char *suit_names[] = {
599 "Spades", "Hearts", "Diamonds", "Clubs"
600 };
601
602 char *
suit_name(suit)603 suit_name(suit)
604 Suit suit;
605 {
606 assert (suit >= Spade && suit <= Club);
607
608 return (suit_names[suit]);
609 }
610
611 #ifdef DEBUG
612 static char *type_names[] = {
613 "Faceup", "Facedown", "Joker"
614 };
615
616 char *
type_name(type)617 type_name(type)
618 Type type;
619 {
620 assert (type >= Faceup && type <= Joker);
621
622 return (type_names[type]);
623 }
624 #endif DEBUG
625
626
627 /*
628 * return the bottom-most card of a list
629 */
630 CardPtr
last_card(list)631 last_card(list)
632 CardList list;
633 {
634 CardPtr tmp = CARDNULL;
635
636 if ((list == CARDLISTNULL) || (list->cards == CARDNULL))
637 return (CARDNULL);
638
639 tmp = list->cards;
640 while (tmp->next) {
641 tmp = tmp->next;
642 }
643 return (tmp);
644 }
645
646 /*
647 * can only move card if there's a run of the same suit underneath it
648 */
649 Bool
can_move(card)650 can_move(card)
651 CardPtr card;
652 {
653 CardPtr tmp;
654 Rank last_rank;
655
656 if (card->type != Faceup)
657 return (False);
658 last_rank = card->rank;
659 tmp = card->next;
660 while (tmp) {
661 if ((tmp->rank != (last_rank - 1)) || (tmp->suit != card->suit))
662 return (False);
663 last_rank = tmp->rank;
664 tmp = tmp->next;
665 }
666 return (True);
667 }
668
669 /*
670 * can 'card' go on to 'dest' ?
671 */
672 Bool
can_move_to(card,list)673 can_move_to(card, list)
674 CardPtr card;
675 CardList list;
676 {
677 CardPtr tmp;
678
679 assert (can_move(card));
680 tmp = last_card(list);
681 if (tmp == CARDNULL)
682 return (True);
683 return (tmp->rank == (card->rank + 1));
684 }
685
686 /*
687 * finds the best move for a specific card
688 *
689 * use the first 'next best' move so we choose them from right to left
690 */
691 CardList
best_card_move(card)692 best_card_move(card)
693 CardPtr card;
694 {
695 CardList next_best = CARDLISTNULL;
696 CardList space = CARDLISTNULL;
697 CardPtr tmp;
698 int i;
699
700 /* iterate through the stacks */
701 for (i = 0; i < NUM_STACKS; i++) {
702 /* don't look at our own stack */
703 if (stack[i] == card->list)
704 continue;
705 tmp = last_card(stack[i]);
706 if (tmp == CARDNULL) { /* spaces are ok */
707 if (next_best == CARDLISTNULL)
708 space = stack[i];
709 continue;
710 }
711 /* rank & suit is optimal */
712 if (tmp->rank == (card->rank + 1)) {
713 if (tmp->suit == card->suit)
714 return (tmp->list);
715 /* just rank is the next best */
716 if (next_best == CARDLISTNULL)
717 next_best = tmp->list;
718 }
719 }
720 if (next_best == CARDLISTNULL)
721 next_best = space;
722
723 return (next_best);
724 }
725
726 /*
727 * performs the best move for an entire sub-list
728 */
729 void
best_list_move(list,first_card)730 best_list_move(list, first_card)
731 CardList list;
732 CardPtr first_card;
733 {
734 CardPtr tmp, tmp2;
735 CardList best = CARDLISTNULL;
736
737 if (first_card != CARDNULL) {
738 tmp = first_card;
739 } else {
740 tmp = list->cards;
741 if (tmp == CARDNULL) {
742 show_message("Empty list");
743 spider_bell(dpy, 0);
744 return;
745 }
746 }
747
748 /*
749 * iterate through stack. for each card that can move,
750 * try to find one. return as soon as we find one
751 */
752 while (tmp) {
753 if (can_move(tmp)) {
754 /*
755 * special case full suits
756 */
757 if (tmp->rank == King) {
758 tmp2 = last_card(list);
759 if (tmp2->rank == Ace) {
760 move_to_pile(tmp);
761 return;
762 }
763 }
764 best = best_card_move(tmp);
765 if (best) {
766 move_to_list(tmp, best, True);
767 } else {
768 card_message("Nowhere to move the", tmp);
769 spider_bell(dpy, 0);
770 }
771 return;
772 }
773 tmp = tmp->next;
774 }
775 }
776
777 void
move_to_pile(card)778 move_to_pile(card)
779 CardPtr card;
780 {
781 int i;
782
783 for (i = 0; i < NUM_PILES; i++) {
784 if (piles[i]->cards == CARDNULL)
785 break;
786 }
787 assert(i < NUM_PILES);
788
789 move_to_list(card, piles[i], True);
790 }
791
792
793 /*
794 * is card thru lastcard King - Ace?
795 */
796 static Bool
is_sequence(card)797 is_sequence(card)
798 CardPtr card;
799 {
800 CardPtr tmp;
801
802 if (card->rank != King)
803 return (False);
804 tmp = card;
805 while (tmp->next) {
806 if (!(tmp->next && (tmp->suit == tmp->next->suit) &&
807 (tmp->rank == (tmp->next->rank + 1))))
808 return (False);
809 tmp = tmp->next;
810 }
811 if (tmp->rank == Ace)
812 return (True);
813 else
814 return (False);
815 }
816
817 /*
818 * Compute a somewhat arbitrary evaluation function for the position:
819 * 2 point per card sitting atop next higher card in same suit
820 * 10 per card turned face up
821 * 15 extra for each column where all cards have been revealed
822 * 50 per completed suit removed (note this costs 12*2 for cards in seq)
823 * If all columns are either empty or contain completed suits, then those
824 * suits also count 50 (including the 24 for the 12 cards that are atop
825 * higher cards), plus an extra 2 for each suit after the first three.
826 * Thus the only way to get 1000 points is to win with all eight suits
827 * still in the tableau.
828 */
829
830 int
compute_score()831 compute_score()
832 {
833 int score = 0;
834 int i;
835 CardPtr tmp;
836 int num_piles = 0;
837
838 if (deal_number == 0)
839 return (0);
840
841 score = 44 * 10; /* score if all cards flipped */
842 for (i = 0; i < NUM_PILES; i++) {
843 if (piles[i]->cards)
844 score += 50;
845 }
846
847 for (i = 0; i < NUM_STACKS; i++) {
848 if (stack[i]->cards) {
849 if (stack[i]->cards->type == Faceup) {
850 score += 15;
851 if (is_sequence(stack[i]->cards)) {
852 score += 50;
853 num_piles++;
854 if (num_piles > 3) {
855 score += 2;
856 }
857 continue;
858 }
859 }
860 } else {
861 score += 15;
862 }
863
864 tmp = stack[i]->cards;
865 while (tmp) {
866 if (tmp->type == Faceup) {
867 if (tmp->prev) {
868 if ((tmp->prev->type == Faceup) &&
869 (tmp->rank == (tmp->prev->rank - 1))
870 && (tmp->suit == tmp->prev->suit))
871 score += 2;
872 }
873 } else {
874 score -= 10; /* still Facedown */
875 }
876 tmp = tmp->next;
877 }
878 }
879
880 return (score);
881 }
882
883 /*
884 * display which suits have all their cards visible
885 */
show_full_suits()886 show_full_suits()
887 {
888 char showing[NUM_RANKS][NUM_SUITS];
889 Bool all[NUM_SUITS];
890 int num = 0;
891 int i, j;
892 CardPtr tmp;
893 char buf[128];
894
895 for (i = 0; i < NUM_RANKS; i++)
896 for (j = 0; j < NUM_SUITS; j++)
897 showing[i][j] = 0;
898
899 for (i = 0; i < NUM_STACKS; i++) {
900 tmp = stack[i]->cards;
901 while (tmp) {
902 if (tmp->type == Faceup) {
903 showing[tmp->rank][tmp->suit]++;
904 }
905 tmp = tmp->next;
906 }
907 }
908 for (j = 0; j < NUM_SUITS; j++) {
909 all[j] = True;
910 for (i = 0; i < NUM_RANKS; i++) {
911 if (showing[i][j] == 0) {
912 all[j] = False;
913 break;
914 }
915 }
916 if (all[j])
917 num++;
918 }
919 if (num == 0) {
920 show_message("No suit has all 13 cards showing.");
921 } else {
922 (void) strcpy(buf,
923 "Sufficient cards visible to form complete set of ");
924 for (j = 0; j < NUM_SUITS; j++) {
925 if (all[j]) {
926 (void)strcat(buf, suit_name((Suit) j));
927 if (--num) {
928 (void)strcat(buf, ", ");
929 } else {
930 (void)strcat(buf, ".");
931 }
932 }
933 }
934 show_message(buf);
935 }
936 }
937
938 /*
939 * print cards in list
940 */
expand(list)941 expand(list)
942 CardList list;
943 {
944 CardPtr tmp, last;
945 char buf[512], buf2[10];
946 Bool sequence = False;
947
948
949 tmp = list->cards;
950 if (tmp == CARDNULL) {
951 show_message("Empty column.");
952 return;
953 }
954 (void)strcpy(buf, "Column contains:");
955 last = CARDNULL;
956 while (tmp) {
957 if (tmp->type != Faceup) {
958 tmp = tmp->next;
959 continue;
960 }
961 if (last && last->suit == tmp->suit &&
962 (last->rank == tmp->rank + 1)) {
963 if (!sequence) {
964 sequence = True;
965 }
966 } else {
967 if (sequence) {
968 (void)sprintf(buf2, "-%s%c %s%c",
969 rnk_name(last->rank),
970 tolower(*suit_name(last->suit)),
971 rnk_name(tmp->rank),
972 tolower(*suit_name(tmp->suit)));
973 sequence = False;
974 } else {
975 (void)sprintf(buf2, " %s%c",rnk_name(tmp->rank),
976 tolower(*suit_name(tmp->suit)));
977 }
978 (void)strcat(buf, buf2);
979 }
980 last = tmp;
981 tmp = tmp->next;
982 }
983 /* handle dangling sequences */
984 if (sequence) {
985 (void)sprintf(buf2, "-%s%c", rnk_name(last->rank),
986 tolower(*suit_name(last->suit)));
987 (void)strcat(buf, buf2);
988 }
989 show_message(buf);
990 }
991
992 static int
col_locate(list,suit,rank,checksuit)993 col_locate(list, suit, rank, checksuit)
994 CardList list;
995 Suit suit;
996 Rank rank;
997 Bool checksuit;
998 {
999 CardPtr tmp;
1000 int count = 0;
1001
1002 tmp = list->cards;
1003 for (tmp = list->cards; tmp; tmp = tmp->next) {
1004 if (tmp->type != Faceup)
1005 continue;
1006 /* we have a find if we asked for a suit and found it
1007 * OR if we don't have a suit and are looking for a free
1008 * card
1009 */
1010 if (tmp->rank == rank &&
1011 ((checksuit && tmp->suit == suit) ||
1012 (!checksuit &&
1013 (!tmp->next || /* end of stack */
1014 (tmp->next && /* free */
1015 tmp->next->rank != (tmp->rank - 1))))))
1016 count++;
1017 }
1018 return count;
1019 }
1020
1021 void
locate(str)1022 locate(str)
1023 char *str;
1024 {
1025 int i, num;
1026 Suit suit;
1027 Rank rank;
1028 char buf[512], buf2[256], times[32];
1029 Bool found = False, checksuit = False;
1030
1031 if (!str)
1032 return;
1033 /*
1034 * assume that the string is well formed (probably stupid
1035 * assumption) and treat accordingly
1036 */
1037 for (i = 0; i < strlen(str); i++) {
1038 switch(str[i]) {
1039 case 'D':
1040 case 'd':
1041 suit = Diamond;
1042 checksuit = True;
1043 break;
1044 case 'S':
1045 case 's':
1046 suit = Spade;
1047 checksuit = True;
1048 break;
1049 case 'H':
1050 case 'h':
1051 suit = Heart;
1052 checksuit = True;
1053 break;
1054 case 'C':
1055 case 'c':
1056 suit = Club;
1057 checksuit = True;
1058 break;
1059 case 'A':
1060 case 'a':
1061 rank = Ace;
1062 break;
1063 case 'T':
1064 case 't':
1065 rank = Ten;
1066 break;
1067 case 'J':
1068 case 'j':
1069 rank = Jack;
1070 break;
1071 case 'Q':
1072 case 'q':
1073 rank = Queen;
1074 break;
1075 case 'K':
1076 case 'k':
1077 rank = King;
1078 break;
1079 default:
1080 rank = atoi(str) - 1;
1081 if (rank < Deuce || rank > Ten) {
1082 (void)sprintf(buf,
1083 "Invalid card specification %s", str);
1084 show_message(buf);
1085 return;
1086 }
1087 break;
1088 }
1089 }
1090
1091 if (checksuit)
1092 (void)sprintf(buf, "%s of %s ",
1093 rank_name(rank), suit_name(suit));
1094 else
1095 (void)sprintf(buf, "Free %s ", rank_name(rank));
1096 for (i = 0; i < NUM_STACKS; i++) {
1097 if (num = col_locate(stack[i], suit, rank, checksuit)) {
1098 if (found) {
1099 (void)strcat(buf, ", ");
1100 }
1101 found = True;
1102 if (num == 1) {
1103 (void)strcpy(times, "once");
1104 } else if (num == 2) {
1105 (void)strcpy(times, "twice");
1106 } else {
1107 (void)sprintf(times, "%d times", num);
1108 }
1109
1110 (void)sprintf(buf2, "occurs in column %d %s ", i + 1,
1111 times);
1112 (void)strcat(buf, buf2);
1113 }
1114 }
1115
1116 if (!found)
1117 (void)strcat(buf, "is not visible");
1118 show_message(buf);
1119 }
1120
1121 /*
1122 * routines to give advice about best move.
1123 *
1124 * doing a really good job here may be impossible -- this is just a
1125 * rough attempt
1126 */
1127
1128 static void
advise_pile_move(list)1129 advise_pile_move(list)
1130 CardList list;
1131 {
1132 char buf[128];
1133
1134 (void) sprintf(buf, "Remove King through Ace in pile %d",
1135 STACK_INDEX(list->place) + 1);
1136 show_message(buf);
1137 }
1138
1139 static void
advise_move(card,from,to)1140 advise_move(card, from, to)
1141 CardPtr card;
1142 CardList from, to;
1143 {
1144 char buf[128];
1145
1146 if (to->cards == CARDNULL) {
1147 (void) sprintf(buf, "Move %s of %s from stack %d to space in stack %d",
1148 rank_name(card->rank), suit_name(card->suit),
1149 STACK_INDEX(from->place) + 1,
1150 STACK_INDEX(to->place) + 1);
1151 } else {
1152 (void) sprintf(buf, "Move %s of %s from stack %d to %s of %s on stack %d",
1153 rank_name(card->rank), suit_name(card->suit),
1154 STACK_INDEX(from->place) + 1,
1155 rank_name(last_card(to)->rank), suit_name(last_card(to)->suit),
1156 STACK_INDEX(to->place) + 1);
1157 }
1158 show_message(buf);
1159 }
1160
1161 /*
1162 * calculate the relative worth of a move
1163 *
1164 * this is by no means an optimal algorithm, since there's no lookahead,
1165 * but it should be good enough to get a beginner started
1166 */
1167 /*
1168 * value is:
1169 * head of sublist rank + 100 (best to move the high cards first)
1170 * + number of cards (move as many as possible)
1171 * + 200 if show a new card (dig out cards)
1172 * + 400 if show a new space (dig out cards)
1173 * + 800 if same suit (same suits is preferable)
1174 *
1175 * the constants are arbitrary values large enough not to be reached
1176 * by the rank or count modifiers
1177 */
1178 #define RANK_MOVE 100
1179 #define NEW_CARD_MOVE 200
1180 #define SPACE_MOVE 400
1181 #define SAME_SUIT_MOVE 800
1182
1183 /* ARGSUSED */
1184 static int
value_move(card,from,to)1185 value_move(card, from, to)
1186 CardPtr card;
1187 CardList from, to;
1188 {
1189 int value;;
1190
1191 value = card->rank + RANK_MOVE; /* higher cards are worth more */
1192
1193 if (!card->prev) {
1194 value += SPACE_MOVE;
1195 } else if (card->prev->type == Facedown) {
1196 value += NEW_CARD_MOVE;
1197 }
1198 /* avoid moving to a space */
1199 if (last_card(to) == CARDNULL) {
1200 /* don't space hop */
1201 if (card->prev == CARDNULL) {
1202 value = 0;
1203 } else {
1204 value /= 2;
1205 }
1206 /* same suit is worth a lot more */
1207 } else if (card->suit == last_card(to)->suit) {
1208 value += SAME_SUIT_MOVE;
1209 /* avoid jumping back & forth from two equal moves */
1210 } else if (card->prev && card->prev->type == Faceup &&
1211 (card->prev->rank == (card->rank + 1))) {
1212 value = 0;
1213 }
1214 return (value);
1215 }
1216
1217 /*
1218 * finds the 'best' move and displays it
1219 */
1220 void
advise_best_move()1221 advise_best_move()
1222 {
1223 CardPtr tmp, tmp2, bestcard;
1224 CardList move = CARDLISTNULL,
1225 bestfrom = CARDLISTNULL,
1226 bestto = CARDLISTNULL;
1227 int best_value = 0, val;
1228 CardList list;
1229 int i;
1230
1231 for (i = 0; i < NUM_STACKS; i++) {
1232 list = stack[i];
1233 tmp = list->cards;
1234 if (tmp == CARDNULL) {
1235 continue;
1236 }
1237
1238 /*
1239 * iterate through stack. for each card that can move,
1240 * calculate the move value
1241 */
1242 while (tmp) {
1243 if (can_move(tmp)) {
1244 /*
1245 * special case full suits
1246 */
1247 if (tmp->rank == King) {
1248 tmp2 = last_card(list);
1249 if (tmp2->rank == Ace) {
1250 advise_pile_move(list);
1251 return;
1252 }
1253 }
1254 move = best_card_move(tmp);
1255 if (move) {
1256 val = value_move(tmp, list, move);
1257 if (val > best_value) {
1258 bestfrom = list;
1259 bestto = move;
1260 bestcard = tmp;
1261 best_value = val;
1262 }
1263 }
1264 break; /* finished with this stack */
1265 }
1266 tmp = tmp->next;
1267 }
1268 }
1269 if (bestfrom) {
1270 advise_move(bestcard, bestfrom, bestto);
1271 } else {
1272 if (deck->cards == CARDNULL) {
1273 show_message("Its all over.");
1274 } else {
1275 show_message("Deal the next hand.");
1276 }
1277 }
1278 }
1279
1280 /*
1281 * fix up the inter-card spacing when resource value "squish" changes.
1282 */
1283 void
fix_up_card_spacing()1284 fix_up_card_spacing()
1285 {
1286 int i, maxy;
1287 CardPtr tmp, last;
1288 CardList list;
1289
1290 for (i = 0; i < NUM_STACKS; i++) {
1291 list = stack[i];
1292 tmp = list->cards;
1293 last = last_card(list);
1294 maxy = last->y;
1295 while(tmp) {
1296 fix_coords(tmp, list, False);
1297 tmp = tmp->next;
1298 }
1299 if (maxy != last->y) {
1300 show_list(list, list->cards);
1301 }
1302 }
1303 }
1304