contest/
contest/Merc21/
contest/Merc21/log/
contest/Merc21/player/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "game.h"
#include "deck.h"
#include "hand.h"
#include "score.h"
#include "merc.h"

void ShowValidCommands( CHAR_DATA *ch )
{
    static char buf[MAX_STRING_LENGTH];
    static char commands[MAX_STRING_LENGTH];
    commands[0] = '\0';

    if ( ch->deck != NULL && ch->hand != NULL )
    {
	if ( GameViewHand(ch->deck->Game, ch->deck->Turn) )
	   strcat( commands, " hand" );
	if ( GameCanBet(ch->deck->Game, ch->deck->Turn) )
	   strcat( commands, " bet" );
	if ( GameCanFold(ch->deck->Game, ch->deck->Turn) )
	   strcat( commands, " fold" );
	if ( GameCanDiscard(ch->deck->Game, ch->deck->Turn) )
	   strcat( commands, " discard" );
	if ( GameCanDraw(ch->deck->Game, ch->deck->Turn) )
	   strcat( commands, " draw" );
	if ( GameCanStick(ch->deck->Game, ch->deck->Turn) )
	   strcat( commands, " stick" );
	if ( GameCanDouble(ch->deck->Game, ch->deck->Turn) )
	   strcat( commands, " double" );
    }

    if ( commands[0] == '\0' )
	strcpy( commands, " none" );

    sprintf( buf, "Card commands for turn %d:", ch->deck->Turn );
    strcat( buf, commands );
    strcat( buf, "\n\r" );
    send_to_char( buf, ch );
}

void FlushPlayers( CHAR_DATA *ch )
{
    if ( ch->deck != NULL && ch->hand != NULL )
    {
	CHAR_DATA *current, *next = NULL;

	for ( current = ch; current != NULL; current = next )
	{
	    CHAR_DATA *prev = current->prev_card_player;
	    next = current->next_card_player;

	    /* Dispose of their hand and deck */
	    if ( --current->deck->Instances <= 0 )
		free( current->deck );
	    current->deck = NULL;
	    free( current->hand );
	    current->hand = NULL;

	    /* Strip the character from the player list */
	    if ( prev != NULL )
	    {
		prev->next_card_player = next;
		current->prev_card_player = NULL;
	    }
	    if ( next != NULL )
	    {
		next->prev_card_player = prev;
		current->next_card_player = NULL;
	    }
	}
    }
}

void RevealScore( CHAR_DATA *ch )
{
    if ( ch->deck != NULL && ch->hand != NULL )
    {
        char buf[MAX_STRING_LENGTH];

	CHAR_DATA *current, *winner = ch;
	combination_t HighCombination = eCombinationHighCard;
        int TopScore = -1;
        int WinnerCount = 0;
        char HighScore[16] = {'\0'};
	int gold = ch->deck->Pot;

	for ( current = ch; current != NULL; current = current->next_card_player )
	{
	    /* Calculate score */
	    sprintf( buf, "You reveal your hand: %s\n\r", 
		HandShow(current->hand, eFalse) );
	    send_to_char( buf, current );
	    sprintf( buf, "$n reveals $s hand: %s", 
		HandShow(current->hand, eFalse) );
	    act( buf, current, NULL, NULL, TO_ROOM );

	    if ( GameScoring(ch->deck->Game) == eScorePoker )
	    {
		combination_t Combination = eCombinationMax;

		strncpy( buf, ScorePoker(current->hand, &Combination), 15 );

		if ( Combination > HighCombination )
		{
		    /* We've a new high score */
		    HighCombination = Combination;
		    strcpy( HighScore, buf );
		    winner = current;
		}
		else if ( Combination == HighCombination )
		{
		    int i; /* Loop counter */

		    for ( i = 0; HighScore[i] != '\0' && buf[i] != '\0'; ++i )
		    {
			if ( HighScore[i] > buf[i] )
			{
			    /* We've a new high score */
			    HighCombination = Combination;
			    strcpy( HighScore, buf );
			    winner = current;
			}
		    }
		}
	    }
	    else if ( GameScoring(ch->deck->Game) == eScoreBlackjack )
	    {
		int Score = ScoreBlackjack(current->hand);

		if ( Score > TopScore )
		{
		    /* We've a new high score */
		    TopScore = Score;
		    winner = current;
		    WinnerCount = 1;
		}
		else if ( Score == TopScore )
		{
		    /* We've more than one winner */
		    WinnerCount++;
		}
	    }
	    else /* Add support for your other scoring systems here */
	    {
		bug( "RevealScore: Function called with unsupported scoring system.\n", 0 );
	    }
	}

	if ( GameSoloBet(ch->deck->Game) )
	{
	    /* Find the dealer */
	    CHAR_DATA *dealer = NULL;
	    for ( current = ch; current != NULL; current = current->next_card_player )
	    {
		if ( current->hand->bDealer )
		    dealer = current;
	    }

	    if ( dealer != NULL )
	    {
		int DealerScore = ScoreBlackjack(dealer->hand);
		for ( current = ch; current != NULL; current = current->next_card_player )
		{
		    if ( current != dealer )
		    {
			int Score = ScoreBlackjack(current->hand);
			if ( Score == 21 && current->hand->Size == 2 )
			{
			    int Winnings = (current->hand->Bet*5)/2; /* 3:2 payout */

			    sprintf( buf, "\n\rBlackjack!  You collect %d gold.\n\r", Winnings );
			    send_to_char( buf, current );
			    sprintf( buf, "\n\rBLACKJACK!  $n collects %d gold.", Winnings );
			    act( buf, current, NULL, NULL, TO_ROOM );

			    /* You get twice your bet back */
			    current->gold += Winnings;
			    current->deck->Pot -= Winnings;
			}
			else if ( Score > 0 && (Score > DealerScore || Score == 21) )
			{
			    int Winnings = current->hand->Bet*2; /* 1:1 payout */

			    sprintf( buf, "\n\rYou win!  You collect %d gold.\n\r", Winnings );
			    send_to_char( buf, current );
			    sprintf( buf, "\n\rWINNER!  $n collects %d gold.", Winnings );
			    act( buf, current, NULL, NULL, TO_ROOM );

			    /* You get twice your bet back */
			    current->gold += Winnings;
			    current->deck->Pot -= Winnings;
			}
			else if ( Score == DealerScore )
			{
			    sprintf( buf, "\n\rYou match the house.  You recover your %d gold bet.\n\r", current->hand->Bet );
			    send_to_char( buf, current );
			    sprintf( buf, "\n\r$n matches the house, and recovers $s %d gold bet.", current->hand->Bet );
			    act( buf, current, NULL, NULL, TO_ROOM );

			    /* You get twice your bet back */
			    current->gold += current->hand->Bet;
			    current->deck->Pot -= current->hand->Bet;
			}
			else /* The player loses */
			{
			    send_to_char( "\n\rYou lose to the house.\n\r", current );
			}
		    }
		}

		/* The dealer empties the pot */
		if ( dealer->deck->Pot > 0 )
		{
		    dealer->gold += dealer->deck->Pot;

		    sprintf( buf, "\n\rYou empty the remaining %d gold from the pot.\n\r", dealer->deck->Pot );
		    send_to_char( buf, dealer );
		    sprintf( buf, "\n\r$n collects the remaining %d gold from the pot.\n\r", dealer->deck->Pot );
		    act( buf, dealer, NULL, NULL, TO_ROOM );
		}
		else if ( dealer->deck->Pot < 0 )
		{
		    dealer->deck->Pot = abs(dealer->deck->Pot);
		    dealer->gold -= dealer->deck->Pot;

		    sprintf( buf, "\n\rYou had to add an extra %d gold to the pot.\n\r", dealer->deck->Pot );
		    send_to_char( buf, dealer );
		}
	    }
	}
	else if ( WinnerCount > 1 )
	{
	    for ( current = ch; current != NULL; current = current->next_card_player )
	    {
		int Score = ScoreBlackjack(current->hand);
		if ( Score > 0 && Score == TopScore )
		{
		    sprintf( buf, "\n\rYou're one of %d winners!  You collect %d gold from the pot.\n\r", WinnerCount, gold/WinnerCount );
		    send_to_char( buf, current );
		    sprintf( buf, "\n\rJOINT WIN!  $n collects %d gold from the pot.\n\r", gold/WinnerCount );
		    act( buf, current, NULL, NULL, TO_ROOM );
		    winner->gold += (gold/WinnerCount);
		}
	    }
	}
	else /* There was a clear winner */
	{
	    sprintf( buf, "\n\rYou win!  You collect %d gold from the pot.\n\r", gold );
	    send_to_char( buf, winner );

	    sprintf( buf, "\n\rWINNER!  $n collects %d gold from the pot.\n\r", gold );
	    act( buf, winner, NULL, NULL, TO_ROOM );
	    winner->gold += gold;
	}

	/* Flush the players out of the game */
	FlushPlayers(ch);
    }
}

void ShareCard( CHAR_DATA *ch, card_t card, boolean_t show )
{
    if ( ch->deck != NULL && ch->hand != NULL )
    {
        CHAR_DATA *next;
        for ( next = ch; next != NULL; next = next->next_card_player )
        {
	    /* Give each player a copy of the card */
            (void)HandAddCard(next->hand, card);

	    /* Show the player their new hand, if required */
	    if ( show )
	    {
		char buf[MAX_STRING_LENGTH];

		sprintf( buf, "Including shared cards, you now have the following hand: %s\n\r\n\r", 
		    HandShow(next->hand, eFalse) );
		send_to_char( buf, next );
		send_to_char( HandShow(next->hand, eTrue), next );
	    }
	}
    }
}

void NextPlayer( CHAR_DATA *ch, bool bCheckWin )
{
    char buf[MAX_STRING_LENGTH];
    if ( ch->deck != NULL && ch->hand != NULL )
    {
	/* You've drawn one or more cards, so your turn is over */
	ch->play_turn = FALSE;

	if ( GameSoloBet(ch->deck->Game) )
	{
	    sprintf( buf, "It's now your turn (current bet: %dgp).\n\r\n\r", 
		ch->hand->Bet );
	}
	else /* Its a shared bet */
	{
	    sprintf( buf, "It's now your turn (pot: %dgp, current bet: %dgp).\n\r\n\r", 
		ch->deck->Pot, ch->deck->Bet );
	}

	if ( ch->next_card_player != NULL )
	{
	    /* It's now the next players turn */
	    CHAR_DATA *next = ch->next_card_player;

	    next->play_turn = TRUE;

	    /* If there are two players and one just lost, the other wins */
	    if ( bCheckWin && next->deck->Instances == 2 )
	    {
		/* Reveal the scores */
		RevealScore( next );
	    }
	    else /* There are still other players */
	    {
		send_to_char( buf, next );
		ShowValidCommands( next );
		act( "It is now $n's turn.", next, NULL, NULL, TO_ROOM );
	    }
	}
	else /* We've reached the last player, so go back to the first one */
	{
	    CHAR_DATA *prev = ch->prev_card_player;
	    if ( prev == NULL )
	    {
		/* There's only one player, so make them win */
		ch->play_turn = TRUE;
		ch->deck->Turn = GameLastTurn(ch->deck->Game);
		RevealScore( ch );
	    }
	    else /* Keep going back until we reach the start */
	    {
		while ( prev->prev_card_player != NULL )
		    prev = prev->prev_card_player;

		/* The first player has a turn again */
		prev->play_turn = TRUE;

		/* If there are two players and one just lost, the other wins */
		if ( bCheckWin && prev->deck->Instances == 2 )
		{
		    /* Reveal the scores */
		    RevealScore( prev );
		}
		else if ( ++prev->deck->Turn > GameLastTurn(prev->deck->Game) )
		{
		    /* Reveal the scores */
		    RevealScore( prev );
		}
		else /* The game is not yet finished */
		{
		    int BurnedCards = GameBurnCards(prev->deck->Game, 
			prev->deck->Turn);
		    int SharedCards = GameSharedDraw(prev->deck->Game, 
			prev->deck->Turn);

		    send_to_char( buf, prev );
		    ShowValidCommands( prev );
		    act( "It is now $n's turn.", prev, NULL, NULL, TO_ROOM );

		    /* Any card burning should be done now */
		    if ( BurnedCards > 0 )
		    {
			sprintf( buf, "You burn %d card%s from the top of the deck.\n\r", 
			    BurnedCards, BurnedCards == 1 ? "" : "s" );
			send_to_char( buf, prev );
			sprintf( buf, "$n burns %d card%s from the top of the deck.", 
			    BurnedCards, BurnedCards == 1 ? "" : "s" );
			act( buf, prev, NULL, NULL, TO_ROOM );

			for ( ; BurnedCards > 0; --BurnedCards )
			{
			    /* Draw a card and cast it into the void */
			    (void)DeckDrawCard(prev->deck);
			}
		    }

		    /* If there are any shared cards to be placed, do so now */
		    if ( SharedCards > 0 )
		    {
			sprintf( buf, "You draw %d new card%s from the deck and place %s face-up on the table.\n\r", 
			    SharedCards, SharedCards == 1 ? "" : "s", SharedCards == 1 ? "it" : "them" );
			send_to_char( buf, prev );
			sprintf( buf, "$n draws %d new card%s from the deck and places %s face-up on the table.", 
			    SharedCards, SharedCards == 1 ? "" : "s", SharedCards == 1 ? "it" : "them" );
			act( buf, prev, NULL, NULL, TO_ROOM );

			for ( ; SharedCards > 0; --SharedCards )
			{
			    /* Draw a card and give each player a copy */
			    card_t card = DeckDrawCard(prev->deck);
			    ShareCard( prev, card, SharedCards == 1 );
			}
		    }
		}
	    }
	}
    }
}

void do_bet( CHAR_DATA *ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];

    if ( ch->deck == NULL || ch->hand == NULL )
    {
	send_to_char( "You need to start a game before you can bet.\n\r", ch );
    }
    else if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) )
    {
	send_to_char( "The game is already over.\n\r", ch );
    }
    else if ( !GameCanBet(ch->deck->Game, 0) )
    {
	sprintf( buf, "You cannot place bets in %s.\n\r", 
	    GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( !GameCanBet(ch->deck->Game, ch->deck->Turn) )
    {
	sprintf( buf, "You cannot place bets in turn %d of %s.\n\r", 
	    ch->deck->Turn, GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( ch->deck->Turn > 0 && ch->deck->Playing == eFalse )
    {
	send_to_char( "You cannot bet until the first hand has been dealt.\n\r", ch );
    }
    else if ( ch->play_turn == FALSE )
    {
	send_to_char( "You can only bet on your turn.\n\r", ch );
    }
    else if ( argument[0] == '\0' )
    {
	if ( ch->deck->Turn == 0 )
	{
	    send_to_char( "How much do you want to set the minimum bet to?\n\r", ch );
	}
	else if ( GameSoloBet(ch->deck->Game) )
	{
	    sprintf( buf, "How much do you wish to bet?  Your current bet is %d gold.\n\r", 
		ch->hand->Bet );
	    send_to_char( buf, ch );
	}
	else /* Its a shared bet */
	{
	    sprintf( buf, "How much do you wish to bet?  The current bet is %d gold.\n\r", 
		ch->deck->Bet );
	    send_to_char( buf, ch );
	}
    }
    else if ( !strcmp( argument, "call" ) )
    {
	if ( ch->deck->Turn == 0 )
	{
	    send_to_char( "You cannot 'bet call' for the minimum bet.\n\r", ch );
	}
	else if ( ch->gold < ch->deck->Bet )
	{
	    send_to_char( "You don't have enough gold to call the current bet.  You'll have to fold.\n\r", ch );
	}
	else /* They can call the current bet */
	{
	    if ( ch->deck->Bet == 0 )
	    {
		send_to_char( "You defer to the next player.\n\r", ch );
		act( "$n defers, chosing not to place a bet.", ch, NULL, NULL, TO_ROOM );
	    }
	    else /* It's a proper bet */
	    {
		/* Update the pot and the characters gold */
		ch->hand->Bet = ch->deck->Bet;
		ch->deck->Pot += ch->deck->Bet;
		ch->gold -= ch->deck->Bet;

		sprintf( buf, "Your call the current bet of %d gold.\n\r", ch->deck->Bet );
		send_to_char( buf, ch );
		sprintf( buf, "$n calls the current bet of %d gold.", ch->deck->Bet );
		act( buf, ch, NULL, NULL, TO_ROOM );
	    }

	    /* Turn over - next player */
	    NextPlayer(ch, FALSE);
	}
    }
    else if ( !is_number(argument) )
    {
	if ( GameSoloBet(ch->deck->Game) )
	{
	    sprintf( buf, "Please specify a number of gold pieces to bet.  The current bet is %d gold.\n\r", 
		ch->deck->Bet );
	    send_to_char( buf, ch );
	}
	else /* Its a shared bet */
	{
	    sprintf( buf, "Please specify a number of gold pieces to bet.  Your current bet is %d gold.\n\r", 
		ch->hand->Bet );
	    send_to_char( buf, ch );
	}
    }
    else if ( ch->deck->Turn == 0 )
    {
	int gold = atoi(argument);
	if ( ch->deck->Bet > 0 )
	{
	    sprintf( buf, "You've already set a minimum bet of %d gold.  You should now 'deal'.\n\r", 
		ch->deck->Bet );
	    send_to_char( buf, ch );
	}
	else if ( gold <= 0 )
	{
	    send_to_char( "You must set a minimum bet of at least 1 gold.\n\r", ch );
	}
	else if ( gold > ch->gold )
	{
	    send_to_char( "You don't have enough gold to bet that much.\n\r", ch );
	}
	else /* It's a valid bet */
	{
	    /* Set the bet - but don't remove gold at this point */
	    ch->hand->Bet = gold;
	    ch->deck->Bet = gold;

	    if ( GameMinimumBet(ch->deck->Game) ) 
	    {
		sprintf( buf, "Minimum bet is set to %d gold.  Type 'deal' to start the game.\n\r", gold );
		send_to_char( buf, ch );
		sprintf( buf, "$n begins a game of %s with a minimum bet of %d gold.  Type 'play' to join.", GameName(ch->deck->Game), gold );
		act( buf, ch, NULL, NULL, TO_ROOM );
	    }
	    else /* A minimum bet wasn't required */
	    {
		sprintf( buf, "You set the minimum bet to %d gold.  You should now 'deal'.\n\r", ch->deck->Bet );
		send_to_char( buf, ch );
		sprintf( buf, "$n sets the minimum bet to %d gold.", ch->deck->Bet );
		act( buf, ch, NULL, NULL, TO_ROOM );
	    }
	}
    }
    else if ( GameSoloBet(ch->deck->Game) ) 
    {
	int gold = atoi(argument);
	if ( gold < ch->deck->Bet )
	{
	    sprintf( buf, "You must at least match the minimum bet of %d gold.\n\r", 
		ch->deck->Bet );
	    send_to_char( buf, ch );
	}
	else if ( gold <= ch->hand->Bet )
	{
	    sprintf( buf, "You must exceed your current bet of %d gold.\n\r", 
		ch->hand->Bet );
	    send_to_char( buf, ch );
	}
	else if ( gold > ch->gold )
	{
	    send_to_char( "You don't have enough gold to bet that much.\n\r", ch );
	}
	else /* It's a valid bet */
	{
	    /* Remove their old bet, so that it can be replaced */
	    ch->deck->Pot -= ch->hand->Bet;
	    ch->gold += ch->hand->Bet;

	    /* Apply the new bet, update the pot, and the characters gold */
	    ch->hand->Bet = gold;
	    ch->deck->Bet = gold;
	    ch->deck->Pot += gold;
	    ch->gold -= gold;

	    sprintf( buf, "Your raise your bet to %d gold.\n\r", ch->hand->Bet );
	    send_to_char( buf, ch );
	    sprintf( buf, "$n raises $s bet to %d gold.", ch->hand->Bet );
	    act( buf, ch, NULL, NULL, TO_ROOM );

	    /* Turn over - next player */
	    NextPlayer(ch, FALSE);
	}
    }
    else /* We've got a numeric value of gold */
    {
	int gold = atoi(argument);
	if ( gold <= ch->deck->Bet )
	{
	    sprintf( buf, "You must exceed the current bet of %d gold, or 'bet call' to match it.\n\r", 
		ch->deck->Bet );
	    send_to_char( buf, ch );
	}
	else if ( gold > ch->gold )
	{
	    send_to_char( "You don't have enough gold to bet that much.\n\r", ch );
	}
	else /* It's a valid bet */
	{
	    /* Remove their old bet, so that it can be replaced */
	    ch->deck->Pot -= ch->hand->Bet;
	    ch->gold += ch->hand->Bet;

	    /* Update the bet, the pot, and the characters gold */
	    ch->hand->Bet = gold;
	    ch->deck->Bet = gold;
	    ch->deck->Pot += gold;
	    ch->gold -= gold;

	    sprintf( buf, "Your raise the bet to %d gold.\n\r", ch->deck->Bet );
	    send_to_char( buf, ch );
	    sprintf( buf, "$n raises the bet to %d gold.", ch->deck->Bet );
	    act( buf, ch, NULL, NULL, TO_ROOM );

	    /* Turn over - next player */
	    NextPlayer(ch, FALSE);
	}
    }
}

void do_deal( CHAR_DATA *ch, char *argument )
{
    if ( ch->deck == NULL || ch->hand == NULL )
    {
	send_to_char( "You need to start a game before you can deal.\n\r", ch );
    }
    else if ( ch->deck->Playing == eTrue )
    {
	send_to_char( "You can only deal at the start of a game.\n\r", ch );
    }
    else if ( ch->play_turn == FALSE )
    {
	send_to_char( "You're not the dealer.\n\r", ch );
    }
    else if ( GameMinimumBet(ch->deck->Game) && ch->deck->Bet <= 0 ) 
    {
	send_to_char( "You must first set the minimum bet (eg 'bet 10').\n\r", ch );
    }
    else if ( ch->deck->Instances < 2 )
    {
	send_to_char( "You need at least 2 players for a game of cards.\n\r", ch );
    }
    else /* Go ahead and deal the cards */
    {
	int Deal = GameInitialDraw(ch->deck->Game);

	/* Indicate that the game has begun */
	ch->deck->Playing = eTrue;

        while ( HandCountCards(ch->hand) < Deal )
        {
	    CHAR_DATA *prev;
            
	    for ( prev = ch; prev != NULL; prev = prev->prev_card_player )
	    {
		/* Draw a card from the deck and put it in their hand */
		card_t card = DeckDrawCard(prev->deck);
		(void)HandAddCard(prev->hand, card);

		if ( HandCountCards(prev->hand) == Deal )
		{
		    char buf[MAX_STRING_LENGTH];

		    /* Check explicitly for turn 1 (we're in turn 0) */
		    if ( GameViewHand(ch->deck->Game, 1) )
		    {
			if ( ch == prev )
			{
			    sprintf( buf, "You deal yourself %d cards: %s\n\r\n\r", 
				Deal, HandShow(prev->hand, eFalse) );
			    send_to_char( buf, ch );
			}
			else /* Dealt to someone else */
			{
			    sprintf( buf, "$n deals you %d cards: %s\n\r", 
				Deal, HandShow(prev->hand, eFalse) );
			    act( buf, ch, NULL, prev, TO_VICT );
			}
			send_to_char( HandShow(prev->hand, eTrue), prev );
		    }
		    else /* We're not allowed to show them the cards */
		    {
			if ( ch == prev )
			{
			    sprintf( buf, "You deal yourself %d cards face-down on the table.\n\r", Deal );
			    send_to_char( buf, ch );

			    if ( ch->deck->Game == eGameBlackjack )
			    {
				sprintf( buf, "You flip one of the cards over to reveal the %s.\n\r", CardName(card) );
				send_to_char( buf, ch );
				sprintf( buf, "$n deals $mself %d cards, one of which he reveals as the %s.", 
				    Deal, CardName(card) );
				act( buf, ch, NULL, NULL, TO_ROOM );
			    }
			}
			else /* Dealt to someone else */
			{
			    sprintf( buf, "$n deals you %d cards face-down on the table.", Deal );
			    act( buf, ch, NULL, prev, TO_VICT );
			}
		    }
		}
	    }
	}

	/* Now we begin turn 1 */
	NextPlayer(ch, FALSE);
    }
}

void do_discard( CHAR_DATA *ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];
    card_t card;
    suit_t suit;
    rank_t rank;

    if ( ch->deck == NULL || ch->hand == NULL )
    {
	send_to_char( "You need to start playing before you can discard a card.\n\r", ch );
	return;
    }

    /* The game is over */
    if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) )
    {
	send_to_char( "The game is already over.\n\r", ch );
	return;
    }

    if ( !GameCanDiscard(ch->deck->Game, 0) )
    {
	sprintf( buf, "You cannot discard cards in %s.\n\r", 
	    GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
	return;
    }

    /* You can only discard on your turn */
    if ( ch->play_turn == FALSE )
    {
	send_to_char( "You can only discard on your own turn.\n\r", ch );
	return;
    }

    /* You can only discard on once the game has started */
    if ( ch->deck->Playing == eFalse )
    {
	send_to_char( "You cannot discard any cards until the dealer has dealt the first hand.\n\r", ch );
	return;
    }

    if ( !GameCanDiscard(ch->deck->Game, ch->deck->Turn) )
    {
	sprintf( buf, "You cannot discard cards in turn %d of %s.\n\r", 
	    ch->deck->Turn, GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
	return;
    }

    /* Can you discard that many cards? */
    if ( HandCountCards(ch->hand) <= GameInitialDraw(ch->deck->Game) - 
	GameCanDiscard(ch->deck->Game, ch->deck->Turn) )
    {
	sprintf( buf, "You may only discard up to %d cards in turn %d of %s.\n\r", 
	    GameCanDiscard(ch->deck->Game, ch->deck->Turn), ch->deck->Turn, 
	    GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	return;
    }

    if ( argument[0] == '\0' || strlen(argument) != 2 )
    {
	send_to_char( "Please specify which card you wish to discard (eg 'discard H5').\n\r", ch );
	return;
    }

    /* Find the suit and rank of the card they're trying to discard */
    suit = CardSuit( UPPER(argument[0]) );
    rank = CardRank( UPPER(argument[1]) );

    if ( suit == eSuitMax || rank == eRankMax )
    {
	/* Swap them around, in case the player did them the wrong way */
	suit = CardSuit( UPPER(argument[1]) );
	rank = CardRank( UPPER(argument[0]) );

	if ( suit == eSuitMax || rank == eRankMax )
	{
	    sprintf( buf, "There is no such card as '%s'.\n\r", argument );
	    send_to_char( buf, ch );
	    return;
	}
    }

    /* Find the card type, based on its suit and rank */
    card = CardType( suit, rank );

    /* Try to remove the card from their hand */
    if ( HandRemoveCard(ch->hand, card) == eFalse )
    {
	sprintf( buf, "You're not even holding the %s (%s)!\n\r", 
	   CardName(card), CardValue(card) );
	send_to_char( buf, ch );
	return;
    }

    /* Try to put the card back onto the bottom of the deck */
    if ( DeckReturnCard(ch->deck, card) == eFalse )
    {
	sprintf( buf, "do_discard: Tried to return '%s' to deck, but it was already there!", CardValue(card) );
	bug( buf, 0 );
    }

    sprintf( buf, "You discard the %s (%s) from your hand.\n\r", 
	CardName(card), CardValue(card) );
    send_to_char( buf, ch );
    act( "$n discards a card from $s hand.", ch, NULL, NULL, TO_ROOM );
}

void do_draw( CHAR_DATA *ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];

    if ( ch->deck == NULL || ch->hand == NULL )
    {
	send_to_char( "You need to start playing before you can draw a card.\n\r", ch );
    }
    else if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) )
    {
	send_to_char( "The game is already over.\n\r", ch );
    }
    else if ( !GameCanDraw(ch->deck->Game, 0) )
    {
	sprintf( buf, "You cannot draw cards in %s.\n\r", 
	    GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( !GameCanDraw(ch->deck->Game, ch->deck->Turn) )
    {
	sprintf( buf, "You cannot draw cards in turn %d of %s.\n\r", 
	    ch->deck->Turn, GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( ch->play_turn == FALSE )
    {
	send_to_char( "You can only draw a card on your own turn.\n\r", ch );
    }
    else if ( ch->deck->Playing == eFalse )
    {
	send_to_char( "You cannot draw any cards until the dealer has dealt the first hand.\n\r", ch );
    }
    else if ( GameCanDraw(ch->deck->Game, ch->deck->Turn) == 1 )
    {
	/* Draw a card from the deck and put it in their hand */
	card_t card = DeckDrawCard(ch->deck);

	if ( card == eCardMax )
	{
	    bug( "do_draw: Managed to end up with an empty deck.", 0 );
	    send_to_char( "The deck is empty!\n\r", ch );
	}
	else if ( HandAddCard(ch->hand, card) == eFalse )
	{
	    bug( "do_draw: Duplicate card found in hand.", 0 );
	    send_to_char( "You were unable to draw the card.\n\r", ch );
	}
	else /* They drew the card fine */
	{
	    int Score = ScoreBlackjack(ch->hand);
	    if ( Score > 0 && Score <= 21 )
	    {
		sprintf( buf, "You draw the %s from the deck.\n\r", CardName(card) );
		send_to_char( buf, ch );
		sprintf( buf, "$n draws a new card from the deck." );
		act( buf, ch, NULL, NULL, TO_ROOM );
	    }
	    else /* They've bone bust */
	    {
		CHAR_DATA *prev = ch->prev_card_player;
		CHAR_DATA *next = ch->next_card_player;

		sprintf( buf, "You draw the %s from the deck, going bust!\n\r", CardName(card) );
		send_to_char( buf, ch );
		sprintf( buf, "$n draws a new card from the deck, and goes bust!" );
		act( buf, ch, NULL, NULL, TO_ROOM );

		/* Turn over - next player */
		NextPlayer(ch, TRUE);

		/* Destroy the hand (no need to remove the cards beforehand) */
	        if ( ch->hand != NULL )
	        {
		    free(ch->hand);
	            ch->hand = NULL;
		}

		/* Strip the character from the player list */
		if ( prev != NULL )
		{
		    prev->next_card_player = next;
		    ch->prev_card_player = NULL;
		}
		if ( next != NULL )
		{
		    next->prev_card_player = prev;
		    ch->next_card_player = NULL;
		}

		/* If they're the last person using that deck, destroy it */
	        if ( ch->deck != NULL )
	        {
		    if ( --ch->deck->Instances == 0 )
			free(ch->deck);

		    ch->deck = NULL;
		}
	    }
	}
    }
    else if ( HandCountCards(ch->hand) == GameFullHand(ch->deck->Game) )
    {
	sprintf( buf, "You've already got a full hand of %d cards.\n\r", 
	    GameFullHand(ch->deck->Game) );
	send_to_char( buf, ch );
    }
    else /* They're playing, so draw a card */
    {
	int CardsDrawn = 0;
	int FullHand = GameFullHand(ch->deck->Game);

        while ( HandCountCards(ch->hand) < FullHand )
	{
	    /* Draw a card from the deck and put it in their hand */
	    card_t card = DeckDrawCard(ch->deck);

	    if ( card == eCardMax )
            {
		bug( "do_draw: Managed to end up with an empty deck.", 0 );
		send_to_char( "The deck is empty!\n\r", ch );
		break;
            }
            else if ( HandAddCard(ch->hand, card) == eTrue )
            {
		CardsDrawn++;
	    }
	    else /* They were unable to add the card to their hand */
	    {
		bug( "do_draw: Duplicate card found in hand.", 0 );
		send_to_char( "You were unable to draw the card.\n\r", ch );
		break;
	    }
	}

	sprintf( buf, "You draw %d new card%s from the deck.\n\r", 
	    CardsDrawn, CardsDrawn == 1 ? "" : "s" );
	send_to_char( buf, ch );
	sprintf( buf, "$n draws %d new card%s from the deck.", 
	    CardsDrawn, CardsDrawn == 1 ? "" : "s" );
	act( buf, ch, NULL, NULL, TO_ROOM );

	/* Turn over - next player */
	NextPlayer(ch, FALSE);
    }
}

void do_fold( CHAR_DATA *ch, char *argument )
{
    if ( ch->deck == NULL || ch->hand == NULL )
    {
	send_to_char( "You need to be playing before you can fold.\n\r", ch );
    }
    else if ( !GameCanFold(ch->deck->Game, 0) )
    {
	char buf[MAX_STRING_LENGTH];
	sprintf( buf, "You cannot fold in %s.\n\r", 
	    GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( !GameCanFold(ch->deck->Game, ch->deck->Turn) )
    {
	char buf[MAX_STRING_LENGTH];
	sprintf( buf, "You cannot fold in turn %d of %s.\n\r", 
	    ch->deck->Turn, GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( ch->play_turn == FALSE )
    {
	send_to_char( "You can only fold on your own turn.\n\r", ch );
    }
    else if ( ch->deck->Playing == eFalse )
    {
	send_to_char( "You cannot fold until the first hand has been dealt.\n\r", ch );
    }
    else /* Show the hand (if any) and then discard it */
    {
	CHAR_DATA *prev = ch->prev_card_player;
	CHAR_DATA *next = ch->next_card_player;

	if ( HandCountCards(ch->hand) == 0 )
	{
	    send_to_char( "You fold.\n\r", ch );
	    act( "$n folds ($s hand is already empty).", ch, NULL, NULL, TO_ROOM );
	}
	else /* Show their hand, then put the cards back into the deck */
	{
	    char buf[MAX_STRING_LENGTH];
	    int i; /* Loop counter */
            const char *pCards = HandShow(ch->hand, eFalse);
	    int Max = strlen(pCards);

	    sprintf( buf, "You fold, throwing down your hand: %s\n\r", pCards );
	    send_to_char( buf, ch );
	    sprintf( buf, "Placing $s cards down, $n folds, revealing: %s", pCards );
	    act( buf, ch, NULL, NULL, TO_ROOM );

	    for ( i = 0; i < Max; i += 3 )
            {
		/* Put all the cards in their hand back into the deck */
		card_t card = CardType(CardSuit(pCards[i]), CardRank(pCards[i+1]));
		if ( card != eCardMax && HandRemoveCard(ch->hand, card) )
                {
		    /* Make sure the card isn't already in the deck */
		    if ( DeckReturnCard(ch->deck, card) == eFalse )
		    {
			sprintf( buf, "do_discard: Tried to return '%s' to deck, but it was already there!\n\r", 
			   CardValue(card) );
			bug( buf, 0 );
		    }
		}
            }
	}

	/* Turn over - next player */
	NextPlayer(ch, TRUE);

	/* Destroy the hand (no need to remove the cards beforehand) */
        if ( ch->hand != NULL )
        {
	    free(ch->hand);
            ch->hand = NULL;
	}

	/* Strip the character from the player list */
	if ( prev != NULL )
	{
	    prev->next_card_player = next;
	    ch->prev_card_player = NULL;
	}
	if ( next != NULL )
	{
	    next->prev_card_player = prev;
	    ch->next_card_player = NULL;
	}

	/* If they're the last person using that deck, destroy it */
        if ( ch->deck != NULL )
        {
	    if ( --ch->deck->Instances == 0 )
		free(ch->deck);

	    ch->deck = NULL;
	}
    }
}

void do_hand( CHAR_DATA *ch, char *argument )
{
    if ( ch->deck == NULL || ch->hand == NULL )
    {
	send_to_char( "You need to start playing in order to get a hand of cards.\n\r", ch );
    }
    else if ( HandCountCards(ch->hand) == 0 )
    {
	send_to_char( "Your hand is currently empty - you need to draw a card.\n\r", ch );
    }
    else if ( !GameViewHand(ch->deck->Game, 0) )
    {
	char buf[MAX_STRING_LENGTH];
	sprintf( buf, "You cannot view your hand in %s.\n\r", 
	    GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( !GameViewHand(ch->deck->Game, ch->deck->Turn) )
    {
	char buf[MAX_STRING_LENGTH];
	sprintf( buf, "You cannot view your hand in turn %d of %s.\n\r", 
	    ch->deck->Turn, GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else /* Just show the hand to its owner */
    {
	char buf[MAX_STRING_LENGTH];

	sprintf( buf, "You glance at your hand: %s\n\r\n\r", 
	    HandShow(ch->hand, eFalse) );
	send_to_char( buf, ch );

	if ( !strcmp(argument, "table") )
	    send_to_char( ScorePoker(ch->hand, NULL), ch );
	else /* Show the graphical layout */
	    send_to_char( HandShow(ch->hand, eTrue), ch );

	send_to_char( "\n\r", ch );
	ShowValidCommands( ch );
    }
}

void do_stick( CHAR_DATA *ch, char *argument )
{
    if ( ch->deck == NULL || ch->hand == NULL )
    {
	send_to_char( "You need to start a game before you can stick with your hand.\n\r", ch );
    }
    else if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) )
    {
	send_to_char( "The game is already over.\n\r", ch );
    }
    else if ( !GameCanStick(ch->deck->Game, 0) )
    {
	char buf[MAX_STRING_LENGTH];
	sprintf( buf, "You cannot stick in %s.\n\r", 
	    GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( !GameCanStick(ch->deck->Game, ch->deck->Turn) )
    {
	char buf[MAX_STRING_LENGTH];
	sprintf( buf, "You cannot stick in turn %d of %s.\n\r", 
	    ch->deck->Turn, GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( ch->play_turn == FALSE )
    {
	send_to_char( "You can only stick on your own turn.\n\r", ch );
    }
    else /* They choose to do nothing this turn */
    {
	send_to_char( "You choose to stick with your current hand.\n\r", ch );
	act( "$n chooses to stick with $s current hand.", ch, NULL, NULL, TO_ROOM );

	/* Turn over - next player */
	NextPlayer(ch, FALSE);
    }
}

void do_start( CHAR_DATA *ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];
    game_t Game = eGameNone;

    /* Make sure the character is not already playing a game */
    if ( ch->deck != NULL )
    {
	send_to_char( "You're already playing a game.\n\r", ch );
	return;
    }

    if ( argument[0] != '0' && is_number(argument) )
	Game = atoi(argument)-1;
    else /* They specified a string, not a number, so look it up */
	Game = GameIndex(argument);

    if ( Game <= eGameNone || Game >= eGameMax )
    {
        int i; /* Loop counter */

	send_to_char( "Please specify the game to play from the following:\n\r", ch );

	for ( i = eGameNone+1; i < eGameMax; ++i )
	{
	    sprintf( buf, "%d) %s\n\r", i+1, GameName(i) );
	    send_to_char( buf, ch );
	}

	return;
    }
    else if ( GameMinimumBet(Game) ) 
    {
	sprintf( buf, "You begin a game of %s.  You must now set a minimum bet (eg 'bet 10').\n\r", 
	    GameName(Game) );
	send_to_char( buf, ch );
    }
    else /* It's a valid game */
    {
	sprintf( buf, "You begin a game of %s (type 'deal' when ready).\n\r", 
	    GameName(Game) );
	send_to_char( buf, ch );
	sprintf( buf, "$n begins a game of %s (use the 'play' command to join).", 
	    GameName(Game) );
	act( buf, ch, NULL, NULL, TO_ROOM );
    }

    /* Create a new deck of cards for solo play */
    ch->deck = DeckCreate();
    ch->deck->Game = Game;
    ch->deck->Instances = 1;

    /* Shuffle the deck */
    DeckShuffle(ch->deck);

    /* Create an empty hand for the character */
    ch->hand = HandCreate();
    ch->hand->bDealer = eTrue;

    /* Indicate that it's this characters turn */
    ch->play_turn = TRUE;

    /* Shouldn't be necessary, but better safe than sorry */
    ch->next_card_player = NULL;
    ch->prev_card_player = NULL;
}

void do_play( CHAR_DATA *ch, char *argument )
{
    CHAR_DATA *victim;

    if ( argument[0] == '\0' )
    {
	send_to_char( "Who do you wish to play with?\n\r", ch );
	return;
    }

    /* Make sure the target exists, and is in the same room as ch */
    if ( ( victim = get_char_world( ch, argument ) ) == NULL
    || ( victim->in_room != ch->in_room ) )
    {
	send_to_char( "They aren't here.\n\r", ch );
	return;
    }

    /* Make sure the character is not already playing a game */
    if ( ch->deck != NULL )
    {
	send_to_char( "You're already playing a game.\n\r", ch );
	return;
    }

    /* Make sure the game isn't already full or under way */
    if ( victim->deck != NULL )
    {
        int MaxPlayers = GameMaxPlayers(victim->deck->Game);

	if ( victim->deck->Instances >= MaxPlayers )
	{
	    char buf[MAX_STRING_LENGTH];
	    sprintf( buf, "That game is already full (it has %d player%s).\n\r", 
		victim->deck->Instances, 
		victim->deck->Instances == 1 ? "" : "s" );
	    send_to_char( buf, ch );
	    return;
	}
	else if ( victim->deck->Playing == eTrue )
	{
	    send_to_char( "You cannot join a game which is already under way.\n\r", ch );
	    return;
	}
	else if ( GameMinimumBet(victim->deck->Game) && victim->deck->Bet <= 0 ) 
	{
	    send_to_char( "You must first wait until the minimum bet has been set.\n\r", ch );
	    return;
	}
    }
    else /* victim->deck == NULL */
    {
	act( "$N is not currently playing any card game.", ch, NULL, victim, TO_CHAR );
	return;
    }

    if ( ch == victim )
    {
	send_to_char( "Use the 'start' game to begin a new card game.\n\r", ch );
    }
    else /* The other person is already playing, so join their game */
    {
	/* Set ch to use the same deck as victim */
	ch->deck = victim->deck;
	ch->deck->Instances++;

	/* Create an empty hand for ch */
	ch->hand = HandCreate();

	/* Store the minimum bet, if any */
	ch->hand->Bet = ch->deck->Bet;

	/* Indicate that it's not this characters turn */
	ch->play_turn = FALSE;

	/* Add yourself to the front of the player list */
	if ( victim->prev_card_player == NULL )
	{
	    victim->prev_card_player = ch;
	    ch->next_card_player = victim;
	    ch->prev_card_player = NULL;
	}
	else /* There are already other players, so join at the front */
	{
	    CHAR_DATA *prev = victim;

	    while ( prev->prev_card_player != NULL )
		prev = prev->prev_card_player;
	    prev->prev_card_player = ch;
	    ch->next_card_player = prev;
	    ch->prev_card_player = NULL;
	}

	act( "You join $N's game of cards.", ch, NULL, victim, TO_CHAR );
	act( "$n joins your game of cards.", ch, NULL, victim, TO_VICT );
	act( "$n joins $N's game of cards.", ch, NULL, victim, TO_NOTVICT );
    }
}

void do_shuffle( CHAR_DATA *ch, char *argument )
{
    if ( ch->deck == NULL )
    {
	send_to_char( "You need to start playing before you can shuffle the deck.\n\r", ch );
    }
    else if ( DeckCountCards(ch->deck) == 0 )
    {
	send_to_char( "You can't shuffle the deck, there's no cards left in it!.\n\r", ch );
    }
    else /* Shuffle the deck */
    {
	DeckShuffle(ch->deck);

	send_to_char( "You shuffle the deck.\n\r", ch );
	act( "$n shuffles the deck.", ch, NULL, NULL, TO_ROOM );
    }
}

void do_double( CHAR_DATA *ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];

    if ( ch->deck == NULL || ch->hand == NULL )
    {
	send_to_char( "You need to start a game before you can double down.\n\r", ch );
    }
    else if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) )
    {
	send_to_char( "The game is already over.\n\r", ch );
    }
    else if ( !GameCanDouble(ch->deck->Game, 0) )
    {
	sprintf( buf, "You cannot double down in %s.\n\r", 
	    GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( !GameCanDouble(ch->deck->Game, ch->deck->Turn) )
    {
	sprintf( buf, "You cannot double down in turn %d of %s.\n\r", 
	    ch->deck->Turn, GameName(ch->deck->Game) );
	send_to_char( buf, ch );
	ShowValidCommands( ch );
    }
    else if ( ch->deck->Turn > 0 && ch->deck->Playing == eFalse )
    {
	send_to_char( "You cannot double down until the first hand has been dealt.\n\r", ch );
    }
    else if ( ch->play_turn == FALSE )
    {
	send_to_char( "You can only double down on your turn.\n\r", ch );
    }
    else /* Double their current bet and draw a card */
    {
	if ( ch->gold < ch->hand->Bet*2 )
	{
	    send_to_char( "You don't have enough gold to double down.\n\r", ch );
	}
	else /* It's a valid bet */
	{
	    /* Draw a card from the deck and put it in their hand */
	    card_t card = DeckDrawCard(ch->deck);

	    if ( card == eCardMax )
	    {
		bug( "do_draw: Managed to end up with an empty deck.", 0 );
		send_to_char( "The deck is empty!\n\r", ch );
	    }
	    else if ( HandAddCard(ch->hand, card) == eFalse )
	    {
		bug( "do_draw: Duplicate card found in hand.", 0 );
		send_to_char( "You were unable to draw the card.\n\r", ch );
	    }
	    else /* They drew the card fine */
	    {
		/* Apply the new bet, pot and gold */
		ch->gold -= ch->hand->Bet;
		ch->deck->Pot += ch->hand->Bet;
		ch->hand->Bet *= 2;
		ch->deck->Bet = ch->hand->Bet;

		int Score = ScoreBlackjack(ch->hand);
		if ( Score > 0 && Score <= 21 )
		{
		    sprintf( buf, "Your double your bet to %d gold and draw the %s.\n\r", 
			ch->hand->Bet, CardName(card) );
		    send_to_char( buf, ch );
		    sprintf( buf, "$n doubles $s bet to %d gold and draws a card.", ch->hand->Bet );
		    act( buf, ch, NULL, NULL, TO_ROOM );

		    /* Turn over - next player */
		    NextPlayer(ch, FALSE);
		}
		else /* They've bone bust */
		{
		    CHAR_DATA *prev = ch->prev_card_player;
		    CHAR_DATA *next = ch->next_card_player;

		    sprintf( buf, "Your double your bet to %d gold and draw the %s, going bust!\n\r", 
			ch->hand->Bet, CardName(card) );
		    send_to_char( buf, ch );
		    sprintf( buf, "$n doubles $s bet to %d gold, draws a card, and goes bust!", ch->hand->Bet );
		    act( buf, ch, NULL, NULL, TO_ROOM );

		    /* Turn over - next player */
		    NextPlayer(ch, TRUE);

		    /* Destroy the hand (no need to remove the cards beforehand) */
		    if ( ch->hand != NULL )
		    {
			free(ch->hand);
	        	ch->hand = NULL;
		    }

		    /* Strip the character from the player list */
		    if ( prev != NULL )
		    {
			prev->next_card_player = next;
			ch->prev_card_player = NULL;
		    }
		    if ( next != NULL )
		    {
			next->prev_card_player = prev;
			ch->next_card_player = NULL;
		    }

		    /* If they're the last person using that deck, destroy it */
	            if ( ch->deck != NULL )
	            {
			if ( --ch->deck->Instances == 0 )
			    free(ch->deck);

			ch->deck = NULL;
		    }
		}
	    }
	}
    }
}