/*
 *
 * state.c -- states for robot player
 *
 * This code is copyright 1995 by James A. Cherry, jac@doe.carleton.ca.
 * It is not to be redistributed or modified without his permission.
 *
 */

#include "funddefs.h"
#include "globals.h"
#include "socket.h"
#include "botutil.h"
#include "proto.h"


void AwaitConnect()
/* Wait for connection to be established.. */
{
    int st = 0;

    if( RecHead == NULL ) return;
    if( DoomName == POSLDOOM ) {
	st = strcmp( RecHead->string, "Welcome to PoslDOoM.\n" );
    } else if( DoomName == WISDOOM ) {
	st = strcmp( RecHead->string, "Welcome to WisDOoM.\n" );
    } else if( DoomName == MARLDOOM || DoomName == DDDOOM ) {
	st = strcmp( RecHead->string, "Welcome to MarlDOoM.\n" );
    }
    if( st == 0 ) {
	sprintf( WriteBuf, "connect %s %s\n", PlrName, Password );
	QueueCommand( WriteBuf, 0 );
	ChangeState( AWAIT_LOGIN );
    }
    RemoveReceivedString();
}

void AwaitLogin()
{
/* This was changed to empty the received queue then go home, to handle
   the case where ACBot connects while it's in a room other than its
   home. */
    if( RecHead != NULL ) {
	RemoveReceivedString();
	return;
    }
    GoHome();
}

void AwaitArrive()
/* Wait for arrival at home. */
{
    char *ir;
    
    if( RecHead == NULL ) return;
    if( DoomName == POSLDOOM ) {
	ir = InRecH( "[posldoom:rm_acbotpro]" );
    } else if( DoomName == DDDOOM ) {
	ir = InRecH( "[marldoom:rm_ddbot]" );
    } else if( DoomName == WISDOOM ) {
	ir = InRecH( "[wisdoom:rm_acbot]" );
    } else {
	ir = InRecH( "[marldoom:rm_acbot]" );
    }
    if( ir != NULL ) {
	QueueCommand( "\"I am now idling, waiting for a game.", 0 );
	ChangeState( IDLE_FOR_GAME );
    }
    RemoveReceivedString();
}

void IdleForGame()
/* Wait around for someone to ask for a game. */
{
    if( RecHead == NULL ) return;
    if( StartNow == 0 ) GenericMessageCheck();
    if( StartNow == 1 ) {
	strcpy( Opponent, NameArg );
	sprintf( WriteBuf, "join %s\n", Opponent );
	QueueCommand( WriteBuf, 0 );
	QueueCommand( "look here", 0 );
	ChangeState( AWAIT_JOIN );
	StartNow = 0;
    }
    RemoveReceivedString();
}

void AwaitJoin()
/* Wait until ACBot joins its opponent. */
{
    char *p1, lc;
    int cloc;

    if( RecHead == NULL ) return;
    if( DoomName == POSLDOOM ) {
	p1 = InRecH( "[posldoom:" );
	cloc = 10;
    } else if( DoomName == WISDOOM ) {
	p1 = InRecH( "[wisdoom:" );
	cloc = 9;
    } else {
	p1 = InRecH( "[marldoom:" );
	cloc = 10;
    }
    lc = RecHead->string[strlen( RecHead->string ) - 2];
    if( p1 != NULL && lc == ']' ) {
	if( ( p1[cloc] == 'b' && p1[cloc + 1] == 'd' ) ) {
	    if( DoomName == DDDOOM ) {
		SayToOpp( "Hi.  I play SOWPODS." );
	    } else {
		SayToOpp( "Hi.  Have you read `help acbot3' lately?" );
	    }
	    if( GetRandom() % 2 == 0 )
 		strcpy( CoinToss, "heads" );
	    else
		strcpy( CoinToss, "tails" );
	    sprintf( WriteBuf, "-%s Please flip a coin.  I call %s.\n", Opponent, CoinToss );
	    QueueCommand( WriteBuf, 0 );
	    sprintf( FlipStr, "%s [to %s]: \"Please flip a coin.  I call %s.\"\n", PlrName, Opponent, CoinToss );
	    ChangeState( AWAIT_FLIP );
	} else {
	    SayToOpp( "You must be on a board before asking me to join you.  Please go to a board and try again." );
	    GoHome();
	}
    }
    RemoveReceivedString();
}

void AwaitFlip()
/* Look for a coin toss, and see if the game can be rated. */
{
/* This loop is meant to remove everything that sits on the RecHead
   queue until it sees its coin toss call.  This is partly to speed it
   up (it removes the whole board from when it did a "look here") and
   partly to ignore any flips prior to its coin toss call. */
    while( RecHead != NULL && FoundFlipStr == 0 ) {
	GenericMessageCheck();
	if( RecHead == NULL ) break;
	if( strcmp( RecHead->string, FlipStr ) == 0 ) {
	    LPrint( "Found ACBot's coin toss call.\n" );
	    
	    FoundFlipStr = 1;
	} else if( InRecH( "This board is" ) == RecHead->string ) {
	    if( InRecH( "not certified" ) != NULL )
		AllowedRated = ARATED_NO;
	    else if( DoomName == DDDOOM && InRecH( "SOWPODS" ) == NULL )
		AllowedRated = ARATED_NO;
	    else if( DoomName != DDDOOM && InRecH( "English" ) == NULL
		&& InRecH( "OSPD" ) == NULL )
		AllowedRated = ARATED_NO;
	}
	RemoveReceivedString();
    }
    if( RecHead == NULL || FoundFlipStr == 0 ) return;
    if( InRecH( "flips a coin." ) != NULL ) {
	sprintf( TBuf, "%s flips a coin.\n", Opponent );
	if( strcmp( TBuf, RecHead->string ) == 0 )
	    FlipHalf = 1;
	else
	    FlipHalf = 0;
	RemoveReceivedString();
	return;
    }
    if( FlipHalf == 1 ) {
	if( strncmp( RecHead->string, "It lands", 8 ) == 0 ) {
	    if( strncmp( &(RecHead->string[9]), CoinToss, 5 ) == 0 ) {
		MyPlrNum = 0;
		TheirPlrNum = 1;
		QueueCommand( "\"I won the toss.", 0 );
		QueueCommand( "reset", 0 );
		QueueRatedOption();
		QueueCommand( "join", 0 );
		JoinOrder = JOIN_ME_FIRST;
		ChangeState( AWAIT_ME_JOIN );
	    } else {
		MyPlrNum = 1;
		TheirPlrNum = 0;
		QueueCommand( "\"You won the toss.", 0 );
		JoinOrder = JOIN_THEM_FIRST;
		ChangeState( AWAIT_THEM_JOIN );
	    }
	}
    }
    RemoveReceivedString();
}

void AwaitMeJoin()
/* Wait for ACBot to join the game. */
{
    char jstr[40];

    if( RecHead == NULL ) return;
    if( strcmp( RecHead->string, "This game has already started.\n" ) == 0
	|| strcmp( RecHead->string, "You have already joined this game.\n" ) == 0 ) {
	SayToOpp( "Oops...the board needs to be reset.  Please page me to join you again." );
	GoHome();
    } else {
	sprintf( jstr, "%s joins the game.\n", PlrName );
	if( strcmp( RecHead->string, jstr ) == 0 ) {
	    if( JoinOrder == JOIN_ME_FIRST ) {
		ChangeState( AWAIT_THEM_JOIN );
	    } else {
		ChangeState( START_GAME );
	    }
	}
    }
    RemoveReceivedString();
}

void AwaitThemJoin()
/* Wait for the opponent to join the game. */
{
    if( RecHead == NULL ) {
	if( CurrTicks >= 30 && AskThemToJoin == 0 ) {
	    SayToOpp( "Please join the game." );
	    AskThemToJoin = 1;
	}
	return;
    }
    sprintf( TBuf, "%s joins the game.\n", Opponent );
    if( strcmp( RecHead->string, TBuf ) == 0 ) {
	if( JoinOrder == JOIN_ME_FIRST ) {
	    QueueCommand( "\"I am starting the game.", 0 );
	    QueueCommand( "start", 0 );
	    ChangeState( START_GAME );
	} else {
	    QueueRatedOption();
	    QueueCommand( "join", 0 );
	    ChangeState( AWAIT_ME_JOIN );
	}
    }
    RemoveReceivedString();
}

void StartGame()
/* Wait for the game to start. */
{
    int i;
    char c;

    if( RecHead == NULL ) {
	if( JoinOrder == JOIN_THEM_FIRST && CurrTicks >= 30
	    && AskThemToStart == 0 && HadWrongRated == 0 ) {
	    SayToOpp( "Please start the game." );
	    AskThemToStart = 1;
	}
	return;
    }
    if( RecHead->string[0] == '(' ) {
	FoundRack = 1;
	ProcessOpenBracket( RecHead->string );
	for( i = 0; i < NumTiles; i++ ) {
	    c = NewTiles[i];
	    PlrTiles[MyPlrNum][i] = c;
	}
	for( i = NumTiles; i < 7; i++ ) {
	    PlrTiles[MyPlrNum][i] = 0;
	}
	SortTiles( PlrTiles[MyPlrNum] );
    }
    if( InRecH( "Players do not agree whether the game should be rated" )
	== RecHead->string ) {
	SayToOpp( "I can only play unrated games on this board." );
	SayToOpp( "Please type `o rated n' followed by `o check'." );
	HadWrongRated = 1;
    }
    if( strcmp( RecHead->string, "Players agree on game options.\n" ) == 0 ) {
	if( HadWrongRated == 1 ) {
	    if( JoinOrder == JOIN_ME_FIRST )
		QueueCommand( "start", 0 );
	    ChangeState( START_GAME );
	}
    }
    if( FoundRack == 1 ) {
	if( MyPlrNum == 0 && strcmp( RecHead->string, MyTurn ) == 0 ) {
	    StartLookForBoard();
	    ChangeState( MY_MOVE_F );
	} else if( MyPlrNum == 1 && strcmp( RecHead->string, TheirTurn ) == 0 ) {
	    StartLookForBoard();
	    ChangeState( THEIR_MOVE );
	}
    }
    RemoveReceivedString();
}

void TheirMove()
/* Handle the opponent's move. */
{
    if( TheyChallenge == THEY_CHALL_LOSE ) {
	SayToOpp( "Looks as though I just won your challenge." );
	if( RatedGame == RATED_NO ) {
	    SayToOpp( "Since we aren't playing for ratings, I won't insist that you pass." );
	} else {
	    SayToOpp( "You must pass now." );
	    CommitLastMove( MyPlrNum, MyPlay, MyPlaced, MyDX, MyDY );
	    ChangeState( AWAIT_PASS );
	}
	TheyChallenge = THEY_CHALL_NONE;
	return;
    } else if( TheyChallenge == THEY_CHALL_WIN ) {
	SayToOpp( "Looks as though I just lost your challenge." );
	SayToOpp( "I will withdraw." );
	QueueCommand( "withdraw", 0 );
	TheyChallenge = THEY_CHALL_NONE;
	ChangeState( AWAIT_ME_START_THEIR_CLOCK );
	return;
    }
    
    if( RecHead == NULL ) {
	if( CurrTicks >= 30 && AskThemToPass == 0
	    && ResignMode == RESIGN_YES ) {
	    SayToOpp( "Please pass." );
	    AskThemToPass = 1;
	}
	return;
    }
    if( RecHead->string[0] == '>' ) {
	ProcessGreaterThan( RecHead->string );
	if( TheyMove == THEY_UNDO ) TheyMove = THEY_NO_MOVE;
    } else if( TheyMove != THEY_NO_MOVE &&
	       strcmp( RecHead->string, MyTurn ) == 0 ) {
	CommitLastMove( MyPlrNum, MyPlay, MyPlaced, MyDX, MyDY );
	if( TheyMove != THEY_PASS ) {
	    if( ResignMode == RESIGN_YES )
		SayToOpp( "I thought you were resigning.  Guess not." );
	    ResignMode = RESIGN_NO;
	}
	if( TheyMove == THEY_PLAY )
	    ChangeState( CHECK_WORDS );
	else
	    ChangeState( MY_MOVE_F );
/* Changed line below from "if TheyMove == THEY_PLAY" to "!= THEY_NO_MOVE" */
    } else if( TheyMove != THEY_NO_MOVE ) {
	if( ThemOut == 0 ) {
	    if( strcmp( RecHead->string, ThemPlayOut ) == 0 )
		ThemOut = 1;
	    else if( strcmp( RecHead->string, ScoreLess ) == 0 )
		ThemOut = 2;
	} else if( ThemOut > 0 ) {
	    if( strcmp( RecHead->string, ThemPauseClock ) == 0 ) {
		if( ThemOut == 1 ) {
		    QueueCommand( "track", 0 );
		    ChangeState( CHECK_REALLY_OUT );
		} else {
		    MeEndGame();
		}
	    }
	}
    }
    RemoveReceivedString();
}

void CheckReallyOut()
/* When `...plays out' and `...pauses the clock' are seen, check that
   there really are no tiles left. */
{
    char *p1;
    int found;

    if( RecHead == NULL ) return;
    if( strncmp( RecHead->string, "Unseen tiles:", 13 ) == 0 ) {
	found = 0;
	p1 = &(RecHead->string[13]);
	while( *p1 != '\n' ) {
	    if( *p1 == CH_BL || ( *p1 >= 'A' && *p1 <= 'Z' ) ) found++;
	    p1++;
	}
	if( found != 0 ) {
/* Note kludge of setting TheyMove here! */
	    SayToOpp( "Nice try at ending the game! :-)" );
	    ChangeState( THEIR_MOVE );
	    TheyMove = THEY_PLAY;
	} else {
	    CommitLastMove( MyPlrNum, MyPlay, MyPlaced, MyDX, MyDY );
	    ChangeState( CHECK_WORDS );
	}
    }
    RemoveReceivedString();
}

void CheckWords()
/* Look up the words played by the opponent. */
{
    int i, placed;
    int x, y, dx, dy;
    struct plrlet newlet[7];
    int w, phony;

    x = TheyX;
    y = TheyY;
    dx = 1 - TheyDir;
    dy = 1 - dx;
    placed = 0;
    for( i = 0; i < strlen( TheyWord ); i++ ) {
	if( Board[y][x] < 'A' ) {
	    newlet[placed].x = x;
	    newlet[placed].y = y;
	    if( TheyWord[i] >= 'A' && TheyWord[i] <= 'Z' )
		newlet[placed].letter = TheyWord[i];
	    else {
		newlet[placed].letter = CH_BL;
		newlet[placed].blankchar = TheyWord[i] - 'a' + 'A';
	    }
	    placed++;
	}
	x += dx;
	y += dy;
    }
    CopyMove( newlet, TheirPlay, placed );
    TheirPlaced = placed;
    TheirDX = dx;
    TheirDY = dy;
    w = FindNewWords( newlet, placed, dx, dy );
    phony = 0;
    for( i = 0; i < w; i++ ) {
	if( BinSearch( &(NewWords[i].letters[0]) ) == 0 ) {
	    phony = 1;
	}
    }
    if( phony == 0 ) {
	CommitLastMove( TheirPlrNum, TheirPlay, TheirPlaced, TheirDX, TheirDY );
	CommentOnTheirPlay();
	if( ThemOut == 1 ) {
	    MeEndGame();
	} else {
	    ChangeState( MY_MOVE_F );
	}
    } else {
	ChallWords = w;
	ChangeState( INITIATE_CHALLENGE );
    }
}

void InitiateChallenge()
/* Challenge opponent's play. */
{
    int i;
    char tword[20];

/* This function was added for the following case: if they realize they
   played a phony and remove their tiles _before_ the Bot challenges,
   then don't bother challenging.  This can happen if there is lag; the
   Bot might receive the "forfeits turn" message before it has a chance
   to challenge. */

    if( LookForForf == FOUND_FORF_YES ) {
	LookForForf = FOUND_FORF_NO;
	ChangeState( MY_MOVE_F );
	return;
    }
    PreChallenge();
    if( ThemOut == 0 && DoneTaggedTrack == 0 )
	QueueCommand( "pause", 6 + GetRandom() % 8 );
    if( DoomName == DDDOOM )
	sprintf( WriteBuf, "sowpods" );
    else
	sprintf( WriteBuf, "ospd" );
    for( i = 0; i < ChallWords; i++ ) {
	sprintf( tword, " %s", &(NewWords[i].letters[0]) );
	strcat( WriteBuf, tword );
    }
    strcat( WriteBuf, "\n" );
    QueueCommand( WriteBuf, 0 );
    if( ThemOut == 0 && DoneTaggedTrack == 0 )
	ChangeState( AWAIT_PAUSE );
    else
	ChangeState( AWAIT_RULING );
}

void AwaitPause()
/* Wait for the clock to be paused. */
{
    if( LookForForf == FOUND_FORF_YES ) {
	LookForForf = FOUND_FORF_NO;
	ChangeState( MY_MOVE_F );
	return;
    }
    if( RecHead == NULL ) return;
    if( strcmp( RecHead->string, MePauseClock ) == 0 )
	ChangeState( AWAIT_RULING );
    RemoveReceivedString();
}

void AwaitRuling()
/* Wait for a dictionary lookup ruling to come back. */
{
    int ruling;
    char hstr[30], *ir;

    if( LookForForf == FOUND_FORF_YES ) {
	LookForForf = FOUND_FORF_NO;
	ChangeState( MY_MOVE_F );
	return;
    }
    if( RecHead == NULL ) return;
    if( DoomName == DDDOOM )
/*r = InRecH( "in OSPD2+OSW3.\n" ); */
	ir = InRecH( "in TWL96+OSW3.\n" ); /* jjc 1996-02-01 */
    else
/*	ir = InRecH( "in OSPD2.\n" ); */
  	ir = InRecH( "in TWL96.\n" ); /* jjc 1996-02-01 */
    if( ir != NULL ) {
	MyRuling = 0;
	sprintf( hstr, "%s checks", PlrName );
	if( strncmp( RecHead->string, hstr, strlen( hstr ) ) == 0 )
	    MyRuling = 1;
    }
    if( MyRuling == 1 ) {
	ruling = OspdRuling( RecHead->string );
/* Note when ACBot challenges, ruling senses are reversed */
	if( ruling == RULING_UNACC ) {
	    if( DoneTaggedTrack ) {
		ChangeState( MY_MOVE_F );
	    } else {
		ChangeState( AWAIT_WITHDRAW );
	    }
	} else if( ruling == RULING_ACC ) {
	    if( ThemOut == 1 ) {
		MeEndGame();
	    } else {
		if( DoneTaggedTrack == 0 ) {
		    CommitLastMove( TheirPlrNum, TheirPlay, TheirPlaced,
				    TheirDX, TheirDY );
		}
		SayToOpp( "I must pass now." );
		QueueCommand( "pass", 0 );
		ChangeState( AWAIT_ME_START_THEIR_CLOCK );
	    }
	}
    }
    RemoveReceivedString();
}

void AwaitWithdraw()
/* Wait for the opponent to withdraw. */
{
    if( LookForForf == FOUND_FORF_YES ) {
	LookForForf = FOUND_FORF_NO;
	ChangeState( MY_MOVE_F );
    }
    if( RecHead == NULL ) {
	if( CurrTicks >= 40 && AskToWithDraw == 0 ) {
	    SayToOpp( "You must withdraw your tiles from the board." );
	    AskToWithDraw = 1;
	}
	return;
    }
    RemoveReceivedString();
}

void AwaitMeStartTheirClock()
/* Wait for ACBot to start the opponent's clock. */
{
    if( RecHead == NULL ) return;
    if( strcmp( RecHead->string, TheirTurn ) == 0 )
	ChangeState( THEIR_MOVE );
    RemoveReceivedString();
}

void MyMoveF()
/* Deal with ACBot's turn. */
{
    compmove *mvptr, *tptr, *uptr;
    int i, placed;
    int dx, dy, cl;

    if( SendHead != NULL ) return;
    if( DoneTaggedTrack == 1 ) {
	SayToOpp( "Please withdraw your tiles and `resume' the game again." );
	GoHome();
	return;
    }
    if( ResignMode == RESIGN_YES ) {
	QueueCommand( "pass", 4 );
	ChangeState( AWAIT_ME_DONE_TURN );
	return;
    }
    placed = 0;
    if( FoundRack != 1 ) {
	if( RackError == 0 ) {
	    QueueCommand( "\"Uh oh.  It's my move, but I don't know my rack.", 0 );
	    QueueCommand( "time", 4 );
	    RackError = 1;
	}
	return;
    } else if( RackError == 1 ) {
	QueueCommand( "\"Whew!  It's OK: I found it.", 0 );
	RackError = 0;
    }
    if( MyMove == ME_NO_MOVE ) {
	MyMovePtr = ComputerMove( MyPlrNum );
	if( MyMovePtr != NULL )
	    placed = MyMovePtr->placed;
	else
	    placed = 0;
	if( placed > 0 ) {
	    MyMove = ME_PLAY;
	    MyDelay = 14 + placed * 6 + GetRandom() % 10;
	} else if( placed < 0 ) {
	    MyMove = ME_EXCHANGE;
	    MyDelay = 10;
	} else {
	    MyMove = ME_PASS;
	    MyDelay = 10;
	}
	if( PlayingSpeed == SPEED_SLOW )
	    MyDelay *= 2;
	else if( PlayingSpeed == SPEED_FAST )
	    MyDelay = 0;
	return;
    }
/* Note: while delaying move, assume GenericMessageCheck() deals with
   any strings received and remove them here. */
    if( LookForForf == FOUND_FORF_YES ) {
	LookForForf = FOUND_FORF_NO;
	QueueCommand( ":isn't sure why you did that...", 0 );
	RestoreGameState();
	ChangeState( MY_MOVE_F );
	return;
    }
    if( RecHead != NULL ) {
	RemoveReceivedString();
	return;
    }
    if( CurrTicks < MyDelay ) return;
    mvptr = MyMovePtr;
    if( MyMove == ME_PLAY ) {
	placed = mvptr->placed;
	ConvertMove( mvptr );
	dx = 1 - mvptr->dir;
	dy = mvptr->dir;
	sprintf( WriteBuf, "place %s\n", MoveString );
	QueueCommand( WriteBuf, 0 );
	QueueCommand( "commit", 0 );
	CopyMove( mvptr->newlet, MyPlay, placed );
	MyWords = FindNewWords( MyPlay, placed, dx, dy );
	MyPlaced = placed;
	MyDX = dx;
	MyDY = dy;
	CommentOnMyPlay( mvptr, COMM_MYPLAY );
    } else if( MyMove == ME_EXCHANGE ) {
	placed = mvptr->placed;
	sprintf( WriteBuf, "dump " );
	cl = 5;
	for( i = 0; i < -placed; i++ ) {
	    WriteBuf[cl++] = mvptr->newlet[i].letter;
	}
	WriteBuf[cl] = '\n';
	QueueCommand( WriteBuf, 0 );
	MyPlaced = -1;
    } else {
	QueueCommand( "pass", 0 );
	MyPlaced = -1;
    }
    tptr = CHead;
    while( tptr != NULL ) {
	uptr = tptr->next;
	free( (compmove *) tptr );
	tptr = uptr;
    }
    ChangeState( AWAIT_ME_DONE_TURN );
}

void AwaitMeDoneTurn()
/* Wait for ACBot to be finished.  Needed to simulate `medium' and `slow'. */
{
    int i;
    char c;

    if( RecHead == NULL ) return;
    if( ( MyMove == ME_EXCHANGE || MyMove == ME_PLAY )
	&& RecHead->string[0] == '(' ) {
	FoundRack = 1;
	ProcessOpenBracket( RecHead->string );
	for( i = 0; i < NumTiles; i++ ) {
	    c = NewTiles[i];
	    PlrTiles[MyPlrNum][i] = c;
	}
	for( i = NumTiles; i < 7; i++ ) {
	    PlrTiles[MyPlrNum][i] = 0;
	}
	SortTiles( PlrTiles[MyPlrNum] );
    } else if( MyMove == ME_PASS ) {
	FoundRack = 1;
    }
    if( MeOut == 0 ) {
	if( strcmp( RecHead->string, MePlayOut ) == 0
	    || strcmp( RecHead->string, ScoreLess ) == 0 )
	    MeOut = 1;
    } else if( MeOut == 1 ) {
	if( strcmp( RecHead->string, MePauseClock ) == 0 ) {
	    WhoEndsGame = THEY_END_GAME;
	    sprintf( EndGameStr, "%s ends the game.\n", Opponent );
	    ChangeState( END_OF_GAME );
	}
    }
    
    if( strcmp( RecHead->string, TheirTurn ) == 0 ) {
	if( FoundRack == 0 )
	    QueueCommand( "time", 0 );
	ChangeState( THEIR_MOVE );
    }
    RemoveReceivedString();
}

void AwaitPass()
/* Wait for the opponent to pass after losing a challenge. */
{
    if( RecHead == NULL ) return;
    if( RecHead->string[0] == '>' ) {
	ProcessGreaterThan( RecHead->string );
	if( TheyMove == THEY_UNDO )
	    TheyMove = THEY_NO_MOVE;
    } else if( strcmp( RecHead->string, MyTurn ) ) {
	if( TheyMove == THEY_PASS ) {
	    ChangeState( MY_MOVE_F );
	} else if( TheyMove != THEY_NO_MOVE ) {
	    SayToOpp( "Hey!  You were supposed to pass!" );
	    LeaveInHuff( Opponent );
	}
    }
    RemoveReceivedString();
}

void EndOfGame()
/* Wait for the end of the game. */
{
    char *p1;
    int mys, theirs;

    if( RecHead == NULL ) {
	if( WhoEndsGame == THEY_END_GAME && CurrTicks >= 60
	    && AskThemToEnd == 0 ) {
	    SayToOpp( "Please end the game." );
	    AskThemToEnd = 1;
	}
	return;
    }
    if( TheyChallenge == THEY_CHALL_LOSE ) {
	TheyChallenge = THEY_CHALL_NONE;
	CommitLastMove( MyPlrNum, MyPlay, MyPlaced, MyDX, MyDY );
    } else if( TheyChallenge == THEY_CHALL_WIN ) {
	SayToOpp( "Looks as though I just lost your challenge." );
	SayToOpp( "I will withdraw." );
	QueueCommand( "withdraw", 0 );
	TheyChallenge = THEY_CHALL_NONE;
	ChangeState( AWAIT_ME_START_THEIR_CLOCK );
	return;
    }
    
    if( strcmp( RecHead->string, EndGameStr ) == 0 ) {
	EndGame = 1;
    } else if( EndGame == 1 &&
	       strncmp( RecHead->string, "> Final scores:", 15 ) == 0 ) {
	p1 = InRecH( PlrName ) + strlen( PlrName ) + 1;
	sscanf( p1, "%d", &mys );
	p1 = InRecH( Opponent ) + strlen( Opponent ) + 1;
	sscanf( p1, "%d", &theirs );
	LPrint( "Scores: me %d, they %d\n", mys, theirs ); 
	Matches++;
	EndGameComment( mys, theirs );
	RematchComment();
	QueueCommand( "log2", 0 );
	if( AllowedRated == ARATED_NO )
	    QueueCommand( "o rated n", 0 );
	else
	    QueueCommand( "o rated ?", 0 );
	QueueCommand( "o timed ?", 0 );
	ChangeState( AWAIT_REMATCH );
    }
    RemoveReceivedString();
}

void AwaitRematch()
/* Ask for a rematch and wait for the response. */
{
    if( SendHead != NULL ) return;
    if( RecHead == NULL ) {
	if( CurrTicks >= 240 ) {
	    DeclineRematch( 1 );
	    GoHome();
	}
	if( CurrTicks >= 180 && AskRematchAgain == 0 ) {
	    SayToOpp( "Did you want a rematch?  I don't think you've answered yet." );
	    AskRematchAgain = 1;
	}
	return;
    }
    if( RematchResponse == REMATCH_YES ) {
	AcceptRematch();
	InitBoard();
	InitTiles();
	QueueCommand( "reset", 4 );
	if( JoinOrder == JOIN_ME_FIRST ) {
	    MyPlrNum = 1 - MyPlrNum;
	    TheirPlrNum = 1 - TheirPlrNum;
	    SayToOpp( "I went first last game, so you go first now." );
	    JoinOrder = JOIN_THEM_FIRST;
	    ChangeState( AWAIT_THEM_JOIN );
	} else {
	    MyPlrNum = 1 - MyPlrNum;
	    TheirPlrNum = 1 - TheirPlrNum;
	    SayToOpp( "You went first last game, so I go first now." );
	    QueueRatedOption();
	    QueueCommand( "join", 0 );
	    JoinOrder = JOIN_ME_FIRST;
	    ChangeState( AWAIT_ME_JOIN );
	}
    } else if( RematchResponse == REMATCH_NO ) {
	DeclineRematch( 0 );
	GoHome();
    }
    RemoveReceivedString();
}

void ConsultMode()
/* Handle `consult' mode. */
{
/* Is the next line right? */
    if( RecHead != NULL ) RemoveReceivedString();
    if( CurrTicks >= 240 ) {
	if( Opponent[0] != '\0' ) {
	    SayToOpp( "I'm exiting `consult' mode now." );
	    SayToOpp( "If you want a rematch, page me with `rematch'." );
	    SayToOpp( "Otherwise, page me with `decline'." );
	    ChangeState( AWAIT_REMATCH );
	} else {
	    sprintf( WriteBuf, "-%s I'm going home to idle again.", Consultee );
	    QueueCommand( WriteBuf, 0 );
	    GoHome();
	    ChangeState( IDLE_FOR_GAME );
	}
    } else if( CurrTicks >= 180 && WarnEndConsult == 0 ) {
	sprintf( WriteBuf, "-%s If I do not receive another `words' command in the", Consultee );
	QueueCommand( WriteBuf, 0 );
	sprintf( WriteBuf, "-%s next 30 seconds, I will exit `consult' mode.", Consultee );
	QueueCommand( WriteBuf, 0 );
	WarnEndConsult = 1;
    }
}
