/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */
#ifdef CROSS
#   include </usr/include/stdio.h>
#else
#   include <stdio.h>
#endif
#include <stdlib.h>
#include <unistd.h>

/* Through, `my' refers to the program, `your' to the player */

#define CTYPE 13
#define CTSIZ (CTYPE+1)
#define DECK 52
#define NOMORE 0
#define DOUBTIT (-1);

typedef char HAND[CTSIZ];

/* data structures */

short debug;

HAND myhand;
HAND yourhand;
char deck[DECK];
short nextcd;
int proflag;

/* the program strategy */

int try[100];
int ntry;
char haveguessed[CTSIZ];
char hehas[CTSIZ];

/* utility and output programs */

void error(s)
        char *s;
{
	fprintf(stderr, "error: %s\n", s);
	exit(1);
}

int choose(a, n)
        char a[];
{
	/* pick and return one at random from the n choices in a */
	/* The last one is moved to replace the one chosen */
	register int j, t;

	if (n <= 0)
                error("null choice");

	j = rand() % n;
	t = a[j];
	a[j] = a[n-1];
	return(t);
}

void shuffle()
{
	/* shuffle the deck, and reset nextcd */
	/* uses the random number generator `rand' in the C library */
	/* assumes that `srand' has already been called */

	register int i;

	for (i=0; i<DECK; ++i) deck[i] = (i%13)+1;  /* seed the deck */

	for (i=DECK; i>0; --i) { /* select the next card at random */
		deck[i-1] = choose(deck, i);
	}

	nextcd = 0;
}

int draw()
{
	if (nextcd >= DECK)
            return NOMORE;
	return deck[nextcd++];
}

int empty(h)
        HAND h;
{
	register int i;

	for (i=1; i<=CTYPE; ++i) {
		if (h[i] != 0 && h[i] != 4)
                        return(0);
	}
	return(i);
}

int mark(hand, cd)
        HAND hand;
{
	if (cd != NOMORE) {
		++hand[cd];
		if (hand[cd] > 4) {
			error("mark overflow");
		}
	}
	return(cd);
}

void deal(hand, n)
        HAND hand;
{
	while(n--) {
		if (mark(hand, draw()) == NOMORE)
                        error("deck exhausted");
	}
}

char *cname[] = {
	"NOMORE!!!",
	"A",
	"2",
	"3",
	"4",
	"5",
	"6",
	"7",
	"8",
	"9",
	"10",
	"J",
	"Q",
	"K",
};

void stats()
{
	register int i, ct, b;

	if (proflag)
                printf("Pro level\n");
	b = ct = 0;

	for (i=1; i<=CTYPE; ++i) {
		if (myhand[i] == 4)
                        ++b;
		else
                        ct += myhand[i];
	}

	if (b) {
		printf("My books: ");
		for (i=1; i<=CTYPE; ++i) {
			if (myhand[i] == 4)
                                printf("%s ", cname[i]);
		}
		printf("\n");
	}

	printf("%d cards in my hand, %d in the pool\n", ct, DECK-nextcd);
	printf("You ask me for: ");
}

void phand(h)
        HAND h;
{
	register int i, j;

	j = 0;

	for (i = 1; i<= CTYPE; ++i) {
		if (h[i] == 4) {
			++j;
			continue;
		}
		if (h[i]) {
			register int k = h[i];

			while(k--) printf("%s ", cname[i]);
		}
	}

	if (j) {
		printf("+ Books of ");
		for (i=1; i<=CTYPE; ++i) {
			if (h[i] == 4)
                                printf("%s ", cname[i]);
		}
	}

	printf("\n");
}

/*	print instructions */

char *inst[] = {
	"`Go Fish' is a childrens' card game.  The Object is to",
	"accumulate `books' of 4 cards with the same face value.  The",
	"players alternate turns; each turn begins with one player",
	"selecting a card from his hand, and asking the other player for",
	"all cards of that face value.  If the other player has one or",
	"more cards of that face value in his hand, he gives them to the",
	"first player, and the first player makes another request.",
	"Eventually, the first player asks for a card which is not in",
	"the second player's hand: he replies `GO FISH!' The first",
	"player then draws a card from the `pool' of undealt cards.  If",
	"this is the card he had last requested, he draws again.  When a",
	"book is made, either through drawing or requesting, the cards",
	"are laid down and no further action takes place with that face",
	"value.  To play the computer, simply make guesses by typing a,",
	"2, 3, 4, 5, 6, 7, 8, 9, 10, j, q, or k when asked.  Hitting",
	"return gives you information about the size of my hand and the",
	"pool, and tells you about my books.  Saying `p' as a first",
	"guess puts you into `pro' level; The default is pretty dumb!",
	"Good Luck!\n",
	"",
};

void instruct()
{
	register char **cpp;

	printf("\n");

	for (cpp = inst; **cpp != '\0'; ++cpp) {
		printf("%s\n", *cpp);
	}
}

void start(h)
        HAND h;
{
	;
}

void score()
{
	register int my, your, i;

	my = your = 0;

	printf("The game is over.\nMy books: ");

	for (i=1; i<=CTYPE;++i) {
		if (myhand[i] == 4) {
			++my;
			printf("%s ", cname[i]);
		}
	}

	printf("\nYour books: ");

	for (i=1; i<=CTYPE;++i) {
		if (yourhand[i] == 4) {
			++your;
			printf("%s ", cname[i]);
		}
	}

	printf("\n\nI have %d, you have %d\n", my, your);

	printf("\n%s win!!!\n", my>your?"I":"You");
	exit(0);
}

void heguessed(d)
{
	++hehas[d];
}

void madebook(x)
{
	printf("Made a book of %s's\n", cname[x]);
}

void hedrew(d)
{
	++hehas[d];
}

/* reflect the effect of a move on the hands */

int move(hs, ht, g, v)
        HAND hs, ht;
{
	/* hand hs has made a guess, g, directed towards ht */
	/* v on indicates that the guess was made by the machine */
	register int d;
	char *sp, *tp;

	sp = tp = "I";
	if (v)
                tp = "You";
	else
                sp = "You";

	if (g == NOMORE) {
		d = draw();
		if (d == NOMORE)
                        score();
		else {
			printf("Empty Hand\n");
			if (! v)
                                printf("You draw %s\n", cname[d]);
			mark(hs, d);
		}
		return(0);
	}

	if (! v)
                heguessed(g);

	if (hs[g] == 0) {
		if (v)
                        error("Rotten Guess");
		printf("You don't have any %s's\n", cname[g]);
		return(1);
	}

	if (ht[g]) { /* successful guess */
		printf("%s have %d %s%s\n", tp, ht[g], cname[g], ht[g]>1?"'s":"");
		hs[g] += ht[g];
		ht[g] = 0;
		if (hs[g] == 4)
                        madebook(g);
		return 1;
	}

	/* GO FISH! */

	printf("%s say \"GO FISH!\"\n", tp);

	newdraw:
	d = draw();
	if (d == NOMORE) {
		printf("No more cards\n");
		return(0);
	}
	mark(hs, d);
	if (! v)
                printf("You draw %s\n", cname[d]);
	if (hs[d] == 4)
                madebook(d);
	if (d == g) {
		printf("%s drew the guess, so draw again\n", sp);
		if (! v)
                        hedrew(d);
		goto newdraw;
	}
	return(0);
}

#define G(x) { if (go) goto err; else go = x; }

int guess()
{
	/* get the guess from the tty and return it... */
	register int g, go;

	go = 0;

	for (;;) {
		switch(g = getchar()) {

		case 'p':
		case 'P':
			++proflag;
			continue;

		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			G (g - '0');
			continue;

		case 'a':
		case 'A':
			G (1);
			continue;

		case '1':
			G (10);
			continue;

		case '0':
			if (go != 10)
                                goto err;
			continue;

		case 'J':
		case 'j':
			G (11);
			continue;

		case 'Q':
		case 'q':
			G (12);
			continue;

		case 'K':
		case 'k':
			G (13);
			continue;

		case '\n':
			if (empty(yourhand))
                                return NOMORE;
			if (go == 0) {
				stats();
				continue;
			}
			return(go);

		case ' ':
		case '\t':
			continue;

		default:
			err:
			while(g != '\n') g = getchar();
			printf("what?\n");
			continue;
		}
	}
}

int myguess()
{
	register int i, lg, t;

	if (empty(myhand))
                return(NOMORE);

	/* make a list of those things which i have */
	/* leave off any which are books */
	/* if something is found that he has, guess it! */

	ntry = 0;
	for (i=1; i<=CTYPE; ++i) {
		if (myhand[i] == 0 || myhand[i] == 4)
                        continue;
		try[ntry++] = i;
	}

	if (! proflag)
                goto random;

	/* get ones he has, if any */

	for (i=0; i<ntry; ++i) {
		if (hehas[try[i]]) {
			i = try[i];
			goto gotguess;
		}
	}

	/* is there one that has never been guessed; if so, guess it */
	lg = 101;
	for (i=0; i<ntry; ++i) {
		if (haveguessed[try[i]] < lg)
                        lg = haveguessed[try[i]];
	}
	/* remove all those not guessed longest ago */

	t = 0;
	for (i=0; i<ntry; ++i) {
		if (haveguessed[try[i]] == lg)
                        try[t++] = try[i];
	}
	ntry = t;
	if (t <= 0)
                error("bad guessing loop");

	random:
	i = choose(try, ntry);  /* make a random choice */

	gotguess:  /* do bookkeeping */

	hehas[i] = 0;  /* he won't anymore! */
	for (t=1; t<=CTYPE; ++t) {
		if (haveguessed[t])
                        --haveguessed[t];
	}
	haveguessed[i] = 100;  /* will have guessed it */
	return(i);
}

void game()
{
	shuffle();

	deal(myhand, 7);
	deal(yourhand, 7);

	start(myhand);

	for (;;) {
		register int g;

		/* you make repeated guesses */

		for (;;) {
			printf("your hand is: ");
			phand(yourhand);
			printf("you ask me for: ");
			if (! move(yourhand, myhand, g = guess(), 0))
                                break;
			printf("Guess again\n");
		}

		/* I make repeated guesses */

		for (;;) {
			if ((g = myguess()) != NOMORE) {
				printf("I ask you for: %s\n", cname[g]);
			}
			if (! move(myhand, yourhand, g, 1))
                                break;
			printf("I get another guess\n");
		}
	}
}

int main(argc, argv)
        char * argv[];
{
	/* initialize shuffling, ask for instructions, play game, die */
	register int c;

	if (argc > 1 && argv[1][0] == '-') {
		while(argv[1][0] == '-') {
                        ++argv[1];
                        ++debug;
                }
		argv++;
		argc--;
	}

	srand(getpid());

	printf("instructions?\n");
	if ((c = getchar()) != '\n') {
		if (c != 'n')
                        instruct();
		while (getchar() != '\n');
	}

	game();
	return 0;
}
