/*
 *
 * botutil1.c -- utilities for the robot, part 1
 *
 * 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"
#include "fowc.h"

void InitGameStart()
/* Initialize variables at start of game. */
{
    MyWords = 0;
    TheirWords = 0;
    MyPlaced = -1;
    TheirPlaced = -1;
    MyWLen = 0;
    TheirWLen = 0;
    TheyChallenge = THEY_CHALL_NONE;
    ResignMode = RESIGN_NO;
    ChallengeWarn = 0;
    WhoEndsGame = 0;
    DoneTaggedTrack = 0;
    sprintf( MyTurn, "It is %s's turn.\n", PlrName );
    sprintf( MePlayOut, "%s plays out.\n", PlrName );
    sprintf( MePauseClock, "%s pauses the clock.\n", PlrName );
    sprintf( TheirTurn, "It is %s's turn.\n", Opponent );
    sprintf( ThemPlayOut, "%s plays out.\n", Opponent );
    sprintf( ThemPauseClock, "%s pauses the clock.\n", Opponent );
    sprintf( ScoreLess, "There have now been six successive scoreless turns.\n" );
}

void ChangeState( newstate )
    int newstate;
/* Change states, setting variables as appropriate for the new state. */
{
    LPrint( "(Changing states: %d to %d)\n", CurrState, newstate );
    if( newstate == IDLE_FOR_GAME ) {
	InitBoard();
	InitTiles();
	Opponent[0] = '\0';
	PlayingSpeed = SPEED_MEDIUM;
	if( DoomName == POSLDOOM || DoomName == DDDOOM ) {
	    TalkMode = MODE_SILENT;
	} else {
	    TalkMode = MODE_VERBOSE;
	}
	TimedGame = TIMED_NONE;
	RatedGame = RATED_NONE;
	QueueCommand( "o rated ?", 0 );
	QueueCommand( "o timed ?", 0 );
	QueueCommand( "o sort y", 0 );
	MWYSize = 8;
	Matches = 0;
    } else if( newstate == AWAIT_FLIP ) {
	AllowedRated = ARATED_YES;
	FoundFlipStr = 0;
	FlipHalf = 0;
    } else if( newstate == AWAIT_THEM_JOIN ) {
	AskThemToJoin = 0;
    } else if( newstate == START_GAME ) {
	AskThemToStart = 0;
	FoundRack = 0;
	HadWrongRated = 0;
	InitGameStart();
	InitBoard();
	InitTiles();
    } else if( newstate == THEIR_MOVE ) {
	AskThemToPass = 0;
	TheyMove = THEY_NO_MOVE;
	TheyChallenge = 0;
	ThemOut = 0;
    } else if( newstate == MY_MOVE_F ) {
	MyMove = ME_NO_MOVE;
	RackError = 0;
	MeOut = 0;
    } else if( newstate == AWAIT_ME_DONE_TURN ) {
	FoundRack = 0;
    } else if( newstate == AWAIT_RULING ) {
	MyRuling = 0;
    } else if( newstate == AWAIT_WITHDRAW ) {
	AskToWithDraw = 0;
    } else if( newstate == AWAIT_PASS ) {
	TheyMove = THEY_NO_MOVE;
    } else if( newstate == END_OF_GAME ) {
	AskThemToEnd = 0;
	EndGame = 0;
    } else if( newstate == AWAIT_REMATCH ) {
	RematchResponse = REMATCH_NONE;
	AskRematchAgain = 0;
    } else if( newstate == CONSULT_MODE ) {
	WarnEndConsult = 0;
    }
    PrevState = CurrState;
    PrevTicks = CurrTicks;
    CurrState = newstate;
    CurrTicks = 0;
}

void QueueCommand( command, delay )
    char *command;
    int delay;
/* Add a command to the queue to be sent to the DOoM. */
{
    sendstr *newptr, *ptr1;

    newptr = (sendstr *) malloc( WS_SIZE );
    if( newptr == NULL ) {
	LPrint( "*** Error: no memory left in QueueCommand()" );
	ExitWindow();
	exit( 10 );
    }
    newptr->next = NULL;
    newptr->string = (char *) malloc( strlen( command ) + 2 );
    strcpy( newptr->string, command );
    newptr->string[strlen( command )] = '\n';
    newptr->string[strlen( command ) + 1] = '\0';
    if( PlayingSpeed == SPEED_SLOW ) {
	newptr->delay = delay * 2;
    } else if( PlayingSpeed == SPEED_MEDIUM ) {
	newptr->delay = delay;
    } else {
	newptr->delay = 0;
    }

    ptr1 = SendHead;
    if( ptr1 == NULL ) SendHead = newptr;
    else {
	while( ptr1->next != NULL )
	    ptr1 = ptr1->next;
	ptr1->next = newptr;
    }
}

void CheckCommandQueue()
/* Check to see if any commands are ready to be sent.  Also, decrement the
   delay field of the first entry in the queue. */
{
    sendstr *ptr1;
    char *p;
    int cmtype, pagearg;

    while( SendHead != NULL && SendHead->delay == 0 ) {
	if( strstr( SendHead->string, " PERFORM_SEARCH" ) != NULL ) {
	    sscanf( SendHead->string, "%s %d %d", RName, &cmtype, &pagearg );
	    PerformSearch( cmtype, pagearg );
	} else if( ( p = strstr( SendHead->string, " BOARD_LOOKUP" ) ) != NULL ) {
	    strncpy( LFBName, SendHead->string, p - SendHead->string );
	    DoBoardLookup();
	} else {
	    strcpy( WriteBuf, SendHead->string );
	    DoWrite( SockP );
	}
	ptr1 = SendHead->next;
	free( (char *) SendHead->string );
	free( (sendstr *) SendHead );
	SendHead = ptr1;
    }
    if( SendHead != NULL )
	SendHead->delay--;
}

void process_reset( name )
    char *name;
/* Deal with `reset' command. */
{
    if( ( CurrState != IDLE_FOR_GAME && CurrState != CONSULT_MODE )
	|| ( CurrState == CONSULT_MODE && strcmp( name, Consultee ) == 0 )
	|| strcmp( name, "JamesAC" ) == 0
	|| strcmp( name, "poslfit" ) == 0 ) {
	sprintf( WriteBuf, "\"I have been reset by %s.", name );
	QueueCommand( WriteBuf, 0 );
	GoHome();
    } else if( CurrState == CONSULT_MODE ) {
	sprintf( WriteBuf, "-%s Only %s may reset me.", name, Consultee );
	QueueCommand( WriteBuf, 0 );
    }
}

void process_medium( name )
    char *name;
/* Deal with `medium' command. */
{
    sprintf( WriteBuf, "\"At %s's request, I am setting my speed to `medium'.", name );
    QueueCommand( WriteBuf, 0 );
    PlayingSpeed = SPEED_MEDIUM;
}

void process_slow( name )
    char *name;
/* Deal with `slow' command. */
{
    sprintf( WriteBuf, "\"At %s's request, I am setting my speed to `slow'.", name );
    QueueCommand( WriteBuf, 0 );
    PlayingSpeed = SPEED_SLOW;
}

void process_fast( name )
    char *name;
/* Deal with `fast' command. */
{
    sprintf( WriteBuf, "\"At %s's request, I am setting my speed to `fast'.", name );
    QueueCommand( WriteBuf, 0 );
    PlayingSpeed = SPEED_FAST;
    MyDelay = 0;
}

void process_silent( name )
    char *name;
/* Deal with `silent' command. */
{
    sprintf( WriteBuf, "\"At %s's request, I am now `silent'.", name );
    QueueCommand( WriteBuf, 0 );
    TalkMode = MODE_SILENT;
}

void process_verbose( name )
    char *name;
/* Deal with `verbose' command. */
{
    sprintf( WriteBuf, "\"At %s's request, I am now `verbose'.", name );
    QueueCommand( WriteBuf, 0 );
    TalkMode = MODE_VERBOSE;
}

void process_status( name )
    char *name;
/* Deal with `status' command. */
{
    char speed[10], talk[10];
    
    speed[0] = '\0'; talk[0] = '\0';
    if( PlayingSpeed == SPEED_FAST ) strcpy( speed, "fast" );
    if( PlayingSpeed == SPEED_MEDIUM ) strcpy( speed, "medium" );
    if( PlayingSpeed == SPEED_SLOW ) strcpy( speed, "slow" );
    if( TalkMode == MODE_SILENT ) strcpy( talk, "silent" );
    if( TalkMode == MODE_VERBOSE ) strcpy( talk, "verbose" );
    sprintf( WriteBuf, "\"My speed is `%s', and I am `%s'.", speed, talk );
    QueueCommand( WriteBuf, 0 );
    WriteBuf[0] = '\0';
    if( CurrState == IDLE_FOR_GAME )
	sprintf( WriteBuf, "\"I am idling, waiting for a game." );
    if( CurrState == AWAIT_FLIP )
	sprintf( WriteBuf, "\"I am waiting for a coin flip or a `resume'." );
    if( CurrState == AWAIT_ME_JOIN )
	sprintf( WriteBuf, "\"I am waiting to see `%s joins the game'.", PlrName );
    if( CurrState == AWAIT_THEM_JOIN )
	sprintf( WriteBuf, "\"I am waiting to see `%s joins the game'.", Opponent );
    if( CurrState == START_GAME )
	sprintf( WriteBuf, "\"I am waiting for the game to start." );
    if( CurrState == THEIR_MOVE )
	sprintf( WriteBuf, "\"I am waiting for %s to move.", Opponent );
    if( CurrState == CHECK_REALLY_OUT )
	sprintf( WriteBuf, "\"I am checking that %s has played out.", Opponent );
    if( CurrState == AWAIT_PAUSE )
	sprintf( WriteBuf, "\"I am waiting to see `%s pauses the clock'.", PlrName );
    if( CurrState == AWAIT_RULING )
	sprintf( WriteBuf, "\"I am awaiting a ruling on my challenge." );
    if( CurrState == AWAIT_WITHDRAW )
	sprintf( WriteBuf, "\"I am waiting for %s to withdraw.", Opponent );
    if( CurrState == AWAIT_ME_START_THEIR_CLOCK )
	sprintf( WriteBuf, "\"I am waiting to see `It is %s's turn'.", Opponent );
    if( CurrState == MY_MOVE_F )
	sprintf( WriteBuf, "\"I am thinking about my move." );
    if( CurrState == AWAIT_ME_DONE_TURN )
	sprintf( WriteBuf, "\"I am awaiting confirmation of my `commit'." );
    if( CurrState == AWAIT_PASS )
	sprintf( WriteBuf, "\"I am awaiting confirmation of my `pass'." );
    if( CurrState == END_OF_GAME )
	sprintf( WriteBuf, "\"I am waiting to see `... ends the game.'" );
    if( CurrState == AWAIT_REMATCH )
	sprintf( WriteBuf, "\"I am waiting for %s's rematch response.", Opponent );
    if( CurrState == CONSULT_MODE )
	sprintf( WriteBuf, "\"I am consulting with %s.", Consultee );
    if( WriteBuf[0] != '\0' ) QueueCommand( WriteBuf, 0 );
}

void process_resign( name )
    char *name;
/* Deal with `resign' command. */
{
    if( CurrState != THEIR_MOVE ) {
	SayToOpp( "You can only resign when it is your turn." );
    } else if( PlrScores[TheirPlrNum] + 50 > PlrScores[MyPlrNum] ) {
	SayToOpp( "You can only resign when behind by 50 or more points." );
    } else {
	SayToOpp( "I accept your resignation.  Please pass." );
	ResignMode = RESIGN_YES;
	AskThemToPass = 1;
    }
}

void process_consult( name )
    char *name;
/* Deal with `consult' command. */
{
    if( strstr( name, "connector" ) != NULL ) return;
    if( CurrState == IDLE_FOR_GAME ||
	( CurrState == CONSULT_MODE && strcmp( name, Consultee ) == 0 )
	|| ( CurrState == AWAIT_REMATCH && strcmp( name, Opponent ) == 0 )
	|| ( CurrState == AWAIT_FLIP && strcmp( name, Opponent ) == 0 ) ) {
	strcpy( Consultee, name );
/* Do _not_ join them if they're the opponent!  Otherwise things can
   really screw up badly.  The opponent might go to a different room and
   then say `consult'.  When the Bot tries to continue the game, it
   will no longer be at the same board. */
	if( Opponent[0] == '\0' ) {
	    sprintf( WriteBuf, "join %s", Consultee );
	    QueueCommand( WriteBuf, 0 );
	} else if( CurrState == AWAIT_FLIP ) {
/* This is special-case code for when someone types `join me' then `consult'.
   ACBot should treat this as though the `join me' had been omitted. */
	    Opponent[0] = '\0';
	}
	sprintf( WriteBuf, "\"I am now consulting with %s.", Consultee );
	QueueCommand( WriteBuf, 0 );
	ChangeState( CONSULT_MODE );
	return;
    }
    if( strcmp( name, Opponent ) != 0 ) {
	AlreadyOccupied( CommForm, name );
    } else {
	SayToOpp( "You can only consult me at the end of a game." );
    }
}

void process_command( name )
    char *name;
/* Entry point for command processing.  JamesAC and poslfit are superusers. */
{
    char *p1;
    int i;
    
    if( ( strcmp( name, "JamesAC" ) == 0
	  || strcmp( name, "poslfit" ) == 0 ) && Command == CMD_DO ) {
	i = 0;
	p1 = CmdPtr;
	while( *p1 != '\n' ) WriteBuf[i++] = *p1++;
/* Remove trailing quote */
	i--;
	WriteBuf[i++] = '\n';
	WriteBuf[i++] = '\0';
	DoWrite( SockP );
	RemoveReceivedString();
	return;
    }
    
    my_to_lower( RecHead->string );
    if( Command == CMD_RESET ) process_reset( name );
    if( Command == CMD_FAST ) process_fast( name );
    if( Command == CMD_MEDIUM ) process_medium( name );
    if( Command == CMD_SLOW ) process_slow( name );
    if( Command == CMD_SILENT ) process_silent( name );
    if( Command == CMD_VERBOSE ) process_verbose( name );
    if( Command == CMD_STATUS ) process_status( name );
    if( Command == CMD_RESIGN ) process_resign( name );
    if( Command == CMD_CONSULT ) process_consult( name );
    if( Command == CMD_RESUME ) {
	if( CurrState == IDLE_FOR_GAME ) {
	    sprintf( WriteBuf, "%c%s I have to join you before we resume.\n", 
		     CommForm, name );
	    QueueCommand( WriteBuf, 2 );
	} else StartResume();
    }
    if( CurrState == AWAIT_REMATCH ||
	( CurrState == CONSULT_MODE && Opponent[0] != '\0' ) ) {
	if( Command == CMD_REMATCH ) {
	    if( CurrState == CONSULT_MODE )
		ChangeState( AWAIT_REMATCH );
	    RematchResponse = REMATCH_YES;
	} else if( Command == CMD_DECLINE ) {
	    if( CurrState == CONSULT_MODE )
		ChangeState( AWAIT_REMATCH );
	    RematchResponse = REMATCH_NO;
	} else {
	    RemoveReceivedString();
	}
    } else {
	RemoveReceivedString();
    }
}

void GoHome()
/* Queue commands that make robot go home. */
{
    QueueCommand( "home", 2 );
    QueueCommand( "look here", 0 );
    ChangeState( AWAIT_ARRIVE );
}

void QueueRatedOption()
/* Queue commands to set rated option. */
{
    if( AllowedRated == ARATED_YES )
	QueueCommand( "o rated ?", 0 );
    else
	QueueCommand( "o rated n", 0 );
}

void LeaveInHuff( name )
    char *name;
/* Reset the board after unsportsmanlike conduct and go home. */
{
    sprintf( WriteBuf, "-%s Because of that, I'm leaving.\n", CName );
    QueueCommand( WriteBuf, 0 );
    QueueCommand( "reset", 0 );
    GoHome();
}

void SayToOpp( string )
    char *string;
/* Say something to opponent. */
{
    sprintf( WriteBuf, "-%s %s\n", Opponent, string );
    QueueCommand( WriteBuf, 0 );
}

void my_to_lower( string )
    char *string;
/* Convert a string to lower case. */
{
    char *p;
    
    p = string;
    while( *p != '\n' && *p != '\0' ) {
	if( *p >= 'A' && *p <= 'Z' ) *p = *p - 'A' + 'a';
	p++;
    }
}

char *InRecH( string )
    char *string;
/* Look for the occurence of a string in the first string on the received
   queue, and return a pointer to it. */
{
    return( strstr( RecHead->string, string ) );
}

int blist_check( name )
    char *name;
/* Check the blacklist for a particular name. */
{
    int i;
    
    for( i = 0; i < BListLen; i++ )
	if( strcmp( name, BlackList[i] ) == 0 ) return( 1 );
    return( 0 );
}

int check_name_begin( endptr )
    char *endptr;
/* Verify that the first word in the first received queue string looks
   something like a player's name. */
{
    char *p1;
    
    if( endptr == NULL ) return( 0 );
    p1 = RecHead->string;
    if( endptr - p1 > 18 ) return( 0 );
    else {
	while( p1 < endptr ) {
	    if( *p1++ == ' ' ) {
		return( 0 );
	    }
	}
    }
    if( strncmp( RecHead->string, "connector", 9 ) == 0 ) return( 0 );
    return( 1 );
}

void process_last( form )
    char form;
/* Deal with an invalid command. */
{
    if( blist_check( NameArg ) == 1 ) return;
    if( CurrState == IDLE_FOR_GAME && blist_check( NameArg ) == 0 )
	Instructions( form, NameArg );
    RemoveReceivedString();
}

void process_help( form )
    char form;
/* Deal with a `help' command. */
{
    if( blist_check( NameArg ) == 1 ) return;
    if( form == '-' )
	sprintf( WriteBuf, "-%s Don't say `help' to _me_; type `help acbot' by itself.", NameArg );
    else
	sprintf( WriteBuf, "'%s Don't page _me_ with `help'; type `help acbot' by itself.", NameArg );
    QueueCommand( WriteBuf, 2 );
    RemoveReceivedString();
}

void process_join( form )
    char form;
{
    int i;

    LPrint( "Found `join me' %s\n", NameArg ); 
    if( CurrState != IDLE_FOR_GAME ||
	( CurrState == CONSULT_MODE &&
	  strcmp( NameArg, Consultee ) != 0 ) ) {
	LPrint( "Sending `occupied'.\n" ); 
	if( blist_check( NameArg ) == 0 )
	    AlreadyOccupied( form, NameArg );
	RemoveReceivedString();
    } else if( blist_check( NameArg ) == 1 ) {
	i = 0;
	AddToStr( "'", &i );
	AddToStr( NameArg, &i );
	AddToStr( " Because of your unsportsmanlike behavior against me in the past, I will no longer play you.", &i );
	TermStr( i, 0 );
	RemoveReceivedString();
    } else {
	LPrint( "Startnow=%d.\n", StartNow ); 
	StartNow = 1;
    }
}

void process_hint( form )
    char form;
{
    if( blist_check( NameArg ) == 1 ) return;
    if( CurrState != IDLE_FOR_GAME ) {
	sprintf( WriteBuf, "%c%s You can use `hint' only when I am idle.\n", form, NameArg );
	QueueCommand( WriteBuf, 2 );
	RemoveReceivedString();
    } else {
	StartHintCommand( NameArg, form );
    }
}

void process_words_command( form )
    char form;
/* Handle one of the many types of `words' command. */
{
    if( blist_check( NameArg ) == 1 ) return;
    if( CurrState != IDLE_FOR_GAME && CurrState != CONSULT_MODE ) {
	if( Command == CMD_WORDS ) {
	    sprintf( WriteBuf, "%c%s You can only use `words' when I am idle or in `consult' mode.\n", form, NameArg );
	    QueueCommand( WriteBuf, 2 );
	}
    } else {
	ProcessWordsCommand( NameArg );
    }
    RemoveReceivedString();
}

void process_critique( form )
    char form;
/* Handle `critique' command. */
{
    char *p1, board[20];
    int i;

    if( blist_check( NameArg ) == 1 ) return;
    p1 = InRecH( "critique" ) + 8;
    while( *p1 == ' ' ) p1++;
    i = 0;
    while( *p1 != '"' && *p1 != ' ' && i < 19 ) board[i++] = *p1++;
    board[i] = '\0';
    if( board[0] == '\0' ) {
	sprintf( WriteBuf, "%c%s You have tell me a board to critique.\n",
		 form, NameArg );
	QueueCommand( WriteBuf, 2 );
    } else {
	sprintf( WriteBuf, "log2 -tagged %s", board );
	QueueCommand( WriteBuf, 0 );
	AddTagEntry( TTYPE_LOG2, NULL, NameArg, board, form );
    }
    RemoveReceivedString();
}

void GenericMessageCheck()
/* Look for messages that must be dealt with irrespective of state. */
{
    char *p1, *p2;
    char form;
    char tostr[30];
    char joino[50];
    int len, i;

    if( RecHead == NULL ) return;

/* Look for being spoken to */
    form = '\'';
    p1 = InRecH( " pages you:" );
    sprintf( tostr, " [to %s]:", PlrName );
    p2 = InRecH( tostr );
    if( p2 != NULL ) {
	p1 = p2;
	form = '-';
    }
    if( check_name_begin( p1 ) == 1 ) {
	len = p1 - RecHead->string;
	strncpy( NameArg, RecHead->string, len );
	NameArg[len] = '\0';
	p1 = InRecH( ": \"" ) + 3;
	while( *p1 == ' ' ) p1++;
	i = 0;
	while( i < 10 && *p1 != ' ' && *p1 != '"' ) CmdStr[i++] = *p1++;
	CmdStr[i] = '\0';
	while( *p1 != ' ' && *p1 != '\0' ) p1++;
	CmdPtr = p1;
	my_to_lower( CmdStr );
	Command = CMD_LAST;
	for( i = 0; Commands[i].ctype != CMD_LAST; i++ ) {
	    if( strcmp( CmdStr, Commands[i].cname ) == 0 ) {
		Command = Commands[i].ctype;
		break;
	    }
	}
	
	StartNow = 0;
	if( Command == CMD_LAST ) {
/* Received an invalid command */
	    process_last( form );
	    return;
	}
	if( Command == CMD_HELP ) {
/* Received `help' command */
	    process_help( form );
	    return;
	}
	if( Command == CMD_JOIN_ME ) {
/* Received `join me' command */
	    process_join( form );
	    return;
	}
	if( Command == CMD_HINT ) {
/* Received `hint' command */
	    my_to_lower( RecHead->string );
	    process_hint( form );
	    return;
	}
	if( Command == CMD_WORDS || Command == CMD_MORE
	    || Command == CMD_BACK || Command == CMD_FORWARD
	    || Command == CMD_GOTO || Command == CMD_LINES ) {
/* Received one of the `words' commands */
	    process_words_command( form );
	    return;
	}
	if( Command == CMD_CRITIQUE ) {
/* Received `critique' command */
	    my_to_lower( RecHead->string );
	    process_critique( form );
	    return;
	}
	if( ( strcmp( NameArg, Opponent ) == 0 && Opponent[0] != '\0' )
/* Handle any other command */
		   || Command == CMD_CONSULT
		   || ( CurrState == CONSULT_MODE && Command == CMD_RESET )
		   || strcmp( NameArg, "JamesAC" ) == 0
		   || strcmp( NameArg, "poslfit" ) == 0 ) {
	    if( blist_check( NameArg ) == 1 ) return;
	    CommForm = form;
	    process_command( NameArg );
	}
	return;
    }
    
/* Look for someone joining ACBot or the opponent. */
    sprintf( tostr, " joins %s.\n", PlrName );
    p1 = InRecH( tostr );
    if( p1 == NULL ) {
	sprintf( joino, " joins %s.\n", Opponent );
	p1 = InRecH( joino );
    }
    if( check_name_begin( p1 ) == 1 ) {
	len = p1 - RecHead->string;
	strncpy( NameArg, RecHead->string, len );
	NameArg[len] = '\0';
	if( strcmp( NameArg, Opponent ) != 0 && blist_check( NameArg ) == 0 )
	    GreetJoiner( NameArg );
	RemoveReceivedString();
	return;
    }
    
/* Look for display generated by "time" */
    if( strncmp( RecHead->string, PlrName, strlen( PlrName ) ) == 0 ) {
	p1 = InRecH( ":" );
	if( p1 == NULL ) goto done_time_look;
	if( p1[1] >= '0' && p1[1] <= '9' && p1[2] >= '0' && p1[2] <= '9' && p1[3] == ' ' ) {
	    p1 = p1 + 4;
	    i = 0;
	    while( ( *p1 >= 'A' && *p1 <= 'Z' ) || *p1 == CH_BL )
		PlrTiles[MyPlrNum][i++] = *p1++;
	    while( i < 7 )
		PlrTiles[MyPlrNum][i++] = 0;
	    SortTiles( PlrTiles[MyPlrNum] );
	    FoundRack = 1;
	}
    }
  done_time_look:
/* Look for opponent disconnecting */
    if( CurrState > IDLE_FOR_GAME ) {
	sprintf( TBuf, "%s disconnects.\n", Opponent );
	if( strcmp( RecHead->string, TBuf ) == 0 ) {
	    QueueCommand( "pause", 0 );
/* Do a `who1' so log file will record if they really disconnected. */
	    QueueCommand( "who1", 0 );
	    if( CurrState >= START_GAME && CurrState != AWAIT_REMATCH )
		SayToOpp( "To continue this game later, use the `resume' command." );
	    GoHome();
	    RemoveReceivedString();
	    return;
	}
    }
/* Look for '>' to figure out score, but don't dequeue it */
    if( RecHead->string[0] == '>' ) {
	if( InRecH( PlrName ) != NULL ) i = MyPlrNum;
	else if( InRecH( Opponent ) != NULL ) i = TheirPlrNum;
	else goto done_adj_score; /* should never happen */
	p1 = InRecH( "+" );
	if( p1 == NULL ) p1 = InRecH( "-" );
	if( p1 == NULL ) goto done_adj_score; /* should never happen */
	p1++;
	while( *p1 >= '0' && *p1 <= '9' ) p1++;
	while( *p1 == ' ' ) p1++;
	len = 0;
	if( *p1 == '-' ) {
	    len = 1;
	    p1++;
	}
	PlrScores[i] = 0;
	while( *p1 >= '0' && *p1 <= '9' )
	    PlrScores[i] = PlrScores[i] * 10 + *p1++ - '0';
    }
  done_adj_score:
    LookForStartChallenge();
    LookForEndChallenge();
    LookForForfeit();
/* If looking for rack while on its move, remove string until find rack. */
    if( CurrState == MY_MOVE_F && FoundRack == 0 && RecHead != NULL )
	RemoveReceivedString();
}
