/*
 *	File name:			strategy_turbobah.c
 *	Purpose:			Turbo-Bah poker playing strategy.
 *	Functions:			computer_bet, computer_draw, strategy_init,
 *						strategy_exit, computer_outcome, computer_hear.
 *	Created:			01 Dec 91
 *	Last Modified:		25 Jul 93
 *	Comments:
 *		"Strategy" files must contain six externally visible functions:
 *
 *		strategy_init - called once during startup.
 *		strategy_exit - called once upon orderly exit.
 *		computer_bet - called whenever a bet is needed.
 *		computer_draw - called whenever a draw of cards is needed.
 *		computer_outcome - called at the end of each hand.
 *		computer_hear - called whenever anyone sends a text message.
 *
 *		The interfaces to these six functions are well-defined and should 
 *		remain stable.  You can use this file to entirely alter the playing 
 *		methods of a robot player without being concerned with server 
 *		communication or anything else.
 *
 *	History:
 *		01 Dec 91/JVE	Created.
 *		14 Sep 92/JVE	A little tweaking.
 *		31 Mar 93/JVE	Ported to AmigaDOS.
 *		25 Jul 93/JVE	Rearranged to get prototype checking to shut up.
 */

# ifdef sun
# include	<sys/param.h>	/* for MIN */
# endif
# if _AMIGA
# include	<math.h>
# define	MIN			min
# define	MAX			max
# endif
# include	<stdio.h>
# include	<stdlib.h>
# include	<time.h>
# include	<string.h>
# include	"standard.h"
# include	"cards.h"		/* for crandom, suit, card macros */
# include	"ipc.h"
# include	"5draw.h"		/* for MAX_PLAYERS */
# include	"whowon.h"
# include	"commands.h"	/* for DISCARD */
# include	"knowledge.h"
# include	"robot.h"
# include	"evaluate.h"
# include	"robot_main_protos.h"
# include	"strategy_protos.h"

# define	MIN_EXPERIENCE			30
# define	MAX_HANDS_FOR_AVERAGE	30

/*
 *	If someone else raises by this or more, they are deemed "Gung Ho", i.e.
 *	very confident in their hand.
 */

# define	GUNG_HO					35
# define	TOTAL_GUNG_HO			51

static int	before_draw = FALSE;
static int	first_time_after_draw = FALSE;
static int	try_for_straight = FALSE;
static int	want_suit = -1;
static int	bluffing = FALSE;
static int	started_bluff_at = 0;
static int	tried_flush = 0, tried_straight = 0, 
			got_flush = 0, got_straight = 0;
static int	tossed_pairs = 0;
static int	hands = 0, wins = 0, cashflow = 0;
static int	bluffed_this_hand;

typedef enum 
	{ 
	UNKNOWN, 
	SMALL_POTATOES,					/* constant small raise */
	RAISE_TO_THE_LIMIT, 			/* always raise by RAISE_LIMIT */
	RAISE_SMALL 					/* raise little first, then a lot */
	} GreatStrategy;

static char		*shortscores[] =
	{
	"ZERO",
	"PAIR",
	"2PAR",
	"3KIN",
	"STRT",
	"FLUS",
	"FULL",
	"4KIN",
	"STFL",
	"RYFL",
	"BLUF"
	};

static int				bet(KNOWLEDGE knowledge[], int me, int score, 
							int required, int need);
static int				four_flush(int hand[]);
static void				AdjustWeights(void);
static GreatStrategy	ChooseStrategy(void);
int						DrewOne(KNOWLEDGE *k, int me);
int						FirstBettor(KNOWLEDGE *k, int me);
int						GungHo(KNOWLEDGE *k, int me);
int						Hoser(KNOWLEDGE *k);
int						LargestOtherRaise(KNOWLEDGE *k, int me);
int						NumberIn(KNOWLEDGE *k);
int						Pot(KNOWLEDGE *k);
int						SuspectThree(KNOWLEDGE *k, int me);

static GreatStrategy strategy = UNKNOWN;

static int		experience = 0;		/* # of great hands */
static float	expect_limit = 0;	/* stake to expect if RAISE_TO_THE_LIMIT */
static float	expect_small = 0;	/* stake to expect if RAISE_SMALL */
static float	expect_potato = 0;	/* stake to expect if SMALL_POTATOES */
static int		hands_limit = 0;	/* # hands played RAISE_TO_THE_LIMIT */
static int		hands_small = 0;	/* # hands played RAISE_SMALL */
static int		hands_potato = 0;	/* # hands played SMALL_POTATOES */
static int		won_last_hand = FALSE;
static int		is_new_hand = TRUE;
static int		winnings = 0;

# define	BLUFF	NUM_SCORES

static int		byhand[NUM_SCORES+1];	/* $$ won by evaluation of my hand */

extern char		*myname;

/*
 ***********************************************************************
 *
 * FUNCTION NAME:	strategy_init
 *
 * DESCRIPTION:		Any initialization needed.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *
 * RETURN CODES: 
 *		TRUE		I am ready to play.
 *		FALSE		I'm not playing, just watching.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int		strategy_init(void)

	{
	int			i;

# ifdef sun
	srandom( (int) time( 0 ) ^ getpid() );
# else
	srand( time( 0 ) );
# endif
	for ( i = 0; i < NUM_SCORES; i++ )
		byhand[i] = 0;
	return TRUE;
	}

/*
 ***********************************************************************
 *
 * FUNCTION NAME:	strategy_exit
 *
 * DESCRIPTION:		Actions to be performed on shutdown.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *
 * RETURN CODES: none
 *
 * COMMENTS:
 *
 **********************************************************************
 */

void	strategy_exit(void)

	{
	talk( "Gotta go.  Bye." );
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	computer_bet
 *
 * DESCRIPTION:		Make a bet.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *		required	 I		Total $$ this hand to bet to stay in.
 *
 * RETURN CODES: 
 *	(int)		Amount of additional cash to bet.
 *	(-1)		Fold.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int		computer_bet( KNOWLEDGE knowledge[], int me, int required )

	{
	int			add_bet;
	int			need;
	int			score;

	if ( is_new_hand )
		{
		before_draw = TRUE;
		first_time_after_draw = FALSE;
		bluffing = FALSE;
		strategy = UNKNOWN;
		bluffed_this_hand = FALSE;
		}
	need = required - knowledge[me].bet;

	score = eval( knowledge[me].cards );

	if ( is_new_hand || first_time_after_draw )
		printf( "%s ", shortscores[score] );
	if ( first_time_after_draw )
		{
		if ( want_suit != -1 )
			{
			++tried_flush;
			if ( score >= FLUSH )
				{
				++got_flush;
				}
			}
		else if ( try_for_straight )
			{
			++tried_straight;
			if ( score >= STRAIGHT )
				{
				++got_straight;
				}
			}
		}

	if ( bluffing )
		{
		/*
		 *	We are bluffing.  If someone else is gung-ho on his hand, we must
		 *	drop out of bluff mode and resume normal operation.  If we are
		 *	going to bluff, do it big-time.
		 */
		if ( GungHo( knowledge, me ) == 0 )
			{
			add_bet = need + RAISE_LIMIT;
			printf( "BLUFFING " );
			}
		else
			{
			bluffing = FALSE;
			printf( "ABANDON! " );
			add_bet = bet( knowledge, me, score, required, need );
			}
		}
	else if ( ( is_new_hand || first_time_after_draw ) && crandom( 40 ) > 38 &&
			  required < 25 && NumberIn( knowledge ) < 4 )
		{
		bluffing = TRUE;
		printf( "BLUFFING " );
		started_bluff_at = required;
		add_bet = need + RAISE_LIMIT;
		bluffed_this_hand = TRUE;
		}
	else
		{
		add_bet = bet( knowledge, me, score, required, need );
		}
	/*
	 *	If we are thinking about folding, but the additional bet needed
	 *	to call is small in relation to what we've already bet, then
	 *	call instead.
	 */
	if ( add_bet == -1 && score > NOTHING && 
		 ( need < knowledge[me].bet / 5 || need < 6 ) )
		add_bet = need;
	/*
	 *	If some hosehead program is present, chase it away.
	 */
	if ( required == ANTE && add_bet == 0 && Hoser( knowledge ) )
		add_bet = need + 1;
	printf( "[%d,%d] ", need, add_bet );
	first_time_after_draw = FALSE;
	is_new_hand = FALSE;
	return add_bet;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	ChooseStrategy
 *
 * DESCRIPTION:		Choose strategy to use for betting on great hand.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *
 * RETURN CODES: 
 *		(GreatStrategy)
 *
 * COMMENTS:
 *
 **********************************************************************
 */

static GreatStrategy	ChooseStrategy(void)

	{
	GreatStrategy			which;
	int						rnd;

	/*
	 *	Don't try to decide rationally before accumulating enough data.
	 */
	if ( experience < MIN_EXPERIENCE )
		{
		if ( ( rnd = crandom( 3 ) ) == 0 )
			{
			printf( "RLIM " );
			which = RAISE_TO_THE_LIMIT;
			}
		else if ( rnd == 1 )
			{
			printf( "RSML " );
			which = RAISE_SMALL;
			}
		else
			{
			printf( "RPOT " );
			which = SMALL_POTATOES;
			}
		}
	else 
		{
		printf( "CHUZ [L%.2f,S%.2f,P%.2f] ", expect_limit, expect_small, expect_potato );
		if ( ( rnd = crandom( (int) 
						( expect_small + expect_limit + expect_potato ) ) ) 
						<= (int) expect_limit )
			{
			printf( "LIM " );
			which = RAISE_TO_THE_LIMIT;
			}
		else if ( rnd <= (int) ( expect_limit + expect_small ) )
			{
			printf( "SML " );
			which = RAISE_SMALL;
			}
		else
			{
			printf( "POT " );
			which = SMALL_POTATOES;
			}
		}
	return which;
	}

/*
 ***********************************************************************
 *
 * FUNCTION NAME:	BetPair
 *
 * DESCRIPTION:		Bet when we have one pair.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *		hand		 I		Our hand.
 *		required	 I		Total cash required of us during this hand.
 *		need		 I		Amount of cash we need to bet to call.
 *
 * RETURN CODES: 
 *		Amount to bet.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

static int		BetPair( KNOWLEDGE knowledge[], int me, int hand[], 
						 int required, int need )

	{
	GAMEINFO		gi;
	int				i;
	int				add_bet;

	/*
	 *	We have one pair.
	 *
	 *	Never raise very much, and set a betting limit based on the
	 *	rank of the pair that we have.
	 *
	 *	If we have four of a suit, we might end up tossing the pair and going
	 *	for the flush.  In this case, the betting limit is ignored.
	 */
	for ( i = 0; i < MAX_CARDS; i++ )
		gi.cards[i] = hand[i];
	evaluate( &gi );
# if 0
	if ( before_draw && four_flush( hand ) != -1 )
		{
		/*
		 *	Identical code in BetNothing.  Should be factored.
		 */
		if ( required < 50 && GungHo( knowledge, me ) == 0 )
			add_bet = need + 20 + crandom( 2 ) * 10;
		else
			add_bet = need;
		}
	else
# endif
		{
		for ( i = 0; i < NUM_CARD_DENOMS; i++ )
			if ( gi.count[i] > 1 )
				break;
# if 0
		if ( need == 0 || required < (2 + i/8) * (15 - NumberIn( knowledge ) * 3 )  )
# else
		if ( need < 10 )
# endif
			{
			add_bet = need;
			if ( Pot( knowledge ) < 30 && i > 8 && need == 0 &&
				 ( before_draw ||
				   ( ! SuspectThree( knowledge, me ) && 
				     ! DrewOne( knowledge, me ) 
				   ) 
				 ) && !FirstBettor( knowledge, me ) &&
				 NumberIn( knowledge ) < 3 )
					add_bet += 4 + crandom( 6 );
			}
		else
			add_bet = -1;
		}
	return add_bet;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	BetTwoPair
 *
 * DESCRIPTION:		Bet when we have two pair.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *		required	 I		Total cash required of us during this hand.
 *		need		 I		Amount of cash we need to bet to call.
 *
 * RETURN CODES: 
 *		Amount to bet.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

static int		BetTwoPair( KNOWLEDGE knowledge[], int me, int required, 
							int need )

	{
	int				add_bet;
	int				gung;

	/*
	 *	If someone else is confident, then just call, otherwise raise.
	 */

	if ( ( gung = GungHo( knowledge, me ) ) >= 2 )
		{
		/*
		 *	2 or more other players are very confident.  Fold.
		 */
		add_bet = -1;
		}
	else if ( gung > 0 || required > 20 + crandom( 20 ) || 
			  ( !before_draw && SuspectThree( knowledge, me ) && 
				need > 0 && required > 30 ) )
		{
		/*
		 *	Getting a little rich.  Call.
		 */
		add_bet = need;
		}
# if 0
	else if ( knowledge[me].bet < 10 && need == 0 )
		add_bet = need + 5 + crandom( 3 ) * ( 1 + crandom( 3 ) ) * 5;
# else
	else
		add_bet = need + 10 + crandom( 10 );
# endif
	return add_bet;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	BetGreatHand
 *
 * DESCRIPTION:		Bet when we have 3 of a kind or better.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *		required	 I		Total cash required of us during this hand.
 *		need		 I		Amount of cash we need to bet to call.
 *
 * RETURN CODES: 
 *		Amount to bet.
 *
 * COMMENTS:
 *		We want to induce the other players to contribute as much cash
 *		as possible to the hand.
 *
 *		This implementation tries to adjust to changing conditions of
 *		other players by deciding between the GreatStrategy set declared
 *		at the top of this file.
 *
 **********************************************************************
 */

static int		BetGreatHand( KNOWLEDGE knowledge[], int me, int required, 
							  int need )

	{
	int				add_bet;
	int				x;
	static int		raise = 0;

	if ( strategy == UNKNOWN )
		{
		strategy = ChooseStrategy();
		raise = 0;
		}
	switch ( strategy )
		{
		case RAISE_TO_THE_LIMIT:
			add_bet = need + RAISE_LIMIT;
			break;
		case RAISE_SMALL:
			/*
			 *	If no one else has raised, raise small to sucker them in.
			 *	Otherwise, raise a little bit more than last raise.
			 */
			x = LargestOtherRaise( knowledge, me );
			raise = MAX( raise, x );
			printf( "LR %d ", raise );
			if ( raise > 0 || knowledge[me].bet > 50 || !before_draw )
				{
				x = raise + 5 + crandom( 10 );
				add_bet = need + ( raise = MIN( x, RAISE_LIMIT ) );
				}
			else
				add_bet = need + ( raise = crandom( 6 ) );
			break;
		case SMALL_POTATOES:
			add_bet = need + 8 + crandom( 3 );
			break;
		default:
			add_bet = need + 7;		/* something is definitely wrong */
			printf( "ACK! Bad strategy!\n" );
			break;
		}
	/*
	 *	If they think that their hand is great, and ours is less than a
	 *	flush, just call.  I *hate* losing lots of money when I have three
	 *	of a kind!
	 */
	if ( GungHo( knowledge, me ) > 0 && eval( knowledge[me].cards ) < FLUSH )
		add_bet = need;
	return add_bet;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	BetNothing
 *
 * DESCRIPTION:		Bet when we have no useful hand.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *		hand		 I		Our hand.
 *		required	 I		Total cash required of us during this hand.
 *		need		 I		Amount of cash we need to bet to call.
 *
 * RETURN CODES: 
 *		Amount to bet.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

static int		BetNothing( KNOWLEDGE knowledge[], int me, int hand[], 
							int required, int need )

	{
	GAMEINFO		gi;
	int				i;
	int				add_bet;

	for ( i = 0; i < MAX_CARDS; i++ )
		gi.cards[i] = hand[i];
	evaluate( &gi );

	if ( before_draw )
		{
		if ( four_flush( hand ) != -1 )
			{
			if ( GungHo( knowledge, me ) == 0 )
				{
				add_bet = BetGreatHand( knowledge, me, required, need );
				}
			else if ( GungHo( knowledge, me ) > 1 )
				{
				add_bet = -1;
				}
			else
				{
				add_bet = need;
				}
			}
		else if ( need > 8 + crandom( 12 ) )
			{
			add_bet = -1;
			}
		else
			{
			add_bet = need;
			}
		}
	else
		{
		if ( required < 10 + ANTE || need == 0 )
			{
			add_bet = need;
			}
		else
			{
			add_bet = -1;
			}
		}

	return add_bet;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	bet
 *
 * DESCRIPTION:		Decide how much to bet
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *		score		 I		Evaluation of our hand.
 *		required	 I		Total cash required of us during this hand.
 *		need		 I		Amount of cash we need to bet to call.
 *
 * RETURN CODES: 
 *		(int)		How much to bet.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

static int	bet( KNOWLEDGE knowledge[], int me, int score, int required, 
				 int need )

	{
	int			add_bet;

	if ( score >= THREE )
		add_bet = BetGreatHand( knowledge, me, required, need );
	else if ( score == TWOPAIR )
		add_bet = BetTwoPair( knowledge, me, required, need );
	else if ( score == PAIR )
		add_bet = BetPair( knowledge, me, knowledge[me].cards, required, need );
	else
		add_bet = BetNothing( knowledge, me, knowledge[me].cards, required, 
							  need );
	return add_bet;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	AdjustWeights
 *
 * DESCRIPTION:		Keep track of what happened with last GreatStrategy.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *
 * RETURN CODES: 
 *
 * COMMENTS:
 *
 **********************************************************************
 */

static void		AdjustWeights(void)

	{
	++experience;
	experience = MIN( experience, MIN_EXPERIENCE );
	switch ( strategy )
		{
		case RAISE_TO_THE_LIMIT:
			++hands_limit;
			hands_limit = MIN( hands_limit, MAX_HANDS_FOR_AVERAGE );
			expect_limit += ( winnings - expect_limit ) / hands_limit;
			expect_limit = MAX( ANTE, expect_limit );
			printf( "LWIN [%d,%.2f] ", winnings, expect_limit );
			break;
		case RAISE_SMALL:
			++hands_small;
			hands_small = MIN( hands_small, MAX_HANDS_FOR_AVERAGE );
			expect_small += ( winnings - expect_small ) / hands_small;
			expect_small = MAX( ANTE, expect_small );
			printf( "SWIN [%d,%.2f] ", winnings, expect_small );
			break;
		case SMALL_POTATOES:
			++hands_potato;
			hands_potato = MIN( hands_potato, MAX_HANDS_FOR_AVERAGE );
			expect_potato += ( winnings - expect_potato ) / hands_potato;
			expect_potato = MAX( ANTE, expect_potato );
			printf( "PWIN [%d,%.2f] ", winnings, expect_potato );
			break;
		default:
			printf( "Adjust for unknown strategy!\n" );
			break;
		}
	}

/*
 ***********************************************************************
 *
 * FUNCTION NAME:	computer_draw
 *
 * DESCRIPTION:		Discard some cards and draw others.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *		cmd			 O		Constructed "discard" command.
 *
 * RETURN CODES: none
 *
 * COMMENTS:
 *
 **********************************************************************
 */

void	computer_draw( KNOWLEDGE knowledge[], int me, char *cmd )

	{
	GAMEINFO		gi;
	int				i, j;
	int				high_singleton = -1;
	int				ace_low = FALSE;		/* using ace low in a straight? */
	int				discards, highcard, highindex;
	char			*p;
	int				nd = 0;
	extern void		standard_draw();

	before_draw = FALSE;
	first_time_after_draw = TRUE;
	try_for_straight = FALSE;
	strcpy( cmd, DISCARD );
	for ( i = 0; i < MAX_CARDS; i++ )
		gi.cards[i] = knowledge[me].cards[i];
	evaluate( &gi );
	want_suit = four_flush( knowledge[me].cards );
	if ( gi.score == NOTHING || 
		 ( gi.score == PAIR && want_suit != -1 && 
		   ( knowledge[me].bet > 25 || NumberIn( knowledge ) > 3 ) ) )
		{
		/*
		 *	If we are trying for a flush, toss out cards of wrong suit.
		 */
		if ( want_suit != -1 )
			{
			if ( gi.score == PAIR )
				{
				printf( "TOSS " );
				++tossed_pairs;
				}
			for ( i = 0; i < MAX_CARDS; i++ )
				if ( suit(knowledge[me].cards[i]) != want_suit )
					sprintf( cmd, "%s:%d", cmd, i );
			}
		else
			{
# if 0
			/*
			 *	Check for possible straight.  Only try if we have 4 in a row.
			 */
			
			if ( try_for_straight = ( possible_straight( gi, &j, &ace_low ) &&
									  crandom( 10 ) > 7 ) )
				{
				if ( ace_low )
					{
					for ( i = 0; i < MAX_CARDS; i++ )
						if ( card(knowledge[me].cards[i]) < j && 
							 card(knowledge[me].cards[i]) > 2 )
							sprintf( cmd, "%s:%d", cmd, i );
					}
				else
					{
					for ( i = 0; i < MAX_CARDS; i++ )
						if ( card(knowledge[me].cards[i]) < j || 
							 card(knowledge[me].cards[i]) > j + 3 )
							sprintf( cmd, "%s:%d", cmd, i );
					}
				}
			else
# endif
				{
				/*
				 *	Otherwise, keep highest card Jack or above.
				 */
				discards = 0;
				for ( i = 0; i < MAX_CARDS; i++ )
					if ( card(knowledge[me].cards[i]) < 9 )
						{
						sprintf( cmd, "%s:%d", cmd, i );
						knowledge[me].cards[i] = -1;
						++discards;
						}
				highcard = 0; highindex = -1;
				if ( discards < 4 )
					for ( i = 0; i < MAX_CARDS; i++ )
						if ( knowledge[me].cards[i] != -1 ) 
							 if ( card(knowledge[me].cards[i]) > highcard )
								{
								highcard = card(knowledge[me].cards[i]);
								if ( highindex != -1 )
									sprintf( cmd, "%s:%d", cmd, highindex );
								highindex = i;
								}
							else
								sprintf( cmd, "%s:%d", cmd, i );
				}
			}
		}
	else if ( gi.score == PAIR )
		{
		/*
		 *	If I haven't bet much, then try to keep a high singleton and hope
		 *	for two pair.  If I have bet much, then someone else probably has
		 *	a good hand and I should go for three of a kind, so don't keep any
		 *	singletons.
		 */
		if ( knowledge[me].bet < 30 && FALSE )
			/*
			 *	Find highest singleton, Ten or higher.
			 */
			for ( i = NUM_CARD_DENOMS - 1; high_singleton == -1 && i > 7; i-- )
				if ( gi.count[i] == 1 )
					high_singleton = i;
		/*
		 *	Toss all singletons which are not the high singleton (if any)
		 *	found above.
		 */
		for ( i = 0; i < MAX_CARDS; i++ )
			if ( gi.count[ card( knowledge[me].cards[i] ) ] == 1 && 
				 card( knowledge[me].cards[i] ) != high_singleton )
				sprintf( cmd, "%s:%d", cmd, i );
		want_suit = -1;		/* not going for flush */
		}
	else if ( gi.score < STRAIGHT )
		{
		/*
		 *	We have Two Pair or 3 of a Kind.  Toss all singletons.
		 */
		for ( i = 0; i < MAX_CARDS; i++ )
			if ( gi.count[ card( knowledge[me].cards[i] ) ] == 1 )
				sprintf( cmd, "%s:%d", cmd, i );
		}
	for ( p = cmd; p != NULL; )
		if ( ( p = strchr( p + 1, ':' ) ) != NULL )
			++nd;
	printf( "DRAW %d ", nd );
	}



/*
 ***********************************************************************
 *
 * FUNCTION NAME:	computer_outcome
 *
 * DESCRIPTION:		Is informed of the outcome of each hand.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *		winner		 I		Index in knowledge[] winning player.
 *
 * RETURN CODES: none
 *
 * COMMENTS:
 *
 **********************************************************************
 */

void	computer_outcome( KNOWLEDGE knowledge[], int me, int winner )

	{
	int			score;

	if ( ! knowledge[me].valid )
		return;
	++hands;
	if ( winner == me )
		{
		won_last_hand = TRUE;
		winnings = Pot( knowledge ) - knowledge[me].bet;
		++wins;
		cashflow += winnings;
		if ( strategy != UNKNOWN )
			AdjustWeights();
		printf( " WON %d\n", winnings );
		}
	else
		{
		won_last_hand = FALSE;
		cashflow += ( winnings = -knowledge[me].bet );
		printf( " LOST %d\n", knowledge[me].bet );
		}
	if ( bluffed_this_hand )
		score = BLUFF;
	else
		score = eval( knowledge[me].cards );
	byhand[score] += winnings;
	is_new_hand = TRUE;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	computer_hear
 *
 * DESCRIPTION:		Listen to incoming text message.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		from		 I		Who is message from?
 *		text		 I		Text of message.
 *
 * RETURN CODES: none
 *
 * COMMENTS:
 *
 **********************************************************************
 */

void	computer_hear( KNOWLEDGE knowledge[], char *from, char *text )

	{
	int			i;
	char		temp[MAXLINE];

	/*
	 *	This causes the robot to exit if he receives a message
	 *	containing the string "EXIT" anywhere in it.
	 */
	if ( strstr( text, myname ) != NULL )
		if ( strstr( text, "EXIT" ) != NULL )
			{
			strategy_exit();
			robot_exit();
			/*NOTREACHED*/
			}
		else if ( strstr( text, "DUMP" ) != NULL )
			{
			sprintf( temp, "hands %d  wins %d  cashflow %d", hands, wins,
					 cashflow );
			talk( temp );
			sprintf( temp, 
				"expect_small = %.2f expect_limit = %.2f expect_potato = %.2f",
				 expect_small, expect_limit, expect_potato );
			talk( temp );
			sprintf( temp, "flushes %d/%d  straights %d/%d  tossed_pairs %d",
					 got_flush, tried_flush, got_straight, tried_straight,
					 tossed_pairs );
			talk( temp );
			strcpy( temp, "" );
			for ( i = 0; i < NUM_SCORES + 1; i++ )
				{
				sprintf( temp, "%s %s %d", temp, shortscores[i], byhand[i] );
				if ( (i + 1) % 4 == 0 )
					{
					talk( temp );
					strcpy( temp, "" );
					}
				}
			if ( i % 4 != 0 )
				talk( temp );
			}
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	Pot
 *
 * DESCRIPTION:		Compute total pot for hand.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		k			 I		knowledge of this hand.
 *
 * RETURN CODES: 
 *		(int)		The total pot.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int			Pot( KNOWLEDGE k[] )

	{
	int		i;
	int		pot = 0;

	for ( i = 0; i < MAX_PLAYERS; i++ )
		{
		if ( k[i].valid )
			{
			pot += k[i].bet;
			}
		}
	return pot;
	}

/*
 ***********************************************************************
 *
 * FUNCTION NAME:	four_flush
 *
 * DESCRIPTION:		Find if we have at least four of the same suit.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		hand		 I		The robot's hand.
 *
 * RETURN CODES: 
 *		(int)		Suit we want, or -1 if we're not close to a flush.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

static int		four_flush( int hand[] )

	{
	int			i;
	int			want_suit = -1;
	int			have_suits[4];

	for ( i = 0; i < 4; i++ )
		have_suits[i] = 0;
	for ( i = 0; i < MAX_CARDS; i++ )
		have_suits[suit(hand[i])]++;
	for ( i = 0; i < 4; i++ )
		if ( have_suits[i] > 3 )
			break;
	if ( i < 4 )
		want_suit = i;
	return want_suit;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	possible_straight
 *
 * DESCRIPTION:		Check for 4 cards in sequence.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		gi			 I		Info on the hand.
 *		start		 O		Returns starting denomination of possible straight.
 *		ace_low		 O		Returns whether or not ace must be used as low.
 *
 * RETURN CODES: 
 *		TRUE		Have four in a row.
 *		FALSE		Don't have four in a row.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

static int		possible_straight( GAMEINFO gi, int *start, int *ace_low )

	{
	int			i, j;
	int			go_for_it = FALSE;

	for ( i = 0; i < NUM_CARD_DENOMS - 3 && !go_for_it; i++ )
		if ( gi.count[i] )
			{
			for ( j = 1; j < 4 && gi.count[i+j]; j++ ) ;
			if ( j == 4 )
				{
				go_for_it = TRUE;
				j = i;
				break;
				}
			else if ( j == 3 && i == 0 && gi.count[NUM_CARD_DENOMS-1] )
				{
				go_for_it = TRUE;
				j = NUM_CARD_DENOMS-1;
				*ace_low = TRUE;
				break;
				}
			}
	*start = j;
	return go_for_it;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	GungHo
 *
 * DESCRIPTION:		See if anyone else really likes their hand.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		k			 I		Knowledge of hand.
 *		me			 I		My player index.
 *
 * RETURN CODES: 
 *		(int)		Number of other players very confident in their hands.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int				GungHo( KNOWLEDGE k[], int me )

	{
	int			i;
	int			rc = 0;

	for ( i = 0; i < MAX_PLAYERS; i++ )
		if ( i != me && k[i].valid && k[i].in &&
			 ( k[i].lastraise >= GUNG_HO || k[i].bet >= TOTAL_GUNG_HO ) )
			++rc;
	return rc;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	LargestOtherRaise
 *
 * DESCRIPTION:		Find largest last raise by someone other than me.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		knowledge	 I		Everything we know about the current hand.
 *		me			 I		Index in knowledge[] of this robot.
 *
 * RETURN CODES: 
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int				LargestOtherRaise( KNOWLEDGE k[], int me )

	{
	int			i;
	int			high = 0;

	for ( i = 0; i < MAX_PLAYERS; i++ )
		if ( i != me && k[i].valid && k[i].lastraise >= high )
			high = k[i].lastraise;
	return high;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	NumberIn
 *
 * DESCRIPTION:		Return number of players still in current hand.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		k			 I		Knowledge of current hand.
 *
 * RETURN CODES: 
 *		(int)		Number of players still in current hand.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int				NumberIn( KNOWLEDGE k[] )

	{
	int			i;
	int			rc = 0;

	for ( i = 0; i < MAX_PLAYERS; i++ )
		if ( k[i].valid && k[i].in )
			++rc;
	return rc;
	}

/*
 ***********************************************************************
 *
 * FUNCTION NAME:	SuspectThree
 *
 * DESCRIPTION:		See if anyone might have three of a kind.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		k			 I		Knowledge of current hand.
 *		me			 I		Index of myself in knowledge array.
 *
 * RETURN CODES: 
 *		TRUE		Someone might have 3-kind or better.
 *		FALSE		No one drew 2 or 0.
 *
 * COMMENTS:
 *		Anyone drawing exactly 2 cards is suspected of having 3-of-a-kind.
 *		Anyone drawing 0 cards is suspected of having better than that.
 *
 **********************************************************************
 */

int				SuspectThree( KNOWLEDGE k[], int me )

	{
	int			i;
	int			rc = FALSE;

	for ( i = 0; i < MAX_PLAYERS; i++ )
		if ( i != me && k[i].valid && k[i].in && 
			 ( k[i].ndrawn == 2 || k[i].ndrawn == 0 ) )
			{
			rc = TRUE;
			break;
			}
	return rc;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	DrewOne
 *
 * DESCRIPTION:		See if anyone else drew only one card.
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		k			 I		Knowledge of current hand.
 *		me			 I		Index of myself in knowledge array.
 *
 * RETURN CODES: 
 *		TRUE		Someone drew 1.
 *		FALSE		No one drew 1.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int				DrewOne( KNOWLEDGE k[], int me )

	{
	int			i;
	int			rc = FALSE;

	for ( i = 0; i < MAX_PLAYERS; i++ )
		if ( i != me && k[i].valid && k[i].in && k[i].ndrawn == 1 )
			{
			rc = TRUE;
			break;
			}
	return rc;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	Hoser
 *
 * DESCRIPTION:		Is there a hosehead program present?
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		k			 I		Knowledge of current hand.
 *
 * RETURN CODES: 
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int				Hoser( KNOWLEDGE k[] )

	{
	int			i, j;
	static char	*HoserList[] = { "PainInTheButt", NULL };

	for ( i = 0; i < MAX_PLAYERS; i++ )
		if ( k[i].valid && k[i].in )
			for ( j = 0; HoserList[j] != NULL; j++ )
				if ( strcmp( k[i].name, HoserList[j] ) == 0 )
					return TRUE;
	return FALSE;
	}


/*
 ***********************************************************************
 *
 * FUNCTION NAME:	FirstBettor
 *
 * DESCRIPTION:		Am I first bettor this betting round?
 *
 * PARAMETERS: 
 *		NAME		I/O		DESCRIPTION
 *		----		---		-----------
 *		k			 I		Knowledge of current hand.
 *		me			 I		Index of myself in knowledge array.
 *
 * RETURN CODES: 
 *		TRUE		I am first bettor this round.
 *		FALSE		I am not first bettor this round.
 *
 * COMMENTS:
 *
 **********************************************************************
 */

int				FirstBettor( KNOWLEDGE k[], int me )

	{
	int			i;

	for ( i = 0; i < MAX_PLAYERS; i++ )
		if ( i != me && k[i].valid && k[i].in && k[i].lastraise != -2 )
			return FALSE;
	return TRUE;
	}
