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