/*
 *
 * critbatch.c -- routines to critique in batch mode
 *
 * 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 "proto.h"
#include "critique.h"

/* Static global variables */

int TPlay[2][27], TPC[2];  /* distrib & count of tiles played */
int TDrawn[2][28], TDC[2]; /* distrib & count of tiles drawn */
int PlayC[2][2];           /* count of plays made */
int DumpC[2][2];           /* count of dumps made */
int ForfC[2][2];           /* count of forfeits made */
int PassC[2][2];           /* count of passes made */
int DLSC[2], TLSC[2];      /* double/triple letter score plays */
int DWSC[2], TWSC[2];      /* double/triple word score plays */
int RScore[2], RScoreN[2]; /* total rack score */
int RLeave[2], RLeaveN[2]; /* rack leave value */
int TEq[2], TEqN[2];       /* total equity sacrificed */
int TTEq[2], TTEqN[2];     /* total equity of top-scoring play */
int FinalS[2];             /* final score before endgame */
int InEndGame;             /* 1 when no tiles left in the bag */

/* Structure for words in statistics summary */

typedef struct swe {
    char *word;
    struct swe *next;
} swent;

swent *SBPHead[2];   /* pointer to played bingos */
swent *SBUHead[2];   /* pointer to missed bingos */
swent *SPCHead[2];   /* pointer to phonies challenged off */
swent *SPGHead[2];   /* pointer to phonies gotten away with */

swent *OutHead[2];   /* head of lists to be outputted */
int OutSize[2];      /* column counter for output lists */

#define SWENT_SIZE ( sizeof( swent ) )
#define ITEM_WIDTH 18
#define DATA_WIDTH 29

void check_for_endgame( void )
{
    int i, tc;

    for( tc = 0, i = 0; i < 27; i++ ) tc += TilesLeft[i];
    if( tc <= 14 ) InEndGame = 1;
    else InEndGame = 0;
}

compmove *find_pass( void )
/* Look for the `pass' move in the list of moves. */
{
    compmove *cm;

    for( cm = CHead; cm != NULL && cm->placed != 0; cm = cm->next );
    return( cm );
}

void calculate_equity( log2_t etype )
/* Extract equity given up by a play. */
{
    int eq, pn;
    compmove *cm;

    if( InEndGame == 1 ) return;
    if( ThPl == NULL ) cm = find_pass();
    else cm = ThPl;
    eq = CHead->score - cm->score;
    pn = PlayCurr->pptr.play->num;
    TEq[pn] += eq;
    TTEq[pn] += CHead->score;
    if( etype == LOG2_MOVE || etype == LOG2_EXCHANGE ) {
	TEqN[pn] += eq;
	TTEqN[pn] += CHead->score;
    }
}

void calculate_rack_stuff( log2_t etype )
/* Find rack value and leave value. */
{
    int pn, rs, i;
    compmove *cm;

    if( InEndGame == 1 ) return;
    pn = PlayCurr->pptr.play->num;
    for( rs = 0, i = 0; i < 7; i++ )
	rs += Letters[Let2Ind( PlayCurr->pptr.play->tiles[i] )].points;
    RScore[pn] += rs;
    if( etype == LOG2_MOVE || etype == LOG2_EXCHANGE )
	RScoreN[pn] += rs;
    if( ThPl == NULL ) cm = find_pass();
    else cm = ThPl;
    RLeave[pn] += cm->rms;
    if( etype == LOG2_MOVE || etype == LOG2_EXCHANGE )
	RLeaveN[pn] += cm->rms;
}

void calculate_other_stats( playent *p1 )
/* Find all other statistics. */
{
    int pn, i;
    log2_t etype;

    pn = p1->num;
    etype = p1->etype;
    if( etype == LOG2_MOVE ) {
	PlayC[pn][InEndGame]++;
	TPC[pn] += p1->cmove.placed;
	for( i = 0; i < p1->cmove.placed; i++ ) {
	    TPlay[pn][Let2Ind( p1->cmove.newlet[i].letter )]++;
	    if( p1->cmove.newlet[i].oldlet == CH_DL ) DLSC[pn]++;
	    if( p1->cmove.newlet[i].oldlet == CH_TL ) TLSC[pn]++;
	    if( p1->cmove.newlet[i].oldlet == CH_DW ) DWSC[pn]++;
	    if( p1->cmove.newlet[i].oldlet == CH_TW ) TWSC[pn]++;
	}
    } else if( etype == LOG2_EXCHANGE ) {
	DumpC[pn][InEndGame]++;
	TDC[pn] -= p1->cmove.placed;
	for( i = 0; i < -p1->cmove.placed; i++ )
	    TDrawn[pn][Let2Ind( p1->cmove.newlet[i].letter )]++;
    } else if( etype == LOG2_FORFEIT ) {
	ForfC[pn][InEndGame]++;
    } else if( etype == LOG2_PASS ) {
	PassC[pn][InEndGame]++;
    }
    if( InEndGame == 0 ) FinalS[pn] = p1->newscore;
}

int check_repeated_rack( void )
/* See if a rack is repeated in the play list; if so, don't find the bingos
   in it the second time. */
{
    pent *p1;

    p1 = PlayCurr;
    while( p1->ptype != PTYPE_FIRST ) {
	if( strncmp( PlayCurr->pptr.play->tiles,
		     p1->pptr.play->tiles, 7 ) == 0 && p1 != PlayCurr )
	    return( 1 );
	p1 = p1->prev;
    }
    return( 0 );
}

void add_word( swent **hptr, char *cptr, int sorted )
/* Add a word to a word list. */
{
    swent *p1, *p2, *we;

    we = (swent *) malloc( SWENT_SIZE );
    we->word = (char *) malloc( strlen( cptr ) + 1 );
    strcpy( we->word, cptr );
    we->next = NULL;
    p1 = *hptr;
    if( p1 == NULL ) {
	*hptr = we;
    } else {
	if( sorted ) {
	    for( p2 = NULL; p1 != NULL; p1 = p1->next ) {
		if( strcmp( we->word, p1->word ) > 0 ) p2 = p1;
		else break;
	    }
	    we->next = p1;
	    if( p1 == *hptr ) *hptr = we;
	    else p2->next = we;
	} else {
	    for( ; p1->next != NULL; p1 = p1->next );
	    p1->next = we;
	}
    }
}

void free_word_list( swent **hptr )
/* Free a word list. */
{
    swent *p1;

    while( *hptr != NULL ) {
	p1 = (*hptr)->next;
	free( (char *) (*hptr)->word );
	free( (swent *) *hptr );
	*hptr = p1;
    }
}

swent *add_blank_line( int plr )
/* Add a word that consists only of spaces. */
{
    swent *newptr, *ptr;
    int i;

    newptr = (swent *) malloc( SWENT_SIZE );
    newptr->next = NULL;
    newptr->word = (char *) malloc( DATA_WIDTH + 1 );
    for( i = 0; i < DATA_WIDTH; i++ ) newptr->word[i] = ' ';
    newptr->word[DATA_WIDTH] = '\0';
    ptr = OutHead[plr];
    if( ptr == NULL ) OutHead[plr] = newptr;
    else {
	while( ptr->next != NULL )
	    ptr = ptr->next;
	ptr->next = newptr;
    }
    OutSize[plr] = 0;
    return( newptr );
}

void add_output_word( int plr, char *word )
/* Add a word to the output word list. */
{
    swent *p1;

/* Assumption: OutHead isn't NULL */
    p1 = OutHead[plr];
    while( p1->next != NULL ) p1 = p1->next;
    if( OutSize[plr] + strlen( word ) > DATA_WIDTH )
	p1 = add_blank_line( plr );
    strncpy( p1->word + OutSize[plr], word, strlen( word ) );
    OutSize[plr] += strlen( word ) + 1;
}

void init_output_words( void )
/* Initialize output word structures. */
{
    int i;

    for( i = 0; i < 2; i++ ) add_blank_line( i );
}

void tab( int spc )
/* Tab over by `spc' spaces. */
{
    while( spc-- > 0 ) printf( " " );
}

void left_justify( char *item )
/* Left-justify a word in the item column. */
{
    printf( "%s", item );
    tab( ITEM_WIDTH - strlen( item ) + 1 );
}

void print_item( char *item )
/* Print indented item in the item column. */
{
    printf( "  %s", item );
    tab( ITEM_WIDTH - strlen( item ) - 1 );
}

void center_line( char *word, int plr )
/* Center a line on the output, removing trailing spaces if necessary. */
{
    int i, lsp, changed;

    changed = 0;
    for( lsp = strlen( word ); lsp > 0 && word[lsp - 1] == ' '; lsp-- );
    if( lsp != strlen( word ) ) {
	changed = 1;
	word[lsp] = '\0';
    }
    i = ( DATA_WIDTH - strlen( word ) ) / 2;
    tab( i );
    printf( "%s", word );
    if( plr == 0 ) tab( DATA_WIDTH - i - strlen( word ) + 2 );
    else printf( "\n" );
    if( changed == 1 ) word[lsp] = ' ';
}

void print_output_words( char *item )
/* Show all the words that have been accumulated and free the lists. */
{
    swent *l0, *l1;
    int lc;

    l0 = OutHead[0]; l1 = OutHead[1];
    lc = 0;
    while( l0 != NULL || l1 != NULL ) {
	if( lc == 0 ) print_item( item );
	else tab( ITEM_WIDTH + 1 );
	if( l0 != NULL ) {
	    center_line( l0->word, 0 );
	    l0 = l0->next;
	} else tab( DATA_WIDTH + 1 );
	if( l1 != NULL ) {
	    center_line( l1->word, 1 );
	    l1 = l1->next;
	} else printf( "\n" );
	lc++;
    }
    free_word_list( &(OutHead[0]) );
    free_word_list( &(OutHead[1]) );
}

void find_bingos( playent *plptr )
/* Check for bingos played and unplayed. */
{
    compmove *cm;
    swent *wp;
    int pn;

    pn = plptr->num;
    if( plptr->etype == LOG2_MOVE && plptr->cmove.placed == 7 ) {
	add_word( &SBPHead[pn], plptr->words[plptr->wordsformed - 1], 1 );
    }
    if( !ShowPlayer[pn] ) return;
    for( cm = CHead; cm != NULL; cm = cm->next ) {
	if( cm == ThPl || cm->placed != 7 ) continue;
	ConvertMove( cm );
	MyToUpper( MoveWS );
	for( wp = SBPHead[pn]; wp != NULL; wp = wp->next )
	    if( strcmp( MoveWS, wp->word ) == 0 ) break;
	if( wp != NULL ) continue;
	for( wp = SBUHead[pn]; wp != NULL; wp = wp->next )
	    if( strcmp( MoveWS, wp->word ) == 0 ) break;
	if( wp != NULL ) continue;
	add_word( &SBUHead[pn], MoveWS, 1 );
    }
}

void find_phonies( playent *plptr, log2_t etype )
/* Check for phonies and add them to the appropriate word list. */
{
    int i, j, pc;

    for( pc = 0, i = 0; i < plptr->wordsformed; i++ )
	if( plptr->phony[i] == 1 ) pc++;
    if( pc == 0 ) {
	if( etype == LOG2_FORFEIT ) {
	    printf( "You forfeited this turn although you didn't play a phony.\n" );
	}
	return;
    }
    if( etype == LOG2_MOVE )
	printf( "You got away with playing the phon" );
    else
	printf( "You played %s %s, so you probably forfeited for playing the phon", plptr->movels, plptr->movews );
    if( pc > 1 ) printf( "ies " );
    else printf( "y " );
    for( i = 0, j = 0; i < plptr->wordsformed; i++ ) {
	if( plptr->phony[i] == 1 ) {
	    if( etype == LOG2_MOVE )
		add_word( (&SPGHead[plptr->num]), plptr->words[i], 0 );
	    else
		add_word( (&SPCHead[plptr->num]), plptr->words[i], 0 );
	    j++;
	    if( j == pc ) {
		if( pc > 1 ) printf( "and " );
		printf( "%s.\n", plptr->words[i] );
	    } else {
		printf( "%s", plptr->words[i] );
		if( pc == 2 ) printf( " " );
		else printf( ", " );
	    }
	}
    }
}

void check_scoring( playent *plptr )
/* See if any scoring errors were made. */
{
    int scdiff, absdiff;

    if( plptr->l2turnscore != plptr->turnscore ) {
	printf( "You scored this turn as %d, but it was worth %d.\n",
		plptr->l2turnscore, plptr->turnscore );
    }
    scdiff = plptr->l2newscore - plptr->l2oldscore;
    if( plptr->etype != LOG2_FORFEIT ) scdiff -= plptr->l2turnscore;
    absdiff = ( scdiff < 0 ? -scdiff : scdiff );
    if( scdiff != 0 ) {
	printf( "You added " );
	if( absdiff < 10 ) {
	    printf( "%s point%s", NumNames[absdiff],
		    ( absdiff == 1 ? "" : "s" ) );
	} else {
	    printf( "%d points", absdiff );
	}
	printf( " too %s to your score for this turn.\n",
		( scdiff > 0 ? "many" : "few" ) );
    }
}

char *my_divide( int a, int b, int f2 )
/* Divide two integers and show the result to one decimal place. */
{
    char sgn[2];

    sgn[1] = '\0';
    if( a < 0 ) { a = -a; sgn[0] = '-'; }
    else sgn[0] = '\0';
    if( b == 0 )
	sprintf( Frv, "n/a" );
    else if( f2 == 0 )
	sprintf( Frv, "%s%d.%d", sgn, ( ( a * 100 / b + 5 ) / 10 ) / 10,
		 ( ( a * 100 / b + 5 ) / 10 ) % 10 );
    else
	sprintf( Frv, "%s%d.%d", sgn, ( ( a * 100 / 2 / b + 5 ) / 10 ) / 10,
		 ( ( a * 100 / 2 / b + 5 ) / 10 ) % 10 );
    return( Frv );
}

void summarize_stats( void )
/* Summarize the statistics. */
{
    int i, j, c;
    int turns[2], turnsn[2], turnsnn[2], df[2], xurnsn[2], xurnsnn[2];
    int tlc[2], tleft[2][27];
    pent *p1;
    playent *p2;
    swent *wp, *wp2;
    char wtemp[DATA_WIDTH + 1], itemt[ITEM_WIDTH + 1];

/* Include final racks in drawn tile count */
    for( i = 0; i < 2; i++ ) {
	for( j = 0; j < 27; j++ )
	    tleft[i][j] = 0;
	tlc[i] = 0;
	df[i] = 2;
    }
    for( p1 = PlayHead->next; p1 != NULL; p1 = p1->next ) {
	p2 = p1->pptr.play;
	if( p2->etype == LOG2_TILES_LEFT ) {
	    if( p2->l2turnscore < 0 ) i = p2->num;
	    else i = 1 - p2->num;
	    for( j = 0; j < p2->cmove.placed; j++ )
		tleft[i][Let2Ind( p2->cmove.newlet[j].letter )]++;
	    tlc[i] = j;
	}
    }
    for( i = 0; i < 2; i++ ) {
	turnsnn[i] = PlayC[i][0] + DumpC[i][0];
	turnsn[i] = turnsnn[i] + ForfC[i][0] + PassC[i][0];
	turns[i] = turnsn[i] + PlayC[i][1] + DumpC[i][1]
	    + ForfC[i][1] + PassC[i][1];
    }

    printf( "\n" );
    left_justify( "GAME STATISTICS" );
    printf( "\n" );
    left_justify( "---------------" );
    printf( "\n" );
    left_justify( "" );
    center_line( PlrNm[0], 0 );
    center_line( PlrNm[1], 1 );
    left_justify( "TILES" );
    printf( "\n" );
    init_output_words();
    for( i = 0; i < 2; i++ ) {
	for( j = 0; j < 27; j++ ) {
	    for( c = 0; c < TPlay[i][j]; c++ )
		wtemp[c] = Ind2Let( j );
	    wtemp[c] = '\0';
	    if( c != 0 ) add_output_word( i, wtemp );
	}
	if( TPC[i] == 0 ) strcpy( wtemp, "none" );
	else sprintf( wtemp, "(%d)", TPC[i] );
	add_output_word( i, wtemp );
    }
    print_output_words( "Played" );
    init_output_words();
    for( i = 0; i < 2; i++ ) {
	for( j = 0; j < 28; j++ ) {
	    for( c = 0; c < TDrawn[i][j]; c++ )
		wtemp[c] = Ind2Let( j );
	    wtemp[c] = '\0';
	    if( c != 0 ) add_output_word( i, wtemp );
	}
	if( TDC[i] == 0 ) strcpy( wtemp, "none" );
	else sprintf( wtemp, "(%d)", TDC[i] );
	add_output_word( i, wtemp );
    }
    print_output_words( "Returned to bag" );
    init_output_words();
    for( i = 0; i < 2; i++ ) {
	for( j = 0; j < 27; j++ ) {
	    for( c = 0; c < tleft[i][j]; c++ )
		wtemp[c] = Ind2Let( j );
	    wtemp[c] = '\0';
	    if( c != 0 ) add_output_word( i, wtemp );
	}
	if( tlc[i] == 0 ) strcpy( wtemp, "none" );
	else sprintf( wtemp, "(%d)", tlc[i] );
	add_output_word( i, wtemp );
    }
    print_output_words( "Left on rack" );
    printf( "\n" );
    for( c = 0; c < 4; c++ ) {
	init_output_words();
	if( c == 0 ) {
	    left_justify( "BINGOS" ); printf( "\n" );
	} else if( c == 2 ) {
	    printf( "\n" ); left_justify( "PHONIES" ); printf( "\n" );
	}
	for( i = 0; i < 2; i++ ) {
	    if( c == 0 ) {
		wp = SBPHead[i]; strcpy( itemt, "Played" );
	    } else if( c == 1 ) {
		wp = SBUHead[i]; strcpy( itemt, "Unplayed" );
	    } else if( c == 2 ) {
		wp = SPCHead[i]; strcpy( itemt, "Failed" );
	    } else {
		wp = SPGHead[i]; strcpy( itemt, "Successful" );
	    }
	    for( j = 0, wp2 = wp; wp2 != NULL; j++, wp2 = wp2->next );
	    if( c == 1 && !ShowPlayer[i] ) add_output_word( i, "n/a" );
	    else if( j == 0 ) add_output_word( i, "none" );
	    else {
		for( wp2 = wp; wp2 != NULL; wp2 = wp2->next ) {
		    sprintf( wtemp, "%s", wp2->word );
		    add_output_word( i, wtemp );
		}
		sprintf( wtemp, "(%d)", j );
		add_output_word( i, wtemp );
	    }
	    free_word_list( &wp );
	}
	print_output_words( itemt );
    }

    printf( "\n" );
    left_justify( "PLAYS" );
    printf( "\n" );
    print_item( "Special" );
    sprintf( wtemp, "%d exch %d pass %d forf", DumpC[0][0] + DumpC[0][1],
	    PassC[0][0] + PassC[0][1], ForfC[0][0] + ForfC[0][1] );
    center_line( wtemp, 0 );
    sprintf( wtemp,"%d exch %d pass %d forf", DumpC[1][0] + DumpC[1][1],
	    PassC[1][0] + PassC[1][1], ForfC[1][0] + ForfC[1][1] );
    center_line( wtemp, 1 );
    print_item( "Total" );
    sprintf( wtemp, "%d", turns[0] ); center_line( wtemp, 0 );
    sprintf( wtemp, "%d", turns[1] ); center_line( wtemp, 1 );
    printf( "\n" );
    left_justify( "PREMIUM SQUARES" );
    printf( "\n" );
    print_item( "Letter" );
    sprintf( wtemp, "%d double %d triple", DLSC[0], TLSC[0] );
    center_line( wtemp, 0 );
    sprintf( wtemp, "%d double %d triple", DLSC[1], TLSC[1] );
    center_line( wtemp, 1 );
    print_item( "Word" );
    sprintf( wtemp, "%d double %d triple", DWSC[0], TWSC[0] );
    center_line( wtemp, 0 );
    sprintf( wtemp, "%d double %d triple", DWSC[1], TWSC[1] );
    center_line( wtemp, 1 );
    printf( "\n" );
/* Kludge so `n/a' gets printed for an unseen player */
    for( i = 0; i < 2; i++ ) {
	xurnsn[i] = turnsn[i]; xurnsnn[i] = turnsnn[i];
	if( !ShowPlayer[i] ) {
	    turnsn[i] = 0; turnsnn[i] = 0; df[i] = 0;
	}
    }
    left_justify( "RACKS" );
    center_line( "all non-    exc. pass", 0 );
    center_line( "all non-    exc. pass", 1 );
    left_justify( "" );
    center_line( " endgame    and forfeit", 0 );
    center_line( " endgame    and forfeit", 1 );
    print_item( "Mean rack value" );
    tab(  4 ); my_divide(  RScore[0],  turnsn[0], 0 ); printf( "%6s", Frv );
    tab(  7 ); my_divide( RScoreN[0], turnsnn[0], 0 ); printf( "%6s", Frv );
    tab( 12 ); my_divide(  RScore[1],  turnsn[1], 0 ); printf( "%6s", Frv );
    tab(  7 ); my_divide( RScoreN[1], turnsnn[1], 0 ); printf( "%6s", Frv );
    printf( "\n" );
    print_item( "Mean rack leave" );
    tab(  4 ); my_divide(  RLeave[0],  turnsn[0], 1 ); printf( "%6s", Frv );
    tab(  7 ); my_divide( RLeaveN[0], turnsnn[0], 1 ); printf( "%6s", Frv );
    tab( 12 ); my_divide(  RLeave[1],  turnsn[1], 1 ); printf( "%6s", Frv );
    tab(  7 ); my_divide( RLeaveN[1], turnsnn[1], 1 ); printf( "%6s", Frv );
    printf( "\n\n" );
    left_justify( "EQUITY" );
    center_line( "all non-    exc. pass", 0 );
    center_line( "all non-    exc. pass", 1 );
    left_justify( "" );
    center_line( " endgame    and forfeit", 0 );
    center_line( " endgame    and forfeit", 1 );
    print_item( "Mean turn score" );
    tab(  4 ); my_divide( FinalS[0],  xurnsn[0], 0 ); printf( "%6s", Frv );
    tab(  7 ); my_divide( FinalS[0], xurnsnn[0], 0 ); printf( "%6s", Frv );
    tab( 12 ); my_divide( FinalS[1],  xurnsn[1], 0 ); printf( "%6s", Frv );
    tab(  7 ); my_divide( FinalS[1], xurnsnn[1], 0 ); printf( "%6s", Frv );
    printf( "\n" );
    print_item( "Mean turn loss" );
    tab(  4 ); my_divide(    TEq[0],  turnsn[0], 1 ); printf( "%6s", Frv );
    tab(  7 ); my_divide(   TEqN[0], turnsnn[0], 1 ); printf( "%6s", Frv );
    tab( 12 ); my_divide(    TEq[1],  turnsn[1], 1 ); printf( "%6s", Frv );
    tab(  7 ); my_divide(   TEqN[1], turnsnn[1], 1 ); printf( "%6s", Frv );
    printf( "\n" );
    print_item( "Total loss" );
    tab(  4 ); my_divide(    TEq[0],      df[0], 0 ); printf( "%6s", Frv );
    tab(  7 ); my_divide(   TEqN[0],      df[0], 0 ); printf( "%6s", Frv );
    tab( 12 ); my_divide(    TEq[1],      df[1], 0 ); printf( "%6s", Frv );
    tab(  7 ); my_divide(   TEqN[1],      df[1], 0 ); printf( "%6s", Frv );
    printf( "\n" );
    print_item( "Efficiency" );
    tab(  4 ); my_divide( ( TTEq[0] - TEq[0] ) * 100, TTEq[0], 0 );
    printf( "%6s%c", Frv, ( Frv[0] == 'n' ? ' ' : '%' ) );
    tab(  6 ); my_divide( ( TTEqN[0] - TEqN[0] ) * 100, TTEqN[0], 0 );
    printf( "%6s%c", Frv, ( Frv[0] == 'n' ? ' ' : '%' ) );
    tab( 11 ); my_divide( ( TTEq[1] - TEq[1] ) * 100, TTEq[1], 0 );
    printf( "%6s%c", Frv, ( Frv[0] == 'n' ? ' ' : '%' ) );
    tab(  6 ); my_divide( ( TTEqN[1] - TEqN[1] ) * 100, TTEqN[1], 0 );
    printf( "%6s%c", Frv, ( Frv[0] == 'n' ? ' ' : '%' ) );
    printf( "\n\n" );
    left_justify( "FINAL SCORE" );
    sprintf( wtemp, "%d", PlrScores[0] );
    center_line( wtemp, 0 );
    sprintf( wtemp, "%d", PlrScores[1] );
    center_line( wtemp, 1 );
}

void init_batch( void )
/* Initialize for batch mode. */
{
    int i, j;

    for( i = 0; i < 2; i++ ) {
	for( j = 0; j < 27; j++ ) { TPlay[i][j] = 0; TDrawn[i][j] = 0; }
	for( j = 0; j < 2; j++ ) {
	    PlayC[i][j] = 0; DumpC[i][j] = 0;
	    ForfC[i][j] = 0; PassC[i][j] = 0;
	}
	TPC[i] = 0; TDC[i] = 0;
	DLSC[i] = 0; TLSC[i] = 0; DWSC[i] = 0; TWSC[i] = 0;
	RScore[i] = 0; RScoreN[i] = 0;
	RLeave[i] = 0; RLeaveN[i] = 0;
	TEq[i] = 0; TEqN[i] = 0;
	SBPHead[i] = NULL;
	SBUHead[i] = NULL;
	SPCHead[i] = NULL;
	SPGHead[i] = NULL;
    }
    InEndGame = 0;
}

void RunBatch( void )
/* Execute batch mode. */
{
    int tc[2];
    log2_t et;
    int pn;

    init_batch();
    tc[0] = 0; tc[1] = 0;
    while( PlayCurr != NULL ) {
	check_for_endgame();
	et = PlayCurr->pptr.play->etype;
	pn = PlayCurr->pptr.play->num;
	if( et == LOG2_MOVE || et == LOG2_PASS
	    || et == LOG2_FORFEIT || et == LOG2_EXCHANGE ) {
	    printf( "%s: Turn %d\n", PlayCurr->pptr.play->name, ++tc[pn] );
	    if( ShowPlayer[pn] ) {
		ShowMoveList();
	    } else {
		if( et == LOG2_MOVE )
		    printf( "You played %s %s this turn.\n",
			    PlayCurr->pptr.play->movews,
			    PlayCurr->pptr.play->movels );
		else if( et == LOG2_PASS )
		    printf( "You passed this turn.\n" );
		else if( et == LOG2_EXCHANGE )
		    printf( "You exchanged %s this turn.\n",
			    PlayCurr->pptr.play->movews );
		else if( et == LOG2_FORFEIT )
		    printf( "You forfeited this turn.\n" );
	    }
	    if( ShowPlayer[pn] && check_repeated_rack() == 0 ) {
		sprintf( Grid[0], "bi\n" );
		CommandBi( 2 );
	    }
	    if( et == LOG2_FORFEIT || et == LOG2_MOVE )
		find_phonies( PlayCurr->pptr.play, et );
	    check_scoring( PlayCurr->pptr.play );
	    find_bingos( PlayCurr->pptr.play );
	    printf( "\n" );
	    if( ShowPlayer[pn] ) calculate_equity( et );
	    if( ShowPlayer[pn] ) calculate_rack_stuff( et );
	    calculate_other_stats( PlayCurr->pptr.play );
	} else if( et == LOG2_TILES_LEFT ) {
	    if( PlayCurr->pptr.play->turnscore > 0 ) {
		printf( "%s got %d points for tiles left on %s's rack.\n",
			PlayCurr->pptr.play->name,
			PlayCurr->pptr.play->turnscore,
			PlrNm[1 - PlayCurr->pptr.play->num] );
	    } else {
		printf( "%s lost %d points for tiles left on their rack.\n",
			PlayCurr->pptr.play->name,
			-PlayCurr->pptr.play->turnscore );
	    }
	} else if( et == LOG2_TIME_PENALTY ) {
	    printf( "%s lost %d points for a time penalty.\n",
		    PlayCurr->pptr.play->name,
		    -PlayCurr->pptr.play->l2turnscore );
	}
	FreeMoveList();
	WalkThruPlayList( PlayCurr, 0 );
	PlayCurr = PlayCurr->next;
    }
    summarize_stats();
/* Note: batch mode is assumed to do a single log2 then exit.  If ever this
   is changed, must change argument in next line from 0 to 1. */
    ResetPlayList( 0 );
}

