/*
 * Oh Hell!
 *
 * Copyright (C) Evan Harris, 1991, 1993, 1994
 *
 * Permission is granted to freely redistribute and modify this code,
 * providing the author(s) get credit for having written it.
 */

#include <stdlib.h>
#ifdef PRINTPROGRESS
#include <stdio.h>
#endif
#include "ohhell.h"

unsigned char hand[NUMPLAYERS][NUMHANDS];
unsigned char bid[NUMPLAYERS];
unsigned char wins[NUMPLAYERS];
unsigned char score[NUMPLAYERS];
unsigned char played[NUMPLAYERS];
unsigned char trumps;
unsigned char led;


void
main(int argc, char **argv)
{
    unsigned char deal = 1;
    unsigned char first = 0;

    InitDisplay(argc, argv);
    InitRandom(NEW);
    InitGame();

    for (;;) {
	switch (PlayGame(deal, first)) {
	  case NEWGAME:
	    InitGame();
	    deal = 1;
	    first = (first + 1) % NUMPLAYERS;
	    break;
	  case NEXTGAME:
	    deal++;
	    first = (first + 1) % NUMPLAYERS;
	    break;
	  case QUIT:
	    EndDisplay();
	    exit(0);
	    break;
	}
    }

}


void
InitGame()
{
    unsigned char i;
  
    for (i = 0; i < NUMPLAYERS; i++)
	score[i] = 0;
    ShowScores(score);
  
    return;
}


short
PlayGame(unsigned char deal, unsigned char first)
{
    unsigned char i, j;
    short b, best;
    short key;
    short cmd;
    unsigned char lead = first;

    if (deal > NUMHANDS) {
	do {
	    cmd = GetCmd();
	}
	while (cmd != NEWGAME && cmd != QUIT);
	return cmd;
    }

    trumps = Deal(deal);

    for (i = 0; i < NUMPLAYERS; i++) {
	wins[i] = 0;
    }
    ShowWins(wins);

    RemoveBids();
    for (i = first; i < first + NUMPLAYERS; i++) {
	b = GetBid(i % NUMPLAYERS, deal, first);
	if (b == NEWGAME || b == QUIT) return b;
	bid[i % NUMPLAYERS] = b;
	ShowBid(bid[i % NUMPLAYERS], i % NUMPLAYERS);
    }

    for (i = 0; i < deal; i++) {
	for (j = 0; j < NUMPLAYERS; j++) {
	    played[j] = NOCARD;
	}
	led = NOCARD;
	for (j = lead; j < lead + NUMPLAYERS; j++) {
	    b = GetPlay(j % NUMPLAYERS, deal, lead);
	    if (b == NEWGAME || b == QUIT) return b;

	    ShowPlay(b, j % NUMPLAYERS);
	    if (j % NUMPLAYERS == 0)
		ShowHand(hand[0], deal);
	    played[j % NUMPLAYERS] = b;
	    if (led == NOCARD)
		led = b;
	}
	best = -1;
	for (j = lead; j < lead + NUMPLAYERS; j++) {
	    if (best == -1 || 
		(SUIT(played[j % NUMPLAYERS]) == SUIT(played[best]) && 
		 (TYPE(played[j % NUMPLAYERS]) + 12) % 13 > 
		 (TYPE(played[best]) + 12) % 13) ||
		(SUIT(played[j % NUMPLAYERS]) == trumps && 
		 SUIT(played[best]) != trumps)) {
		best = j % NUMPLAYERS;
	    }
	}
	wins[best]++;
	lead = best;
	ShowWins(wins);
	key = WaitForKey();
	RemovePlays();
	if (key != CONTINUE)
	    return key;
    }
  
    for (i = 0; i < NUMPLAYERS; i++) {
	if (wins[i] == bid[i])
	    score[i] += 10 + bid[i];
    }
    ShowScores(score);

    return NEXTGAME;
}


unsigned char
Deal(unsigned char deal)
{
    unsigned char cards[NUMCARDS];
    unsigned char i, j, k;
    short r;
  
    for (i = 0; i < NUMCARDS; i++) {
	cards[i] = 0;
    }
    for (i = 0; i < deal; i++) {
	for (j = 0; j < NUMPLAYERS; j++) {
	    r = Random(NUMCARDS - (i * NUMPLAYERS + j));
	    for (k = 0; k < NUMCARDS && r >= 0; k++) {
		if (cards[k] == 0)
		    r--;
	    }
	    r = k - 1;
	    cards[r] = 1;
      
	    hand[j][i] = r;
	}
    }
  
    if (deal * NUMPLAYERS < NUMCARDS) {
	r = Random(NUMCARDS - deal * NUMPLAYERS);
	for (k = 0; k < NUMCARDS && r >= 0; k++) {
	    if (cards[k] == 0)
		r--;
	}
	trumps = k - 1;

	ShowTrumps(trumps);
	trumps = SUIT(trumps);
    } 
    else {
	RemoveTrumps();
	trumps = NOSUIT;
    }

    qsort(hand[0], deal, sizeof(unsigned char), (__compar_fn_t)&Cmp);
    ShowHand(hand[0], deal);

    return trumps;
}


short
GetBid(unsigned char player, unsigned char deal, unsigned char first)
{
    short bid = 0;
    unsigned char i, numtrumps = 0;

    if (player == 0)
	return GetPlayerBid(deal);

    for (i = 0; i < deal; i++) {
	if (TYPE(hand[player][i]) == ACE || TYPE(hand[player][i]) == KING ||
	    TYPE(hand[player][i]) == QUEEN)
	    bid++;
	else if (SUIT(hand[player][i]) == trumps)
	    numtrumps++;
    }

    if (deal < 4 && player == first)
	bid++;
    if (numtrumps > deal / 4)
	bid += numtrumps - deal / 4;
    if (bid > deal)
	bid = deal;

    return bid;
}


short
GetPlay(unsigned char player, unsigned char deal, unsigned char first)
{
#ifdef PRINTPROGRESS
    static char suits[] = { 's', 'c', 'h', 'd' };
    static char types[] = { 'A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K' };
#endif
    short cmd = NOCARD, ret;
    unsigned char i;
    unsigned char best = NOCARD;
    unsigned char wanttowin;
    unsigned char winning;
    short smallestwinner, biggestwinner, smallestloser, biggestloser;
    unsigned char legal[NUMCARDS / NUMPLAYERS];
    unsigned char numlegal;
    unsigned char under, over, equal;

    if (player == 0) {
	while (cmd == NOCARD) {
#if 0
	    FlushIn();
#endif
	    cmd = GetCmd();
	    if (cmd < 0)
		return cmd;
	    else if (led != NOCARD && 
		     SUIT(hand[player][cmd]) != SUIT(led)) {
		for (i = 0; i < deal; i++)
		    if (hand[player][i] != NOCARD &&
			SUIT(hand[player][i]) == SUIT(led))
			cmd = NOCARD;
	    }
	}
	ret = hand[player][cmd];
	hand[player][cmd] = NOCARD;
	return ret;
    }

#ifdef PRINTPROGRESS
    if (led == NOCARD) fprintf(stderr, "\n");
    fprintf(stderr, "Player %d : ", player);
    for (i = 0; i < deal; i++)
	if (hand[player][i] != NOCARD)
	    fprintf(stderr, "%c%c ", types[TYPE(hand[player][i])],
		    suits[SUIT(hand[player][i])]);
#endif

    if (led != NOCARD) {
	/*
	 * Find which card that has been played is winning.
	 */
	winning = led;
	for (i = (first + 1) % NUMPLAYERS;
	     i != player;
	     i = (i + 1) % NUMPLAYERS) {
	    if ((SUIT(played[i]) == SUIT(winning)
		 && TYPECMP(played[i], winning) > 0) || 
		(SUIT(played[i]) == trumps && SUIT(winning) != trumps))
		winning = played[i];
	}

	/*
	 * Find all the legal cards.  First a pass looking for suit cards.
	 */
	numlegal = 0;
	for (i = 0; i < deal; i++) {
	    if (hand[player][i] != NOCARD
		&& SUIT(hand[player][i]) == SUIT(led)) {
		legal[numlegal] = i;
		numlegal++;
	    }
	}

#ifdef PRINTPROGRESS
	fprintf(stderr, " legal : %d ", numlegal);
#endif

	smallestloser = biggestloser = smallestwinner = biggestwinner = -1;
	if (numlegal > 0) {
	    /*
	     * Look amongst suit cards for all winners and losers.
	     */
	    for (i = 0; i < numlegal; i++) {
		if ((SUIT(led) == trumps || SUIT(winning) != trumps) && 
		    TYPECMP(winning, hand[player][legal[i]]) < 0) {
		    if (smallestwinner == -1)
			smallestwinner = biggestwinner = legal[i];
		    else if (TYPECMP(hand[player][smallestwinner],
				     hand[player][legal[i]]) > 0)
			smallestwinner = legal[i];
		    else if (TYPECMP(hand[player][biggestwinner],
				     hand[player][legal[i]]) < 0)
			biggestwinner = legal[i];
		} else {
		    if (smallestloser == -1)
			smallestloser = biggestloser = legal[i];
		    else if (TYPECMP(hand[player][smallestloser],
				     hand[player][legal[i]]) > 0)
			smallestloser = legal[i];
		    else if (TYPECMP(hand[player][biggestloser],
				     hand[player][legal[i]]) < 0)
			biggestloser = legal[i];
		}
	    }
	} else {
	    /*
	     * Look amongst all cards for winners (trumps) and losers (others).
	     */
	    for (i = 0; i < deal; i++) {
		if (hand[player][i] != NOCARD) {
		    if (SUIT(hand[player][i]) == trumps &&
			(SUIT(winning) != trumps
			 || TYPECMP(winning, hand[player][i]) < 0)) {
			if (smallestwinner == -1)
			    smallestwinner = biggestwinner = i;
			else if (TYPECMP(hand[player][smallestwinner],
					 hand[player][i]) > 0)
			    smallestwinner = i;
			else if (TYPECMP(hand[player][biggestwinner],
					 hand[player][i]) < 0)
			    biggestwinner = i;
		    } 
		    else {
			if (smallestloser == -1)
			    smallestloser = biggestloser = i;
			else if ((SUIT(hand[player][smallestloser]) == trumps
				  && SUIT(hand[player][i]) != trumps)
				 || TYPECMP(hand[player][smallestloser],
					    hand[player][i]) > 0)
			    smallestloser = i;
			else if ((SUIT(hand[player][smallestloser]) != trumps
				  && SUIT(hand[player][i]) == trumps)
				 || TYPECMP(hand[player][biggestloser],
					    hand[player][i]) < 0)
			    biggestloser = i;
		    }
		}
	    }
	}
    }

    under = over = equal = 0;
    for (i = 0; i < NUMPLAYERS; i++) {
	if (i != player) {
	    if (wins[player] < bid[player])
		under++;
	    else if (wins[player] > bid[player])
		over++;
	    else
		equal++;
	}
    }

    if (wins[player] < bid[player])
	wanttowin = TRUE;
    else
	wanttowin = FALSE;

    if (led == NOCARD) {
	for (i = 0; i < deal; i++) {
	    if (hand[player][i] != NOCARD) {
		if (best == NOCARD ||
		    (wanttowin &&
		     TYPECMP(hand[player][best], hand[player][i]) < 0) ||
		    (!wanttowin &&
		     TYPECMP(hand[player][best], hand[player][i]) > 0))
		    best = i;
	    }
	}
    }
    else {
#ifdef PRINTPROGRESS
	if (smallestloser != -1)
	    fprintf(stderr, " sl : %c%c",
		    types[TYPE(hand[player][smallestloser])],
		    suits[SUIT(hand[player][smallestloser])]);
	if (biggestloser != -1)
	    fprintf(stderr, " bl : %c%c",
		    types[TYPE(hand[player][biggestloser])],
		    suits[SUIT(hand[player][biggestloser])]);
	if (smallestwinner != -1)
	    fprintf(stderr, " sw : %c%c",
		    types[TYPE(hand[player][smallestwinner])],
		    suits[SUIT(hand[player][smallestwinner])]);
	if (biggestwinner != -1)
	    fprintf(stderr, " bw : %c%c ",
		    types[TYPE(hand[player][biggestwinner])],
		    suits[SUIT(hand[player][biggestwinner])]);
#endif

	if (wanttowin) {
	    if (smallestwinner != -1) {
		if (player == (first - 1 + NUMPLAYERS) % NUMPLAYERS)
		    best = smallestwinner;
		else
		    best = biggestwinner;
	    }
	    else
		best = smallestloser;
	}
	else {
	    if (biggestloser != -1)
		best = biggestloser;
	    else if (equal >= under)
		best = smallestwinner;
	    else
		best = biggestwinner;
	}
    }

#ifdef PRINTPROGRESS
    fprintf(stderr, " playing : %c%c\n", types[TYPE(hand[player][best])],
	    suits[SUIT(hand[player][best])]);
#endif

    ret = hand[player][best];
    hand[player][best] = NOCARD;

    return ret;
}


int Cmp(unsigned char *a, unsigned char *b)
{
    static unsigned char remap[4] = { 0, 2, 1, 3 };
    unsigned char arank = CARD(remap[SUIT(*a)], (TYPE(*a) + 12) % 13);
    unsigned char brank = CARD(remap[SUIT(*b)], (TYPE(*b) + 12) % 13);

    if (arank < brank) return 1;
    if (arank > brank) return -1;
    return 0;
}
