/*	$NetBSD: scores.c,v 1.22 2021/05/02 12:50:45 rillig Exp $	*/

/*
 * scores.c			 Larn is copyrighted 1986 by Noah Morgan.
 *
 * Functions in this file are:
 *
 * readboard() 	Function to read in the scoreboard into a static buffer
 * writeboard()	Function to write the scoreboard from readboard()'s buffer
 * makeboard() 	Function to create a new scoreboard (wipe out old one)
 * hashewon()	 Function to return 1 if player has won a game before, else 0
 * long paytaxes(x)	 Function to pay taxes if any are due winshou()
 * ubroutine to print out the winning scoreboard shou(x)
 * ubroutine to print out the non-winners scoreboard showscores()
 * unction to show the scoreboard on the terminal showallscores()
 * Function to show scores and the iven lists that go with them sortboard()
 * unction to sort the scoreboard newscore(score, whoo, whyded, winner)
 * Function to add entry to scoreboard new1sub(score,i,whoo,taxes)
 * Subroutine to put player into a new2sub(score,i,whoo,whyded)
 * Subroutine to put player into a died(x) 	Subroutine to record who
 * played larn, and what the score was diedsub(x) Subroutine to print out a
 * line showing player when he is killed diedlog() 	Subroutine to read a
 * log file and print it out in ascii format getplid(name)
 * on to get players id # from id file
 *
 */
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: scores.c,v 1.22 2021/05/02 12:50:45 rillig Exp $");
#endif				/* not lint */
#include <sys/types.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "header.h"
#include "extern.h"

struct scofmt {			/* This is the structure for the scoreboard 		 */
	long            score;	/* the score of the player 							 */
	long            suid;	/* the user id number of the player 				 */
	short           what;	/* the number of the monster that killed
				 * player 	 */
	short           level;	/* the level player was on when he died 			 */
	short           hardlev;/* the level of difficulty player played at 		 */
	short           order;	/* the relative ordering place of this entry 		 */
	char            who[40];/* the name of the character 						 */
	char            sciv[26][2];	/* this is the inventory list of the
					 * character 		 */
};
struct wscofmt {		/* This is the structure for the winning
				 * scoreboard */
	long            score;	/* the score of the player 							 */
	long            timeused;	/* the time used in mobuls to win the
					 * game 			 */
	long            taxes;	/* taxes he owes to LRS 							 */
	long            suid;	/* the user id number of the player 				 */
	short           hardlev;/* the level of difficulty player played at 		 */
	short           order;	/* the relative ordering place of this entry 		 */
	char            who[40];/* the name of the character 						 */
};

struct log_fmt {		/* 102 bytes struct for the log file 				 */
	long            score;	/* the players score 								 */
	int32_t          diedtime;	/* time when game was over 							 */
	short           cavelev;/* level in caves 									 */
	short           diff;	/* difficulty player played at 						 */
#ifdef EXTRA
	long            elapsedtime;	/* real time of game in seconds 					 */
	long            bytout;	/* bytes input and output 							 */
	long            bytin;
	long            moves;	/* number of moves made by player 					 */
	short           ac;	/* armor class of player 							 */
	short           hp, hpmax;	/* players hitpoints 								 */
	short           cputime;/* CPU time needed in seconds 						 */
	short           killed, spused;	/* monsters killed and spells cast 					 */
	short           usage;	/* usage of the CPU in % 							 */
	short           lev;	/* player level 									 */
#endif
	char            who[12];/* player name 										 */
	char            what[46];	/* what happened to player 							 */
};

static struct scofmt sco[SCORESIZE];	/* the structure for the scoreboard  */
static struct wscofmt winr[SCORESIZE];	/* struct for the winning scoreboard */
static struct log_fmt logg;	/* structure for the log file 		 */
static const char *whydead[] = {
	"quit", "suspended", "self - annihilated", "shot by an arrow",
	"hit by a dart", "fell into a pit", "fell into a bottomless pit",
	"a winner", "trapped in solid rock", "killed by a missing save file",
	"killed by an old save file", "caught by the greedy cheater checker trap",
	"killed by a protected save file", "killed his family and committed suicide",
	"erased by a wayward finger", "fell through a bottomless trap door",
	"fell through a trap door", "drank some poisonous water",
	"fried by an electric shock", "slipped on a volcano shaft",
	"killed by a stupid act of frustration", "attacked by a revolting demon",
	"hit by his own magic", "demolished by an unseen attacker",
	"fell into the dreadful sleep", "killed by an exploding chest",
	 /* 26 */ "killed by a missing maze data file", "annihilated in a sphere",
	"died a post mortem death", "wasted by a malloc() failure"
};

static int readboard(void);
static int writeboard(void);
static int winshou(void);
static int shou(int);
static int sortboard(void);
static void newscore(long, char *, int, int);
static void new1sub(long, int, char *, long);
static void new2sub(long, int, char *, int);
static void diedsub(int);

/*
 * readboard() 	Function to read in the scoreboard into a static buffer
 *
 * returns -1 if unable to read in the scoreboard, returns 0 if all is OK
 */
static int
readboard(void)
{
	int             i;

	if (gid != egid)
		setegid(egid);
	i = lopen(scorefile);
	if (gid != egid)
		setegid(gid);
	if (i < 0) {
		lprcat("Can't read scoreboard\n");
		lflush();
		return (-1);
	}
	lrfill((char *) sco, sizeof(sco));
	lrfill((char *) winr, sizeof(winr));
	lrclose();
	lcreat((char *) 0);
	return (0);
}

/*
 * writeboard()	Function to write the scoreboard from readboard()'s buffer
 *
 * returns -1 if unable to write the scoreboard, returns 0 if all is OK
 */
static int
writeboard(void)
{
	int             i;

	set_score_output();
	if (gid != egid)
		setegid(egid);
	i = lcreat(scorefile);
	if (gid != egid)
		setegid(gid);
	if (i < 0) {
		lprcat("Can't write scoreboard\n");
		lflush();
		return (-1);
	}
	lwrite((char *) sco, sizeof(sco));
	lwrite((char *) winr, sizeof(winr));
	lwclose();
	lcreat((char *) 0);
	return (0);
}

/*
 * makeboard() 		Function to create a new scoreboard (wipe out old one)
 *
 * returns -1 if unable to write the scoreboard, returns 0 if all is OK
 */
int
makeboard(void)
{
	int    i;
	set_score_output();
	for (i = 0; i < SCORESIZE; i++) {
		winr[i].taxes = winr[i].score = sco[i].score = 0;
		winr[i].order = sco[i].order = i;
	}
	if (writeboard())
		return (-1);
	if (gid != egid)
		setegid(egid);
	chmod(scorefile, 0660);
	if (gid != egid)
		setegid(gid);
	return (0);
}

/*
 * hashewon()	 Function to return 1 if player has won a game before, else 0
 *
 * This function also sets c[HARDGAME] to appropriate value -- 0 if not a
 * winner, otherwise the next level of difficulty listed in the winners
 * scoreboard.  This function also sets outstanding_taxes to the value in
 * the winners scoreboard.
 */
int
hashewon(void)
{
	int    i;
	c[HARDGAME] = 0;
	if (readboard() < 0)
		return (0);	/* can't find scoreboard */
	for (i = 0; i < SCORESIZE; i++)	/* search through winners scoreboard */
		if (winr[i].suid == userid)
			if (winr[i].score > 0) {
				c[HARDGAME] = winr[i].hardlev + 1;
				outstanding_taxes = winr[i].taxes;
				return (1);
			}
	return (0);
}

/*
 * long paytaxes(x)		 Function to pay taxes if any are due
 *
 * Enter with the amount (in gp) to pay on the taxes.
 * Returns amount actually paid.
 */
long
paytaxes(long x)
{
	int    i;
	long   amt;
	if (x < 0)
		return (0L);
	if (readboard() < 0)
		return (0L);
	for (i = 0; i < SCORESIZE; i++)
		if (winr[i].suid == userid)	/* look for players winning
						 * entry */
			if (winr[i].score > 0) {	/* search for a winning
							 * entry for the player */
				amt = winr[i].taxes;
				if (x < amt)
					amt = x;	/* don't overpay taxes
							 * (Ughhhhh) */
				winr[i].taxes -= amt;
				outstanding_taxes -= amt;
				set_score_output();
				if (writeboard() < 0)
					return (0);
				return (amt);
			}
	return (0L);		/* couldn't find user on winning scoreboard */
}

/*
 * winshou()		Subroutine to print out the winning scoreboard
 *
 * Returns the number of players on scoreboard that were shown
 */
static int
winshou(void)
{
	struct wscofmt *p;
	int    i, j, count;
	for (count = j = i = 0; i < SCORESIZE; i++)	/* is there anyone on
							 * the scoreboard? */
		if (winr[i].score != 0) {
			j++;
			break;
		}
	if (j) {
		lprcat("\n  Score    Difficulty   Time Needed   Larn Winners List\n");

		for (i = 0; i < SCORESIZE; i++)	/* this loop is needed to
						 * print out the */
			for (j = 0; j < SCORESIZE; j++) {	/* winners in order */
				p = &winr[j];	/* pointer to the scoreboard
						 * entry */
				if (p->order == i) {
					if (p->score) {
						count++;
						lprintf("%10ld     %2ld      %5ld Mobuls   %s \n",
							(long) p->score, (long) p->hardlev, (long) p->timeused, p->who);
					}
					break;
				}
			}
	}
	return (count);		/* return number of people on scoreboard */
}

/*
 * shou(x)			Subroutine to print out the non-winners scoreboard
 * 	int x;
 *
 * Enter with 0 to list the scores, enter with 1 to list inventories too
 * Returns the number of players on scoreboard that were shown
 */
static int
shou(int x)
{
	int    i, j, n, k;
	int             count;
	for (count = j = i = 0; i < SCORESIZE; i++)	/* is the scoreboard
							 * empty? */
		if (sco[i].score != 0) {
			j++;
			break;
		}
	if (j) {
		lprcat("\n   Score   Difficulty   Larn Visitor Log\n");
		for (i = 0; i < SCORESIZE; i++)	/* be sure to print them out
						 * in order */
			for (j = 0; j < SCORESIZE; j++)
				if (sco[j].order == i) {
					if (sco[j].score) {
						count++;
						lprintf("%10ld     %2ld       %s ",
							(long) sco[j].score, (long) sco[j].hardlev, sco[j].who);
						if (sco[j].what < 256)
							lprintf("killed by a %s", monster[sco[j].what].name);
						else
							lprintf("%s", whydead[sco[j].what - 256]);
						if (x != 263)
							lprintf(" on %s", levelname[sco[j].level]);
						if (x) {
							for (n = 0; n < 26; n++) {
								iven[n] = sco[j].sciv[n][0];
								ivenarg[n] = sco[j].sciv[n][1];
							}
							for (k = 1; k < 99; k++)
								for (n = 0; n < 26; n++)
									if (k == iven[n]) {
										srcount = 0;
										show3(n);
									}
							lprcat("\n\n");
						} else
							lprc('\n');
					}
					j = SCORESIZE;
				}
	}
	return (count);		/* return the number of players just shown */
}

/*
 * showscores()		Function to show the scoreboard on the terminal
 *
 * Returns nothing of value
 */
static char     esb[] = "The scoreboard is empty.\n";
void
showscores(void)
{
	int    i, j;
	lflush();
	lcreat((char *) 0);
	if (readboard() < 0)
		return;
	i = winshou();
	j = shou(0);
	if (i + j == 0)
		lprcat(esb);
	else
		lprc('\n');
	lflush();
}

/*
 * showallscores()	Function to show scores and the iven lists that go with them
 *
 * Returns nothing of value
 */
void
showallscores(void)
{
	int    i, j;
	lflush();
	lcreat((char *) 0);
	if (readboard() < 0)
		return;
	c[WEAR] = c[WIELD] = c[SHIELD] = -1;	/* not wielding or wearing
						 * anything */
	for (i = 0; i < MAXPOTION; i++)
		potionname[i] = potionhide[i];
	for (i = 0; i < MAXSCROLL; i++)
		scrollname[i] = scrollhide[i];
	i = winshou();
	j = shou(1);
	if (i + j == 0)
		lprcat(esb);
	else
		lprc('\n');
	lflush();
}

/*
 * sortboard()		Function to sort the scoreboard
 *
 * Returns 0 if no sorting done, else returns 1
 */
static int
sortboard(void)
{
	int    i, j = 0, pos;
	long            jdat;
	for (i = 0; i < SCORESIZE; i++)
		sco[i].order = winr[i].order = -1;
	pos = 0;
	while (pos < SCORESIZE) {
		jdat = 0;
		for (i = 0; i < SCORESIZE; i++)
			if ((sco[i].order < 0) && (sco[i].score >= jdat)) {
				j = i;
				jdat = sco[i].score;
			}
		sco[j].order = pos++;
	}
	pos = 0;
	while (pos < SCORESIZE) {
		jdat = 0;
		for (i = 0; i < SCORESIZE; i++)
			if ((winr[i].order < 0) && (winr[i].score >= jdat)) {
				j = i;
				jdat = winr[i].score;
			}
		winr[j].order = pos++;
	}
	return (1);
}

/*
 * newscore(score, whoo, whyded, winner) 	Function to add entry to scoreboard
 * 	int score, winner, whyded;
 * 	char *whoo;
 *
 * Enter with the total score in gp in score,  players name in whoo,
 * 	died() reason # in whyded, and TRUE/FALSE in winner if a winner
 * ex.		newscore(1000, "player 1", 32, 0);
 */
static void
newscore(long score, char *whoo, int whyded, int winner)
{
	int    i;
	long            taxes;
	if (readboard() < 0)
		return;		/* do the scoreboard	 */
	/* if a winner then delete all non-winning scores */
	if (cheat)
		winner = 0;	/* if he cheated, don't let him win */
	if (winner) {
		for (i = 0; i < SCORESIZE; i++)
			if (sco[i].suid == userid)
				sco[i].score = 0;
		taxes = score * TAXRATE;
		score += 100000 * c[HARDGAME];	/* bonus for winning */
		/*
		 * if he has a slot on the winning scoreboard update it if
		 * greater score
		 */
		for (i = 0; i < SCORESIZE; i++)
			if (winr[i].suid == userid) {
				new1sub(score, i, whoo, taxes);
				return;
			}
		/*
		 * he had no entry. look for last entry and see if he has a
		 * greater score
		 */
		for (i = 0; i < SCORESIZE; i++)
			if (winr[i].order == SCORESIZE - 1) {
				new1sub(score, i, whoo, taxes);
				return;
			}
	} else if (!cheat) {	/* for not winning scoreboard */
		/*
		 * if he has a slot on the scoreboard update it if greater
		 * score
		 */
		for (i = 0; i < SCORESIZE; i++)
			if (sco[i].suid == userid) {
				new2sub(score, i, whoo, whyded);
				return;
			}
		/*
		 * he had no entry. look for last entry and see if he has a
		 * greater score
		 */
		for (i = 0; i < SCORESIZE; i++)
			if (sco[i].order == SCORESIZE - 1) {
				new2sub(score, i, whoo, whyded);
				return;
			}
	}
}

/*
 * new1sub(score,i,whoo,taxes) 	  Subroutine to put player into a
 * 	int score,i,whyded,taxes;		  winning scoreboard entry if his score
 * 	char *whoo; 					  is high enough
 *
 * Enter with the total score in gp in score,  players name in whoo,
 * 	died() reason # in whyded, and TRUE/FALSE in winner if a winner
 * 	slot in scoreboard in i, and the tax bill in taxes.
 * Returns nothing of value
 */
static void
new1sub(long score, int i, char *whoo, long taxes)
{
	struct wscofmt *p;
	p = &winr[i];
	p->taxes += taxes;
	if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) {
		strcpy(p->who, whoo);
		p->score = score;
		p->hardlev = c[HARDGAME];
		p->suid = userid;
		p->timeused = gltime / 100;
	}
}

/*
 * new2sub(score,i,whoo,whyded)	 	  Subroutine to put player into a
 * 	int score,i,whyded,taxes;		  non-winning scoreboard entry if his
 * 	char *whoo; 					  score is high enough
 *
 * Enter with the total score in gp in score,  players name in whoo,
 * 	died() reason # in whyded, and slot in scoreboard in i.
 * Returns nothing of value
 */
static void
new2sub(long score, int i, char *whoo, int whyded)
{
	int    j;
	struct scofmt *p;
	p = &sco[i];
	if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) {
		strcpy(p->who, whoo);
		p->score = score;
		p->what = whyded;
		p->hardlev = c[HARDGAME];
		p->suid = userid;
		p->level = level;
		for (j = 0; j < 26; j++) {
			p->sciv[j][0] = iven[j];
			p->sciv[j][1] = ivenarg[j];
		}
	}
}

/*
 * died(x) 	Subroutine to record who played larn, and what the score was
 * 	int x;
 *
 * if x < 0 then don't show scores
 * died() never returns! (unless c[LIFEPROT] and a reincarnatable death!)
 *
 * 	< 256	killed by the monster number
 * 	256		quit
 * 	257		suspended
 * 	258		self - annihilated
 * 	259		shot by an arrow
 * 	260		hit by a dart
 * 	261		fell into a pit
 * 	262		fell into a bottomless pit
 * 	263		a winner
 * 	264		trapped in solid rock
 * 	265		killed by a missing save file
 * 	266		killed by an old save file
 * 	267		caught by the greedy cheater checker trap
 * 	268		killed by a protected save file
 * 	269		killed his family and killed himself
 * 	270		erased by a wayward finger
 * 	271		fell through a bottomless trap door
 * 	272		fell through a trap door
 * 	273		drank some poisonous water
 * 	274		fried by an electric shock
 * 	275		slipped on a volcano shaft
 * 	276		killed by a stupid act of frustration
 * 	277		attacked by a revolting demon
 * 	278		hit by his own magic
 * 	279		demolished by an unseen attacker
 * 	280		fell into the dreadful sleep
 * 	281		killed by an exploding chest
 * 	282		killed by a missing maze data file
 * 	283		killed by a sphere of annihilation
 * 	284		died a post mortem death
 * 	285		malloc() failure
 * 	300		quick quit -- don't put on scoreboard
 */

static int      scorerror;
void
died(int x)
{
	int    f, win;
	char            ch;
	const char     *mod;
	time_t          zzz;
	if (c[LIFEPROT] > 0) {	/* if life protection */
		switch ((x > 0) ? x : -x) {
		case 256:
		case 257:
		case 262:
		case 263:
		case 265:
		case 266:
		case 267:
		case 268:
		case 269:
		case 271:
		case 282:
		case 284:
		case 285:
		case 300:
			goto invalid;	/* can't be saved */
		};
		--c[LIFEPROT];
		c[HP] = 1;
		--c[CONSTITUTION];
		cursors();
		lprcat("\nYou feel wiiieeeeerrrrrd all over! ");
		beep();
		lflush();
		sleep(4);
		return;		/* only case where died() returns */
	}
invalid:
	clearvt100();
	lflush();
	f = 0;
	if (ckpflag)
		unlink(ckpfile);/* remove checkpoint file if used */
	if (x < 0) {
		f++;
		x = -x;
	}			/* if we are not to display the scores */
	if ((x == 300) || (x == 257))
		exit(0);		/* for quick exit or saved game */
	if (x == 263)
		win = 1;
	else
		win = 0;
	c[GOLD] += c[BANKACCOUNT];
	c[BANKACCOUNT] = 0;
	/* now enter the player at the end of the scoreboard */
	newscore(c[GOLD], logname, x, win);
	diedsub(x);		/* print out the score line */
	lflush();

	set_score_output();
	if ((wizard == 0) && (c[GOLD] > 0)) {	/* wizards can't score		 */
#ifndef NOLOG
		if (gid != egid)
			setegid(egid);
		if (lappend(logfile) < 0) {	/* append to file */
			if (lcreat(logfile) < 0) {	/* and can't create new
							 * log file */
				lcreat((char *) 0);
				lprcat("\nCan't open record file:  I can't post your score.\n");
				sncbr();
				resetscroll();
				lflush();
				exit(0);
			}
			if (gid != egid)
				setegid(egid);
			chmod(logfile, 0660);
			if (gid != egid)
				setegid(gid);
		}
		if (gid != egid)
			setegid(gid);
		strcpy(logg.who, loginname);
		logg.score = c[GOLD];
		logg.diff = c[HARDGAME];
		if (x < 256) {
			ch = *monster[x].name;
			if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
				mod = "an";
			else
				mod = "a";
			snprintf(logg.what, sizeof(logg.what),
			    "killed by %s %s", mod, monster[x].name);
		} else
			snprintf(logg.what, sizeof(logg.what),
			    "%s", whydead[x - 256]);
		logg.cavelev = level;
		time(&zzz);	/* get CPU time -- write out score info */
		logg.diedtime = zzz;
#ifdef EXTRA
		times(&cputime);/* get CPU time -- write out score info */
		logg.cputime = i = (cputime.tms_utime + cputime.tms_stime) / 60 + c[CPUTIME];
		logg.lev = c[LEVEL];
		logg.ac = c[AC];
		logg.hpmax = c[HPMAX];
		logg.hp = c[HP];
		logg.elapsedtime = (zzz - initialtime + 59) / 60;
		logg.usage = (10000 * i) / (zzz - initialtime);
		logg.bytin = c[BYTESIN];
		logg.bytout = c[BYTESOUT];
		logg.moves = c[MOVESMADE];
		logg.spused = c[SPELLSCAST];
		logg.killed = c[MONSTKILLED];
#endif
		lwrite((char *) &logg, sizeof(struct log_fmt));
		lwclose();
#endif	/* NOLOG */

		/*
		 * now for the scoreboard maintenance -- not for a suspended
		 * game
		 */
		if (x != 257) {
			if (sortboard()) {
				set_score_output();
				scorerror = writeboard();
			}
		}
	}
	if ((x == 256) || (x == 257) || (f != 0))
		exit(0);
	if (scorerror == 0)
		showscores();	/* if we updated the scoreboard */
	if (x == 263)
		mailbill();
	exit(0);
}

/*
 * diedsub(x) Subroutine to print out the line showing the player when he is killed
 * 	int x;
 */
static void
diedsub(int x)
{
	char   ch;
	const char *mod;

	lprintf("Score: %ld, Diff: %ld,  %s ", (long) c[GOLD], (long) c[HARDGAME], logname);
	if (x < 256) {
		ch = *monster[x].name;
		if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
			mod = "an";
		else
			mod = "a";
		lprintf("killed by %s %s", mod, monster[x].name);
	} else
		lprintf("%s", whydead[x - 256]);
	if (x != 263)
		lprintf(" on %s\n", levelname[level]);
	else
		lprc('\n');
}

/*
 * diedlog() 	Subroutine to read a log file and print it out in ascii format
 */
void
diedlog(void)
{
	int    n;
	char  *p;
	static char  q[] = "?";
	struct stat     stbuf;
	time_t t;

	lcreat((char *) 0);
	if (lopen(logfile) < 0) {
		lprintf("Can't locate log file <%s>\n", logfile);
		return;
	}
	if (fstat(io_infd, &stbuf) < 0) {
		lprintf("Can't  stat log file <%s>\n", logfile);
		return;
	}
	for (n = stbuf.st_size / sizeof(struct log_fmt); n > 0; --n) {
		lrfill((char *) &logg, sizeof(struct log_fmt));
		t = logg.diedtime;
		if ((p = ctime(&t)) == NULL)
			p = q;
		else {
			p[16] = '\n';
			p[17] = 0;
		}
		lprintf("Score: %ld, Diff: %ld,  %s %s on %ld at %s", (long) (logg.score), (long) (logg.diff), logg.who, logg.what, (long) (logg.cavelev), p + 4);
#ifdef EXTRA
		if (logg.moves <= 0)
			logg.moves = 1;
		lprintf("  Experience Level: %ld,  AC: %ld,  HP: %ld/%ld,  Elapsed Time: %ld minutes\n", (long) (logg.lev), (long) (logg.ac), (long) (logg.hp), (long) (logg.hpmax), (long) (logg.elapsedtime));
		lprintf("  CPU time used: %ld seconds,  Machine usage: %ld.%02ld%%\n", (long) (logg.cputime), (long) (logg.usage / 100), (long) (logg.usage % 100));
		lprintf("  BYTES in: %ld, out: %ld, moves: %ld, deaths: %ld, spells cast: %ld\n", (long) (logg.bytin), (long) (logg.bytout), (long) (logg.moves), (long) (logg.killed), (long) (logg.spused));
		lprintf("  out bytes per move: %ld,  time per move: %ld ms\n", (long) (logg.bytout / logg.moves), (long) ((logg.cputime * 1000) / logg.moves));
#endif
	}
	lflush();
	lrclose();
	return;
}

#ifndef UIDSCORE
/*
 * getplid(name)		Function to get players id # from id file
 *
 * Enter with the name of the players character in name.
 * Returns the id # of the players character, or -1 if failure.
 * This routine will try to find the name in the id file, if its not there,
 * it will try to make a new entry in the file.  Only returns -1 if can't
 * find him in the file, and can't make a new entry in the file.
 * Format of playerids file:
 * 		Id # in ascii     \n     character name     \n
 */
static int      havepid = -1;	/* playerid # if previously done */
int
getplid(nam)
	char           *nam;
{
	int             fd7, high = 999, no;
	char  *p, *p2;
	char            name[80];
	if (havepid != -1)
		return (havepid);	/* already did it */
	lflush();		/* flush any pending I/O */
	snprintf(name, sizeof(name), "%s\n", nam);/* append a \n to name */
	if (lopen(playerids) < 0) {	/* no file, make it */
		if ((fd7 = creat(playerids, 0664)) < 0)
			return (-1);	/* can't make it */
		close(fd7);
		goto addone;	/* now append new playerid record to file */
	}
	for (;;) {		/* now search for the name in the player id
				 * file */
		p = lgetl();
		if (p == NULL)
			break;	/* EOF? */
		no = atoi(p);	/* the id # */
		p2 = lgetl();
		if (p2 == NULL)
			break;	/* EOF? */
		if (no > high)
			high = no;	/* accumulate highest id # */
		if (strcmp(p2, name) == 0) {	/* we found him */
			return (no);	/* his id number */
		}
	}
	lrclose();
	/* if we get here, we didn't find him in the file -- put him there */
addone:
	if (lappend(playerids) < 0)
		return (-1);	/* can't open file for append */
	lprintf("%ld\n%s", (long) ++high, name);	/* new id # and name */
	lwclose();
	lcreat((char *) 0);	/* re-open terminal channel */
	return (high);
}
#endif	/* UIDSCORE */
