/*>>> epd.c: Extended Position Description routines */

/* Revised: 1996.06.23 */

/*
Copyright (C) 1996 by Steven J. Edwards (sje@mv.mv.com)
All rights reserved.  This code may be freely redistibuted and used by
both research and commerical applications.  No warranty exists.
*/

/*
Everything in this source file is independent of the host program, as
are the prototypes in the epd.h include file.  Requests for changes
and additions should be communicated to the author via the e-mail
address given above.
*/

/*
This file was originally prepared on an Apple Macintosh using the
Metrowerks CodeWarrior 6 ANSI C compiler.  Tabs are set at every
four columns.  Further testing and development was performed on a
generic PC running Linux 1.3.20 and using the gcc 2.7.0 compiler.
*/

/* system includes */

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

/* EPD definitions (host independent) */

#include "epddefs.h"

/* EPD routine prototypes (host independent) */

#include "epd.h"
#include "chess.h"
#include "data.h"

/* ASCII character constants */

#define ascii_nul ((char) 0x00)
#define ascii_sp  ((char) 0x20)

/* tree limit; adjust according to memory availability */

#define treeL 16384

/* tree overrun safety margin */

#define treemarginL 256

/* played moves history limit; adjust according to memory availability */

#define pmhL 512

/* data flows (input and output) */

#define flowL 2

/* character case mapping */

#define map_lower(ch) (isupper((ch)) ? tolower((ch)) : (ch))
#define map_upper(ch) (islower((ch)) ? toupper((ch)) : (ch))

/* identifier character check */

#define IdentChar(ch) (isalpha((ch)) || isdigit((ch)) || ((ch) == '_'))

/* vacancy check */

#define Vacant(sq) (EPDboard.rbv[(sq)] == cp_v0)

/* token record type (for token chain) */

typedef struct tknS
	{
	charptrT     tkn_str;  /* allocated token string value */
	struct tknS *tkn_prev; /* previous record */
	struct tknS *tkn_next; /* next record */
	} tknT, *tknptrT;

/* tree stack entry record type */

typedef struct tseS
	{
	siT   tse_count; /* entry count for this level */
	mptrT tse_base;  /* first move in moveset */
	mptrT tse_curr;  /* current move of interest in moveset */
	} tseT, *tseptrT;

/* color to move strings */

static charptrT ctmext_strv[rcL];

/* global game chain anchors */

static gamptrT head_gamptr;
static gamptrT tail_gamptr;

/* EPD standard opcode mnemonics */

static charptrT epdsostrv[epdsoL];

/* EPD refcom operand strings */

static charptrT refcomstrv[refcomL];

/* EPD refreq operand strings */

static charptrT refreqstrv[refreqL];

/* PGN Seven Tag Roster names */

static charptrT pgnstrstrv[pgnstrL];

/* game termination indication marker strings */

static charptrT gtimstrv[gtimL];

/* player name strings */

static charptrT playerstrv[rcL];

/* character conversion vectors (colors and pieces) */

static char asccv[rcL];
static char ascpv[rpL];

/* character conversion vectors (ranks and files) */

static char ascrv[rankL];
static char ascfv[fileL];

/* promotion piece from special case move code coversion vector */

static pT cv_p_scmvv[scmvL];

/* various color and piece conversion vectors */

static cpT cv_cp_c_pv[rcL][rpL];
static cT cv_c_cpv[cpL];
static pT cv_p_cpv[cpL];
static cT inv_cv[rcL];

/* direction vectors */

static dvT dvv[dxL];
static xdvT xdvv[dxL];

/* extension board (border detection) */

static xbT xb;

/* maps */

static siT flank_mapv[sqL];
static siT triangle_mapv[sqL];

/* token chain anchors */

static tknptrT head_tknptr;
static tknptrT tail_tknptr;

/* local SAN vector and its index */

static sanT lsan;
static siT lsani;

/* census vectors */

static siT count_cv[rcL];
static siT count_cpv[rcL][rpL];

/* the current board */

static rbT EPDboard;

/* the current environment stack entry  */

static eseT ese;

/* the current tree stack entry */

static tseT tse;

/* the master ply index */

static siT ply;

/* the base of the move tree and its current pointer */

static mptrT treebaseptr;
static mptrT treeptr;

/* the base of the tree stack entry stack and its current pointer */

static tseptrT tsebaseptr;
static tseptrT tseptr;

/* base of the environment stack and its current pointer */

static eseptrT esebaseptr;
static eseptrT eseptr;

/* tablebase information record vector storage base */

static tbptrT tbbaseptr;

/* tablebase file pointer cache vector */

static tbcT tbcv[tbcL];

/* return area for board data */

static rbT ret_rb;

/* return area for move data */

static mT ret_m;

/*--> EPDFatal: emit fatal diagnostic and quit */
nonstatic
void
EPDFatal(charptrT s)
{
fprintf(stderr, "EPD Fatal error: %s.\n", s);
exit(1);
}

/*--> EPDSwitchFault: emit switch fault diagnostic and quit */
nonstatic
void
EPDSwitchFault(charptrT s)
{
fprintf(stderr, "Switch fault detected.\n");
EPDFatal(s);

return;
}

/*--> EPDMemoryGrab: allocate memory */
nonstatic
voidptrT
EPDMemoryGrab(liT n)
{
voidptrT ptr;

ptr = (voidptrT) malloc((n == 0) ? 1 : n);
if (ptr == NULL)
	EPDFatal("EPDMemoryGrab");

return (ptr);
}

/*--> EPDMemoryFree: deallocate memory */
nonstatic
void
EPDMemoryFree(voidptrT ptr)
{
if (ptr != NULL)
	free(ptr);

return;
}

/*--> EPDStringGrab: allocate and copy a string */
nonstatic
charptrT
EPDStringGrab(charptrT s)
{
charptrT ptr;

ptr = (charptrT) EPDMemoryGrab(strlen(s) + 1);
strcpy(ptr, s);

return (ptr);
}

/*--> EPDStringFree: deallocate a string */
nonstatic
void
EPDStringFree(charptrT s)
{
EPDMemoryFree(s);

return;
}

/*--> EPDStringAppendChar: append a character to a string */
nonstatic
charptrT
EPDStringAppendChar(charptrT s, char c)
{
charptrT ptr;
liT length;

/* the first argument is deallocated */

length = strlen(s);
ptr = (charptrT) EPDMemoryGrab(length + 2);
strcpy(ptr, s);
EPDMemoryFree(s);
*(ptr + length) = c;
*(ptr + length + 1) = ascii_nul;

return (ptr);
}

/*--> EPDStringAppendStr: append a string to a string */
nonstatic
charptrT
EPDStringAppendStr(charptrT s0, charptrT s1)
{
charptrT ptr;
liT length;

/* the first argument is deallocated */

length = strlen(s0) + strlen(s1);
ptr = (charptrT) EPDMemoryGrab(length + 1);
strcpy(ptr, s0);
strcat(ptr, s1);
EPDMemoryFree(s0);

return (ptr);
}

/*--> EPDMapFromDuration: convert from duration to seconds */
nonstatic
liT
EPDMapFromDuration(charptrT s)
{
liT seconds;
siT scs, mns, hrs, dys;

sscanf(s, "%04hd:%02hd:%02hd:%02hd", &dys, &hrs, &mns, &scs);

seconds = ((liT) dys * 86400) +
	((liT) hrs * 3600) + ((liT) mns * 60) + (liT) scs;

return (seconds);
}

/*--> EPDMapToDuration: convert from seconds to duration */
nonstatic
charptrT
EPDMapToDuration(liT seconds)
{
charptrT s;
siT scs, mns, hrs, dys;

s = (charptrT) EPDMemoryGrab(durationL);

scs = seconds % 60;
mns = (seconds / 60) % 60;
hrs = (seconds / 3600) % 24;
dys = (seconds / 86400) % 10000;

sprintf(s, "%04hd:%02hd:%02hd:%02hd", dys, hrs, mns, scs);

return (s);
}

/*--> EPDNewGPM: allocate and initialize a new GPM record */
static
gpmptrT
EPDNewGPM(mptrT mptr)
{
gpmptrT gpmptr;
cT c;
sqT sq;
cpT cp0, cp1;

gpmptr = (gpmptrT) EPDMemoryGrab(sizeof(gpmT));

gpmptr->gpm_m = *mptr;

gpmptr->gpm_ese.ese_actc = ese.ese_actc;
gpmptr->gpm_ese.ese_cast = ese.ese_cast;
gpmptr->gpm_ese.ese_epsq = ese.ese_epsq;
gpmptr->gpm_ese.ese_hmvc = ese.ese_hmvc;
gpmptr->gpm_ese.ese_fmvn = ese.ese_fmvn;

for (c = c_w; c <= c_b; c++)
	gpmptr->gpm_ese.ese_ksqv[c] = ese.ese_ksqv[c];

for (sq = sq_a1; sq <= sq_h8; sq += 2)
	{
	cp0 = EPDboard.rbv[sq + 0];
	cp1 = EPDboard.rbv[sq + 1];
	gpmptr->gpm_nbv[sq >> 1] = ((cp1 << nybbW) | cp0);
	};

gpmptr->gpm_prev = gpmptr->gpm_next = NULL;

return (gpmptr);
}

/*--> EPDReleaseGPM: release a GPM record */
static
void
EPDReleaseGPM(gpmptrT gpmptr)
{
if (gpmptr != NULL)
	EPDMemoryFree(gpmptr);

return;
}

/*--> EPDAppendGPM: append a GPM record to a game  */
static
void
EPDAppendGPM(gamptrT gamptr, gpmptrT gpmptr)
{
if (gamptr->gam_tailgpm == NULL)
	gamptr->gam_headgpm = gpmptr;
else
	gamptr->gam_tailgpm->gpm_next = gpmptr;

gpmptr->gpm_prev = gamptr->gam_tailgpm;
gpmptr->gpm_next = NULL;

gamptr->gam_tailgpm = gpmptr;

return;
}

/*--> EPDUnthreadGPM: unthread a GPM record from a game */
static
void
EPDUnthreadGPM(gamptrT gamptr, gpmptrT gpmptr)
{
if (gpmptr->gpm_prev == NULL)
	gamptr->gam_headgpm = gpmptr->gpm_next;
else
	gpmptr->gpm_prev->gpm_next = gpmptr->gpm_next;

if (gpmptr->gpm_next == NULL)
	gamptr->gam_tailgpm = gpmptr->gpm_prev;
else
	gpmptr->gpm_next->gpm_prev = gpmptr->gpm_prev;

return;
}

/*--> EPDReleaseGPMoveChain: release the game played moves chain */
static
void
EPDReleaseGPMoveChain(gamptrT gamptr)
{
gpmptrT gpmptr;

while (gamptr->gam_headgpm != NULL)
	{
	gpmptr = gamptr->gam_tailgpm;
	EPDUnthreadGPM(gamptr, gpmptr);
	EPDReleaseGPM(gpmptr);
	};

return;
}

/*--> EPDNewGAM: allocate and initialize a new GAM record */
static
gamptrT
EPDNewGAM(void)
{
gamptrT gamptr;
pgnstrT pgnstr;

gamptr = (gamptrT) EPDMemoryGrab(sizeof(gamT));

for (pgnstr = 0; pgnstr < pgnstrL; pgnstr++)
	gamptr->gam_strv[pgnstr] = EPDStringGrab("");

gamptr->gam_gtim = gtim_u;

gamptr->gam_headgpm = gamptr->gam_tailgpm = NULL;
gamptr->gam_prev = gamptr->gam_next = NULL;

return (gamptr);
}

/*--> EPDReleaseGAM: release a GAM record */
static
void
EPDReleaseGAM(gamptrT gamptr)
{
pgnstrT pgnstr;

if (gamptr != NULL)
	{
	for (pgnstr = 0; pgnstr < pgnstrL; pgnstr++)
		if (gamptr->gam_strv[pgnstr] != NULL)
			EPDMemoryFree(gamptr->gam_strv[pgnstr]);
	EPDReleaseGPMoveChain(gamptr);
	EPDMemoryFree(gamptr);
	};

return;
}

/*--> EPDAppendGAM: append a GAM record to the game chain */
static
void
EPDAppendGAM(gamptrT gamptr)
{
if (tail_gamptr == NULL)
	head_gamptr = gamptr;
else
	tail_gamptr->gam_next = gamptr;

gamptr->gam_prev = tail_gamptr;
gamptr->gam_next = NULL;

tail_gamptr = gamptr;

return;
}

/*--> EPDUnthreadGAM: unthread a GAM record from the game chain */
static
void
EPDUnthreadGAM(gamptrT gamptr)
{
if (gamptr->gam_prev == NULL)
	head_gamptr = gamptr->gam_next;
else
	gamptr->gam_prev->gam_next = gamptr->gam_next;

if (gamptr->gam_next == NULL)
	tail_gamptr = gamptr->gam_prev;
else
	gamptr->gam_next->gam_prev = gamptr->gam_prev;

return;
}

/*--> EPDReleaseGameChain: release the game chain */
static
void
EPDReleaseGameChain(void)
{
gamptrT gamptr;

while (head_gamptr != NULL)
	{
	gamptr = tail_gamptr;
	EPDUnthreadGAM(gamptr);
	EPDReleaseGAM(gamptr);
	};

return;
}

/*--> EPDGamePlyCount: return ply count of a game */
static
siT
EPDGamePlyCount(gamptrT gamptr)
{
siT count;
gpmptrT gpmptr;

count = 0;
gpmptr = gamptr->gam_headgpm;

while (gpmptr != NULL)
	{
	count++;
	gpmptr = gpmptr->gpm_next;
	};

return (count);
}

/*--> EPDGameOpen: create/open a new game structure */
nonstatic
gamptrT
EPDGameOpen(void)
{
gamptrT gamptr;

gamptr = EPDNewGAM();
EPDAppendGAM(gamptr);

return (gamptr);
}

/*--> EPDGameClose: close/destroy a game structure */
nonstatic
void
EPDGameClose(gamptrT gamptr)
{
if (gamptr != NULL)
	{
	EPDUnthreadGAM(gamptr);
	EPDReleaseGAM(gamptr);
	};

return;
}

/*--> EPDGameAppendMove: append a move to a game structure */
nonstatic
void
EPDGameAppendMove(gamptrT gamptr, mptrT mptr)
{
gpmptrT gpmptr;

gpmptr = EPDNewGPM(mptr);
EPDAppendGPM(gamptr, gpmptr);

return;
}

/*--> EPDNewTKN: allocate and initialize a new TKN record */
static
tknptrT
EPDNewTKN(charptrT s)
{
tknptrT tknptr;

tknptr = (tknptrT) EPDMemoryGrab(sizeof(tknT));

tknptr->tkn_str = EPDStringGrab(s);
tknptr->tkn_prev = tknptr->tkn_next = NULL;

return (tknptr);
}

/*--> EPDReleaseTKN: release a TKN record */
static
void
EPDReleaseTKN(tknptrT tknptr)
{
if (tknptr != NULL)
	{
	if (tknptr->tkn_str != NULL)
		EPDMemoryFree(tknptr->tkn_str);
	EPDMemoryFree(tknptr);
	};

return;
}

/*--> EPDAppendTKN: append a TKN record to the token chain */
static
void
EPDAppendTKN(tknptrT tknptr)
{
if (tail_tknptr == NULL)
	head_tknptr = tknptr;
else
	tail_tknptr->tkn_next = tknptr;

tknptr->tkn_prev = tail_tknptr;
tknptr->tkn_next = NULL;

tail_tknptr = tknptr;

return;
}

/*--> EPDUnthreadTKN: unthread a TKN record from the token chain */
static
void
EPDUnthreadTKN(tknptrT tknptr)
{
if (tknptr->tkn_prev == NULL)
	head_tknptr = tknptr->tkn_next;
else
	tknptr->tkn_prev->tkn_next = tknptr->tkn_next;

if (tknptr->tkn_next == NULL)
	tail_tknptr = tknptr->tkn_prev;
else
	tknptr->tkn_next->tkn_prev = tknptr->tkn_prev;

return;
}

/*--> EPDReleaseTokenChain: release the token chain */
static
void
EPDReleaseTokenChain(void)
{
tknptrT tknptr;

while (head_tknptr != NULL)
	{
	tknptr = tail_tknptr;
	EPDUnthreadTKN(tknptr);
	EPDReleaseTKN(tknptr);
	};

return;
}

/*--> EPDTokenize: create the token chain */
nonstatic
void
EPDTokenize(charptrT s)
{
siT i;
char c;
tknptrT tknptr;
charptrT str;

/* first, release any existing chain */

EPDReleaseTokenChain();

/* scan the input until end of string */

i = 0;
c = *(s + i++);
while (c != ascii_nul)
	{
	/* skip leading whitespace */

	while ((c != ascii_nul) && isspace(c))
		c = *(s + i++);

	/* if not at end of string, then a token has started */

	if (c != ascii_nul)
		{
		str = EPDStringGrab("");

		while ((c != ascii_nul) && !isspace(c))
			{
			str = EPDStringAppendChar(str, c);
			c = *(s + i++);
			};

		tknptr = EPDNewTKN(str);
		EPDAppendTKN(tknptr);
		EPDMemoryFree(str);
		};
	};

return;
}

/*--> EPDTokenCount: count the tokens in the token chain */
nonstatic
siT
EPDTokenCount(void)
{
siT n;
tknptrT tknptr;

n = 0;
tknptr = head_tknptr;
while (tknptr != NULL)
	{
	n++;
	tknptr = tknptr->tkn_next;
	};

return (n);
}

/*--> EPDTokenFetch: fetch n-th (zero origin) token from the token chain */
nonstatic
charptrT
EPDTokenFetch(siT n)
{
charptrT s;
siT i;
tknptrT tknptr;

i = 0;
tknptr = head_tknptr;
while ((tknptr != NULL) && (i < n))
	{
	i++;
	tknptr = tknptr->tkn_next;
	};

if (tknptr == NULL)
	s = NULL;
else
	s = tknptr->tkn_str;

return (s);
}

/*--> EPDCICharEqual: test for case independent character equality */
nonstatic
siT
EPDCICharEqual(char ch0, char ch1)
{
siT flag;

if (map_lower(ch0) == map_lower(ch1))
	flag = 1;
else
	flag = 0;

return (flag);
}

/*--> EPDPieceFromCP: fetch piece from color-piece */
nonstatic
pT
EPDPieceFromCP(cpT cp)
{
pT p;

p = cv_p_cpv[cp];

return (p);
}

/*--> EPDCheckPiece: test if a character is a piece letter */
nonstatic
siT
EPDCheckPiece(char ch)
{
siT flag;
pT p;

flag = 0;
p = p_p;
while (!flag && (p <= p_k))
	if (EPDCICharEqual(ch, ascpv[p]))
		flag = 1;
	else
		p++;

return (flag);
}

/*--> EPDEvaluatePiece: evaluate a piece letter character */
nonstatic
pT
EPDEvaluatePiece(char ch)
{
pT p;
siT flag;

flag = 0;
p = p_p;
while (!flag && (p <= p_k))
	if (EPDCICharEqual(ch, ascpv[p]))
		flag = 1;
	else
		p++;

if (!flag)
	p = p_nil;

return (p);
}

/*--> EPDCheckColor: test if a character is a color letter */
nonstatic
siT
EPDCheckColor(char ch)
{
siT flag;
cT c;

flag = 0;
c = c_w;
while (!flag && (c <= c_b))
	if (EPDCICharEqual(ch, asccv[c]))
		flag = 1;
	else
		c++;

return (flag);
}

/*--> EPDEvaluateColor: evaluate a color letter character */
nonstatic
cT
EPDEvaluateColor(char ch)
{
cT c;
siT flag;

flag = 0;
c = c_w;
while (!flag && (c <= c_b))
	if (EPDCICharEqual(ch, asccv[c]))
		flag = 1;
	else
		c++;

if (!flag)
	c = c_nil;

return (c);
}

/*--> EPDCheckRank: test if a character is a rank character */
nonstatic
siT
EPDCheckRank(char ch)
{
siT flag;
rankT rank;

flag = 0;
rank = rank_1;
while (!flag && (rank <= rank_8))
	if (EPDCICharEqual(ch, ascrv[rank]))
		flag = 1;
	else
		rank++;

return (flag);
}

/*--> EPDEvaluateRank: evaluate a color rank character */
nonstatic
rankT
EPDEvaluateRank(char ch)
{
rankT rank;
siT flag;

flag = 0;
rank = rank_1;
while (!flag && (rank <= rank_8))
	if (EPDCICharEqual(ch, ascrv[rank]))
		flag = 1;
	else
		rank++;

if (!flag)
	rank = rank_nil;

return (rank);
}

/*--> EPDCheckFile: test if a character is a file character */
nonstatic
siT
EPDCheckFile(char ch)
{
siT flag;
fileT file;

flag = 0;
file = file_a;
while (!flag && (file <= file_h))
	if (EPDCICharEqual(ch, ascfv[file]))
		flag = 1;
	else
		file++;

return (flag);
}

/*--> EPDEvaluateFile: evaluate a color file character */
nonstatic
rankT
EPDEvaluateFile(char ch)
{
fileT file;
siT flag;

flag = 0;
file = file_a;
while (!flag && (file <= file_h))
	if (EPDCICharEqual(ch, ascfv[file]))
		flag = 1;
	else
		file++;

if (!flag)
	file = file_nil;

return (file);
}

/*--> EPDNewEOV: allocate a new EOV record */
nonstatic
eovptrT
EPDNewEOV(void)
{
eovptrT eovptr;

eovptr = (eovptrT) EPDMemoryGrab(sizeof(eovT));

eovptr->eov_eob = eob_nil;
eovptr->eov_str = NULL;
eovptr->eov_prev = eovptr->eov_next = NULL;

return (eovptr);
}

/*--> EPDReleaseEOV: release an EOV record */
nonstatic
void
EPDReleaseEOV(eovptrT eovptr)
{
if (eovptr != NULL)
	{
	if (eovptr->eov_str != NULL)
		EPDMemoryFree(eovptr->eov_str);
	EPDMemoryFree(eovptr);
	};

return;
}

/*--> EPDAppendEOV: append an EOV record */
nonstatic
void
EPDAppendEOV(eopptrT eopptr, eovptrT eovptr)
{
if (eopptr->eop_taileov == NULL)
	eopptr->eop_headeov = eovptr;
else
	eopptr->eop_taileov->eov_next = eovptr;

eovptr->eov_prev = eopptr->eop_taileov;
eovptr->eov_next = NULL;

eopptr->eop_taileov = eovptr;

return;
}

/*--> EPDUnthreadEOV: unthread an EOV record */
static
void
EPDUnthreadEOV(eopptrT eopptr, eovptrT eovptr)
{
if (eovptr->eov_prev == NULL)
	eopptr->eop_headeov = eovptr->eov_next;
else
	eovptr->eov_prev->eov_next = eovptr->eov_next;

if (eovptr->eov_next == NULL)
	eopptr->eop_taileov = eovptr->eov_prev;
else
	eovptr->eov_next->eov_prev = eovptr->eov_prev;

return;
}

/*--> EPDCreateEOVStr: create a new EOV record with a string value */
nonstatic
eovptrT
EPDCreateEOVStr(charptrT str)
{
eovptrT eovptr;

eovptr = EPDNewEOV();
eovptr->eov_eob = eob_string;
eovptr->eov_str = EPDStringGrab(str);

return (eovptr);
}

/*--> EPDCreateEOVSym: create a new EOV record with a symbol value */
nonstatic
eovptrT
EPDCreateEOVSym(charptrT sym)
{
eovptrT eovptr;

eovptr = EPDNewEOV();
eovptr->eov_eob = eob_symbol;
eovptr->eov_str = EPDStringGrab(sym);

return (eovptr);
}

/*--> EPDCreateEOVInt: create a new EOV record with an integer value */
nonstatic
eovptrT
EPDCreateEOVInt(liT lval)
{
eovptrT eovptr;
char tv[tL];

sprintf(tv, "%ld", lval);
eovptr = EPDNewEOV();
eovptr->eov_eob = eob_symbol;
eovptr->eov_str = EPDStringGrab(tv);

return (eovptr);
}

/*--> EPDLocateEOV: try to locate 1st EOV record with given string value */
nonstatic
eovptrT
EPDLocateEOV(eopptrT eopptr, charptrT strval)
{
eovptrT eovptr;
siT flag;

flag = 0;
eovptr = eopptr->eop_headeov;
while (!flag && (eovptr != NULL))
	if (strcmp(strval, eovptr->eov_str) == 0)
		flag = 1;
	else
		eovptr = eovptr->eov_next;

if (!flag)
	eovptr = NULL;

return (eovptr);
}

/*--> EPDCountEOV: count EOV entries in EOP */
nonstatic
siT
EPDCountEOV(eopptrT eopptr)
{
eovptrT eovptr;
siT count;

count = 0;
eovptr = eopptr->eop_headeov;
while (eovptr != NULL)
	{
	count++;
	eovptr = eovptr->eov_next;
	};

return (count);
}

/*--> EPDReplaceEOVStr: replace EOV string value with given string value */
nonstatic
void
EPDReplaceEOVStr(eovptrT eovptr, charptrT str)
{
if (eovptr->eov_str != NULL)
	{
	EPDMemoryFree(eovptr->eov_str);
	eovptr->eov_str = NULL;
	};

if (str != NULL)
	eovptr->eov_str = EPDStringGrab(str);

return;
}

/*--> EPDNewEOP: allocate a new EOP record */
nonstatic
eopptrT
EPDNewEOP(void)
{
eopptrT eopptr;

eopptr = (eopptrT) EPDMemoryGrab(sizeof(eopT));

eopptr->eop_opsym = NULL;
eopptr->eop_headeov = eopptr->eop_taileov = NULL;
eopptr->eop_prev = eopptr->eop_next = NULL;

return (eopptr);
}

/*--> EPDReleaseEOP: release an EOP record */
nonstatic
void
EPDReleaseEOP(eopptrT eopptr)
{
eovptrT eovptr0, eovptr1;

if (eopptr != NULL)
	{
	if (eopptr->eop_opsym != NULL)
		EPDMemoryFree(eopptr->eop_opsym);
	eovptr0 = eopptr->eop_headeov;
	while (eovptr0 != NULL)
		{
		eovptr1 = eovptr0->eov_next;
		EPDUnthreadEOV(eopptr, eovptr0);
		EPDReleaseEOV(eovptr0);
		eovptr0 = eovptr1;
		};
	EPDMemoryFree(eopptr);
	};

return;
}

/*--> EPDAppendEOP: append an EOP record */
nonstatic
void
EPDAppendEOP(epdptrT epdptr, eopptrT eopptr)
{
if (epdptr->epd_taileop == NULL)
	epdptr->epd_headeop = eopptr;
else
	epdptr->epd_taileop->eop_next = eopptr;

eopptr->eop_prev = epdptr->epd_taileop;
eopptr->eop_next = NULL;

epdptr->epd_taileop = eopptr;

return;
}

/*--> EPDUnthreadEOP: unthread an EOP record */
static
void
EPDUnthreadEOP(epdptrT epdptr, eopptrT eopptr)
{
if (eopptr->eop_prev == NULL)
	epdptr->epd_headeop = eopptr->eop_next;
else
	eopptr->eop_prev->eop_next = eopptr->eop_next;

if (eopptr->eop_next == NULL)
	epdptr->epd_taileop = eopptr->eop_prev;
else
	eopptr->eop_next->eop_prev = eopptr->eop_prev;

return;
}

/*--> EPDCreateEOP: create a new EOP record with opsym */
nonstatic
eopptrT
EPDCreateEOP(charptrT opsym)
{
eopptrT eopptr;

eopptr = EPDNewEOP();
eopptr->eop_opsym = EPDStringGrab(opsym);

return (eopptr);
}

/*--> EPDCreateEOPCode: create a new EOP record using opsym index */
nonstatic
eopptrT
EPDCreateEOPCode(epdsoT epdso)
{
eopptrT eopptr;

eopptr = EPDCreateEOP(EPDFetchOpsym(epdso));

return (eopptr);
}

/*--> EPDLocateEOP: attempt to locate EOP record with given opsym */
nonstatic
eopptrT
EPDLocateEOP(epdptrT epdptr, charptrT opsym)
{
eopptrT eopptr;
siT flag;

flag = 0;
eopptr = epdptr->epd_headeop;
while (!flag && (eopptr != NULL))
	if (strcmp(opsym, eopptr->eop_opsym) == 0)
		flag = 1;
	else
		eopptr = eopptr->eop_next;

if (!flag)
	eopptr = NULL;

return (eopptr);
}

/*--> EPDLocateEOPCode: attempt to locate EOP record with given code */
nonstatic
eopptrT
EPDLocateEOPCode(epdptrT epdptr, epdsoT epdso)
{

return (EPDLocateEOP(epdptr, epdsostrv[epdso]));
}

/*--> EPDCountEOP: count EOP entries in EPD */
nonstatic
siT
EPDCountEOP(epdptrT epdptr)
{
eopptrT eopptr;
siT count;

count = 0;
eopptr = epdptr->epd_headeop;
while (eopptr != NULL)
	{
	count++;
	eopptr = eopptr->eop_next;
	};

return (count);
}

/*--> EPDDropIfLocEOP: try to locate/drop EOP record with given opsym */
nonstatic
void
EPDDropIfLocEOP(epdptrT epdptr, charptrT opsym)
{
eopptrT eopptr;

eopptr = EPDLocateEOP(epdptr, opsym);
if (eopptr != NULL)
	{
	EPDUnthreadEOP(epdptr, eopptr);
	EPDReleaseEOP(eopptr);
	};

return;
}

/*--> EPDDropIfLocEOPCode: try to locate/drop EOP record with given code */
nonstatic
void
EPDDropIfLocEOPCode(epdptrT epdptr, epdsoT epdso)
{
eopptrT eopptr;

eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso]);
if (eopptr != NULL)
	{
	EPDUnthreadEOP(epdptr, eopptr);
	EPDReleaseEOP(eopptr);
	};

return;
}

/*--> EPDAddOpInt: add a single integer operand operation */
nonstatic
void
EPDAddOpInt(epdptrT epdptr, epdsoT epdso, liT val)
{
eopptrT eopptr;
eovptrT eovptr;

eovptr = EPDCreateEOVInt(val);
eopptr = EPDCreateEOPCode(epdso);
EPDAppendEOV(eopptr, eovptr);
EPDDropIfLocEOPCode(epdptr, epdso);
EPDAppendEOP(epdptr, eopptr);

return;
}

/*--> EPDAddOpStr: add a single string operand operation */
nonstatic
void
EPDAddOpStr(epdptrT epdptr, epdsoT epdso, charptrT s)
{
eopptrT eopptr;
eovptrT eovptr;

eovptr = EPDCreateEOVStr(s);
eopptr = EPDCreateEOPCode(epdso);
EPDAppendEOV(eopptr, eovptr);
EPDDropIfLocEOPCode(epdptr, epdso);
EPDAppendEOP(epdptr, eopptr);

return;
}

/*--> EPDAddOpSym: add a single symbol operand operation */
nonstatic
void
EPDAddOpSym(epdptrT epdptr, epdsoT epdso, charptrT s)
{
eopptrT eopptr;
eovptrT eovptr;

eovptr = EPDCreateEOVSym(s);
eopptr = EPDCreateEOPCode(epdso);
EPDAppendEOV(eopptr, eovptr);
EPDDropIfLocEOPCode(epdptr, epdso);
EPDAppendEOP(epdptr, eopptr);

return;
}

/*--> EPDNewEPD: allocate a new EPD record */
nonstatic
epdptrT
EPDNewEPD(void)
{
epdptrT epdptr;
siT i;

epdptr = (epdptrT) EPDMemoryGrab(sizeof(epdT));

for (i = 0; i < nbL; i++)
	epdptr->epd_nbv[i] = ((cp_v0 << nybbW) | cp_v0);

epdptr->epd_actc = c_v;
epdptr->epd_cast = 0;
epdptr->epd_epsq = sq_nil;
epdptr->epd_headeop = epdptr->epd_taileop = NULL;

return (epdptr);
}

/*--> EPDReleaseOperations: release EPD operation list */
nonstatic
void
EPDReleaseOperations(epdptrT epdptr)
{
eopptrT eopptr0, eopptr1;

if (epdptr != NULL)
	{
	eopptr0 = epdptr->epd_headeop;
	while (eopptr0 != NULL)
		{
		eopptr1 = eopptr0->eop_next;
		EPDUnthreadEOP(epdptr, eopptr0);
		EPDReleaseEOP(eopptr0);
		eopptr0 = eopptr1;
		};
	epdptr->epd_headeop = NULL;
	epdptr->epd_taileop = NULL;
	};

return;
}

/*--> EPDReleaseEPD: release an EPD record */
nonstatic
void
EPDReleaseEPD(epdptrT epdptr)
{
if (epdptr != NULL)
	{
	EPDReleaseOperations(epdptr);
	EPDMemoryFree(epdptr);
	};

return;
}

/*--> EPDFetchOpsym: return a pointer to the indicated mnemonic */
nonstatic
charptrT
EPDFetchOpsym(epdsoT epdso)
{
return (epdsostrv[epdso]);
}

/*--> EPDCountOperands: count operands */
static
siT
EPDCountOperands(eopptrT eopptr)
{
siT count;
eovptrT eovptr;

count = 0;
eovptr = eopptr->eop_headeov;
while (eovptr != NULL)
	{
	count++;
	eovptr = eovptr->eov_next;
	};

return (count);
}

/*--> EPDCountOperations: count operations */
static
siT
EPDCountOperations(epdptrT epdptr)
{
siT count;
eopptrT eopptr;

count = 0;
eopptr = epdptr->epd_headeop;
while (eopptr != NULL)
	{
	count++;
	eopptr = eopptr->eop_next;
	};

return (count);
}

/*--> EPDSortOperands: sort operands according to string value */
static
void
EPDSortOperands(eopptrT eopptr)
{
siT count;
siT pass, flag;
eovptrT ptr0, ptr1, ptr2, ptr3;

count = EPDCountOperands(eopptr);
if (count > 1)
	{
	flag = 1;
	pass = 0;
	while (flag && (pass < (count - 1)))
		{
		flag = 0;
		ptr0 = eopptr->eop_headeov;
		ptr1 = ptr0->eov_next;
		while (ptr1 != NULL)
			{
			if (strcmp(ptr0->eov_str, ptr1->eov_str) > 0)
				{
				flag = 1;

				ptr2 = ptr0->eov_prev;
				ptr3 = ptr1->eov_next;

				ptr0->eov_prev = ptr1;
				ptr0->eov_next = ptr3;

				ptr1->eov_prev = ptr2;
				ptr1->eov_next = ptr0;

				if (ptr2 == NULL)
					eopptr->eop_headeov = ptr1;
				else
					ptr2->eov_next = ptr1;

				if (ptr3 == NULL)
					eopptr->eop_taileov = ptr0;
				else
					ptr3->eov_prev = ptr0;
				}
			else
				ptr0 = ptr1;

			ptr1 = ptr0->eov_next;
			};
		pass++;
		};
	};

return;
}

/*--> EPDSortOperations: sort operations according to opcode */
static
void
EPDSortOperations(epdptrT epdptr)
{
siT count;
siT pass, flag;
eopptrT ptr0, ptr1, ptr2, ptr3;

count = EPDCountOperations(epdptr);
if (count > 1)
	{
	flag = 1;
	pass = 0;
	while (flag && (pass < (count - 1)))
		{
		flag = 0;
		ptr0 = epdptr->epd_headeop;
		ptr1 = ptr0->eop_next;
		while (ptr1 != NULL)
			{
			if (strcmp(ptr0->eop_opsym, ptr1->eop_opsym) > 0)
				{
				flag = 1;

				ptr2 = ptr0->eop_prev;
				ptr3 = ptr1->eop_next;

				ptr0->eop_prev = ptr1;
				ptr0->eop_next = ptr3;

				ptr1->eop_prev = ptr2;
				ptr1->eop_next = ptr0;

				if (ptr2 == NULL)
					epdptr->epd_headeop = ptr1;
				else
					ptr2->eop_next = ptr1;

				if (ptr3 == NULL)
					epdptr->epd_taileop = ptr0;
				else
					ptr3->eop_prev = ptr0;
				}
			else
				ptr0 = ptr1;

			ptr1 = ptr0->eop_next;
			};
		pass++;
		};
	};

return;
}

/*--> EPDNormalize: apply normalizing sorts */
static
void
EPDNormalize(epdptrT epdptr)
{
eopptrT eopptr;
charptrT opsym;
siT flag;

/* sort all operations */

EPDSortOperations(epdptr);

/* sort operands for selected standard operations */

eopptr = epdptr->epd_headeop;
while (eopptr != NULL)
	{
	flag = 0;
	opsym = eopptr->eop_opsym;

	if (!flag && (strcmp(opsym, epdsostrv[epdso_am]) == 0))
		{
		EPDSortOperands(eopptr);
		flag = 1;
		};

	if (!flag && (strcmp(opsym, epdsostrv[epdso_bm]) == 0))
		{
		EPDSortOperands(eopptr);
		flag = 1;
		};

	eopptr = eopptr->eop_next;
	};

return;
}

/*--> EPDCloneEPDBase: clone an EPD structure, base items only */
nonstatic
epdptrT
EPDCloneEPDBase(epdptrT epdptr)
{
epdptrT nptr;
siT index;

nptr = EPDNewEPD();

for (index = 0; index < nbL; index++)
	nptr->epd_nbv[index] = epdptr->epd_nbv[index];

nptr->epd_actc = epdptr->epd_actc;
nptr->epd_cast = epdptr->epd_cast;
nptr->epd_epsq = epdptr->epd_epsq;

return (nptr);
}

/*--> EPDCloneEOV: clone an EOV structure */
nonstatic
eovptrT
EPDCloneEOV(eovptrT eovptr)
{
eovptrT nptr;

nptr = EPDNewEOV();

nptr->eov_eob = eovptr->eov_eob;

if (eovptr->eov_str != NULL)
	nptr->eov_str = EPDStringGrab(eovptr->eov_str);

return (nptr);
}

/*--> EPDCloneEOP: clone an EOP structure */
nonstatic
eopptrT
EPDCloneEOP(eopptrT eopptr)
{
eopptrT nptr;
eovptrT eovptr, rptr;

nptr = EPDNewEOP();

if (eopptr->eop_opsym != NULL)
	nptr->eop_opsym = EPDStringGrab(eopptr->eop_opsym);

rptr = eopptr->eop_headeov;
while (rptr != NULL)
	{
	eovptr = EPDCloneEOV(rptr);
	EPDAppendEOV(nptr, eovptr);
	rptr = rptr->eov_next;
	};

return (nptr);
}

/*--> EPDCloneEPD: clone an EPD structure */
nonstatic
epdptrT
EPDCloneEPD(epdptrT epdptr)
{
epdptrT nptr;
eopptrT eopptr, rptr;

nptr = EPDCloneEPDBase(epdptr);

rptr = epdptr->epd_headeop;
while (rptr != NULL)
	{
	eopptr = EPDCloneEOP(rptr);
	EPDAppendEOP(nptr, eopptr);
	rptr = rptr->eop_next;
	};

return (nptr);
}

/*--> EPDSetKings: set the king location vector */
static
void
EPDSetKings(void)
{
sqT sq;

/* this operates only on the local environment */

ese.ese_ksqv[c_w] = ese.ese_ksqv[c_b] = sq_nil;

for (sq = sq_a1; sq <= sq_h8; sq++)
	switch (EPDboard.rbv[sq])
		{
		case cp_wk:
			ese.ese_ksqv[c_w] = sq;
			break;
		case cp_bk:
			ese.ese_ksqv[c_b] = sq;
			break;
		default:
			break;
		};

return;
}

/*--> EPDSet: set up an EPD structure for the given position */
nonstatic
epdptrT
EPDSet(rbptrT rbptr, cT actc, castT cast, sqT epsq)
{
epdptrT epdptr;
sqT sq;
cpT cp0, cp1;

/* this does not reference the current position */

epdptr = EPDNewEPD();

for (sq = sq_a1; sq <= sq_h8; sq += 2)
	{
	cp0 = rbptr->rbv[sq + 0];
	cp1 = rbptr->rbv[sq + 1];
	epdptr->epd_nbv[sq >> 1] = ((cp1 << nybbW) | cp0);
	};

epdptr->epd_actc = actc;
epdptr->epd_cast = cast;
epdptr->epd_epsq = epsq;

return (epdptr);
}

/*--> EPDSetCurrentPosition: set current position */
nonstatic
void
EPDSetCurrentPosition(rbptrT rbptr, cT actc, castT cast, sqT epsq, siT hmvc, siT fmvn)
{
sqT sq;

/* this changes the current position */

for (sq = sq_a1; sq <= sq_h8; sq++)
	EPDboard.rbv[sq] = rbptr->rbv[sq];

ese.ese_actc = actc;
ese.ese_cast = cast;
ese.ese_epsq = epsq;
ese.ese_hmvc = hmvc;
ese.ese_fmvn = fmvn;

EPDSetKings();

return;
}

/*--> EPDGetCurrentPosition: return EPD structure for current position */
nonstatic
epdptrT
EPDGetCurrentPosition(void)
{
epdptrT epdptr;
sqT sq;
cpT cp0, cp1;

epdptr = EPDNewEPD();

for (sq = sq_a1; sq <= sq_h8; sq += 2)
	{
	cp0 = EPDboard.rbv[sq + 0];
	cp1 = EPDboard.rbv[sq + 1];
	epdptr->epd_nbv[sq >> 1] = ((cp1 << nybbW) | cp0);
	};

epdptr->epd_actc = ese.ese_actc;
epdptr->epd_cast = ese.ese_cast;
epdptr->epd_epsq = ese.ese_epsq;

return (epdptr);
}

/*--> EPDFetchACTC: fetch current active color */
nonstatic
cT
EPDFetchACTC(void)
{
/* return the value of the current active color */

return (ese.ese_actc);
}

/*--> EPDFetchCAST: fetch current castling availability */
nonstatic
castT
EPDFetchCAST(void)
{
/* return the value of the current castling availability */

return (ese.ese_cast);
}

/*--> EPDFetchEPSQ: fetch current en passant target square */
nonstatic
sqT
EPDFetchEPSQ(void)
{
/* return the value of the current en passant target square */

return (ese.ese_epsq);
}

/*--> EPDFetchHMVC: fetch current halfmove clock */
nonstatic
siT
EPDFetchHMVC(void)
{
/* return the value of the current halfmove clock */

return (ese.ese_hmvc);
}

/*--> EPDFetchFMVN: fetch current fullmove number */
nonstatic
siT
EPDFetchFMVN(void)
{
/* return the value of the current fullmove number */

return (ese.ese_fmvn);
}

/*--> EPDFetchBoard: fetch current board */
nonstatic
rbptrT
EPDFetchBoard(void)
{
/* copy from the local board into the designated static return area */

ret_rb = EPDboard;

return (&ret_rb);
}

/*--> EPDFetchCP: fetch color-piece */
nonstatic
cpT
EPDFetchCP(sqT sq)
{
cpT cp;

/* fetch from the local board */

cp = EPDboard.rbv[sq];

return (cp);
}

/*--> EPDFetchBoardString: create and return a board diagram */
nonstatic
charptrT
EPDFetchBoardString(void)
{
charptrT s;
charptrT r;
rankT rank;
fileT file;
cpT cp;

/* allocate */

s = (charptrT) EPDMemoryGrab((rankL * ((fileL * 2) + 1)) + 1);

/* fill */

r = s;
for (rank = rank_8; rank >= rank_1; rank--)
	{
	for (file = file_a; file <= file_h; file++)
		{
		cp = EPDboard.rbm[rank][file];
		if (cp == cp_v0) {
			if ((rank % 2) == (file % 2))
				{
				*r++ = ':';
				*r++ = ':';
				}
			else
				{
				*r++ = ascii_sp;
				*r++ = ascii_sp;
				}
                }
		else
			{
			*r++ = asccv[cv_c_cpv[cp]];
			*r++ = ascpv[cv_p_cpv[cp]];
			};
		};
	*r++ = '\n';
	};

/* terminating NUL */

*r = ascii_nul;

return (s);
}

/*--> EPDGetGTIM: get game termination marker indicator */
nonstatic
gtimT
EPDGetGTIM(gamptrT gamptr)
{

return (gamptr->gam_gtim);
}

/*--> EPDPutGTIM: put game termination marker indicator */
nonstatic
void
EPDPutGTIM(gamptrT gamptr, gtimT gtim)
{
gamptr->gam_gtim = gtim;

return;
}

/*--> EPDGenBasic: generate basic EPD notation for a given position */
nonstatic
charptrT
EPDGenBasic(rbptrT rbptr, cT actc, castT cast, sqT epsq)
{
charptrT ptr;
epdptrT epdptr;

/* this does not reference the current position */

epdptr = EPDSet(rbptr, actc, cast, epsq);
ptr = EPDEncode(epdptr);

EPDReleaseEPD(epdptr);

return (ptr);
}

/*--> EPDGenBasicCurrent: generate basic EPD for current position */
nonstatic
charptrT
EPDGenBasicCurrent(void)
{
charptrT ptr;

/* this references but does not change the current position */

ptr = EPDGenBasic(&EPDboard, ese.ese_actc, ese.ese_cast, ese.ese_epsq);

return (ptr);
}

/*--> EPDDecodeFEN: read a FEN string to make an EPD structure */
nonstatic
epdptrT
EPDDecodeFEN(charptrT s)
{
epdptrT epdptr;
siT flag;
tknptrT save_head_tknptr, save_tail_tknptr;
charptrT tptr;

/* this does not reference the current position */

flag = 1;
epdptr = NULL;
tptr = NULL;

/* save the initial token chain pointers */

save_head_tknptr = head_tknptr;
save_tail_tknptr = tail_tknptr;

/* clear the token chain pointers */

head_tknptr = NULL;
tail_tknptr = NULL;

/* tokenize the input */

EPDTokenize(s);

/* check for six tokens */

if (flag)
	if (EPDTokenCount() != 6)
		flag = 0;

/* construct an input string from the tokens */

if (flag)
	{
	/* handle the first four common fields */

	tptr = EPDStringGrab("");
	tptr = EPDStringAppendStr(tptr, EPDTokenFetch(0));
	tptr = EPDStringAppendChar(tptr, ascii_sp);
	tptr = EPDStringAppendStr(tptr, EPDTokenFetch(1));
	tptr = EPDStringAppendChar(tptr, ascii_sp);
	tptr = EPDStringAppendStr(tptr, EPDTokenFetch(2));
	tptr = EPDStringAppendChar(tptr, ascii_sp);
	tptr = EPDStringAppendStr(tptr, EPDTokenFetch(3));
	tptr = EPDStringAppendChar(tptr, ascii_sp);

	/* append the halfmove clock operation */

	tptr = EPDStringAppendStr(tptr, epdsostrv[epdso_hmvc]);
	tptr = EPDStringAppendChar(tptr, ascii_sp);
	tptr = EPDStringAppendStr(tptr, EPDTokenFetch(4));
	tptr = EPDStringAppendChar(tptr, ';');
	tptr = EPDStringAppendChar(tptr, ascii_sp);

	/* append the fullmove number operation */

	tptr = EPDStringAppendStr(tptr, epdsostrv[epdso_fmvn]);
	tptr = EPDStringAppendChar(tptr, ascii_sp);
	tptr = EPDStringAppendStr(tptr, EPDTokenFetch(5));
	tptr = EPDStringAppendChar(tptr, ';');
	};

/* release the temporary token chain */

EPDReleaseTokenChain();

/* restore the initial token chain pointers */

head_tknptr = save_head_tknptr;
tail_tknptr = save_tail_tknptr;

/* read the resulting EPD input string */

if (flag)
	{
	epdptr = EPDDecode(tptr);
	if (epdptr == NULL)
		flag = 0;
	};

/* cancellations if a problem occurred */

if (!flag)
	{
	if (tptr != NULL)
		EPDMemoryFree(tptr);

	if (epdptr != NULL)
		{
		EPDReleaseEPD(epdptr);
		epdptr = NULL;
		};
	};

return (epdptr);
}

/*--> EPDEncodeFEN: make a FEN string from an EPD structure */
nonstatic
charptrT
EPDEncodeFEN(epdptrT epdptr)
{
charptrT s;
char ch;
siT bi;
siT ps;
sqT sq;
cpT cp;
rankT rank;
fileT file;
eopptrT eopptr;
char nv[tL];
char bv[tL];

/* this does not reference the current position */

bi = 0;

/* output board */

for (rank = rank_8; rank >= rank_1; rank--)
	{
	ps = 0;
	for (file = file_a; file <= file_h; file++)
		{
		sq = map_sq(rank, file);
		if ((sq % 2) == 0)
			cp = (epdptr->epd_nbv[sq >> 1] & nybbM);
		else
			cp = ((epdptr->epd_nbv[sq >> 1] >> nybbW) & nybbM);

		if (cp == cp_v0)
			ps++;
		else
			{
			if (ps != 0)
				{
				bv[bi++] = '0' + ps;
				ps = 0;
				};
			ch = ascpv[cv_p_cpv[cp]];
			if (cv_c_cpv[cp] == c_w)
				ch = map_upper(ch);
			else
				ch = map_lower(ch);
			bv[bi++] = ch;
			};
		};
	if (ps != 0)
		{
		bv[bi++] = '0' + ps;
		ps = 0;
		};
	if (rank != rank_1)
		bv[bi++] = '/';
	};
bv[bi++] = ascii_sp;

/* output active color (lower case) */

bv[bi++] = map_lower(asccv[epdptr->epd_actc]);
bv[bi++] = ascii_sp;

/* output castling availablility */

if (epdptr->epd_cast == 0)
	bv[bi++] = '-';
else
	{
	if (epdptr->epd_cast & cf_wk)
		bv[bi++] = map_upper(ascpv[p_k]);
	if (epdptr->epd_cast & cf_wq)
		bv[bi++] = map_upper(ascpv[p_q]);
	if (epdptr->epd_cast & cf_bk)
		bv[bi++] = map_lower(ascpv[p_k]);
	if (epdptr->epd_cast & cf_bq)
		bv[bi++] = map_lower(ascpv[p_q]);
	};
bv[bi++] = ascii_sp;

/* output ep capture square */

if (epdptr->epd_epsq == sq_nil)
	bv[bi++] = '-';
else
	{
	bv[bi++] = ascfv[map_file(epdptr->epd_epsq)];
	bv[bi++] = ascrv[map_rank(epdptr->epd_epsq)];
	};
bv[bi++] = ascii_sp;

/* output halfmove clock */

eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso_hmvc]);
if ((eopptr != NULL) && (eopptr->eop_headeov != NULL) &&
	(eopptr->eop_headeov->eov_str != NULL))
	sprintf(nv, "%ld", atol(eopptr->eop_headeov->eov_str));
else
	sprintf(nv, "0");
strcpy(&bv[bi], nv);
bi += strlen(nv);
bv[bi++] = ascii_sp;

/* output fullmove number */

eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso_fmvn]);
if ((eopptr != NULL) && (eopptr->eop_headeov != NULL) &&
	(eopptr->eop_headeov->eov_str != NULL))
	sprintf(nv, "%ld", atol(eopptr->eop_headeov->eov_str));
else
	sprintf(nv, "1");
strcpy(&bv[bi], nv);
bi += strlen(nv);

/* NUL termination */

bv[bi++] = ascii_nul;

/* allocate result */

s = EPDStringGrab(bv);

return (s);
}

/*--> EPDDecode: read an EPD structure from a string */
nonstatic
epdptrT
EPDDecode(charptrT s)
{
epdptrT epdptr;
eopptrT eopptr;
eovptrT eovptr;
siT flag, quoteflag;
siT ch;
liT i;
siT j, d;
byteptrT bptr;
fileT file;
rankT rank;
sqT sq;
cT c;
pT p;
cpT cp;

/* this does not reference the current position */

/* set up */

flag = 1;
i = 0;
ch = *(s + i++);

/* initialize the return structure */

epdptr = EPDNewEPD();

/* skip whitespace */

if (flag)
	{
	while (flag && (ch != ascii_nul) && isspace(ch))
		ch = *(s + i++);
	if (ch == ascii_nul)
		flag = 0;
	};

/* process piece placement data */

if (flag)
	{
	rank = rank_8;
	file = file_a;

	while (flag && (ch != ascii_nul) && !isspace(ch))
		{
		switch (ch)
			{
			case '/':
				if ((file != fileL) || (rank == rank_1))
					flag = 0;
				else
					{
					rank--;
					file = file_a;
					};
				break;

			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
				d = ch - '0';
				if ((file + d) > fileL)
					flag = 0;
				else
					for (j = 0; j < d; j++)
						{
						sq = map_sq(rank, file);
						bptr = &epdptr->epd_nbv[sq >> 1];
						if ((sq % 2) == 0)
							{
							*bptr &= ~nybbM;
							*bptr |= cp_v0;
							}
						else
							{
							*bptr &= ~(nybbM << nybbW);
							*bptr |= (cp_v0 << nybbW);
							};
						file++;
						};
				break;

			default:
				if (!EPDCheckPiece((char)ch) || (file >= fileL))
					flag = 0;
				else
					{
					p = EPDEvaluatePiece((char)ch);
					if (isupper(ch))
						c = c_w;
					else
						c = c_b;
					sq = map_sq(rank, file);
					bptr = &epdptr->epd_nbv[sq >> 1];
					cp = cv_cp_c_pv[c][p];
					if ((sq % 2) == 0)
						{
						*bptr &= ~nybbM;
						*bptr |= cp;
						}
					else
						{
						*bptr &= ~(nybbM << nybbW);
						*bptr |= (cp << nybbW);
						};
					file++;
					};
				break;
			};

		ch = *(s + i++);
		};

	if (flag)
		if ((file != fileL) || (rank != rank_1))
			flag = 0;
	};

/* need at least one whitespace character */

if (flag)
	if ((ch == ascii_nul) || !isspace(ch))
		flag = 0;

/* skip whitespace */

if (flag)
	{
	while (flag && (ch != ascii_nul) && isspace(ch))
		ch = *(s + i++);
	if (ch == ascii_nul)
		flag = 0;
	};

/* process active color */

if (flag)
	{
	if (!EPDCheckColor((char)ch))
		flag = 0;
	else
		{
		epdptr->epd_actc = EPDEvaluateColor((char)ch);
		ch = *(s + i++);
		};
	};

/* need at least one whitespace character */

if (flag)
	if ((ch == ascii_nul) || !isspace(ch))
		flag = 0;

/* skip whitespace */

if (flag)
	{
	while (flag && (ch != ascii_nul) && isspace(ch))
		ch = *(s + i++);
	if (ch == ascii_nul)
		flag = 0;
	};

/* process castling availability */

if (flag)
	{
	epdptr->epd_cast = 0;
	if (ch == '-')
		ch = *(s + i++);
	else
		{
		/* white kingside castling availability */

		if (flag && (ch == map_upper(ascpv[p_k])))
			{
			epdptr->epd_cast |= cf_wk;
			ch = *(s + i++);
			if (ch == ascii_nul)
				flag = 0;
			};

		/* white queenside castling availability */

		if (flag && (ch == map_upper(ascpv[p_q])))
			{
			epdptr->epd_cast |= cf_wq;
			ch = *(s + i++);
			if (ch == ascii_nul)
				flag = 0;
			};

		/* black kingside castling availability */

		if (flag && (ch == map_lower(ascpv[p_k])))
			{
			epdptr->epd_cast |= cf_bk;
			ch = *(s + i++);
			if (ch == ascii_nul)
				flag = 0;
			};

		/* black queenside castling availability */

		if (flag && (ch == map_lower(ascpv[p_q])))
			{
			epdptr->epd_cast |= cf_bq;
			ch = *(s + i++);
			if (ch == ascii_nul)
				flag = 0;
			};
		};
	};

/* need at least one whitespace character */

if (flag)
	if ((ch == ascii_nul) || !isspace(ch))
		flag = 0;

/* skip whitespace */

if (flag)
	{
	while (flag && (ch != ascii_nul) && isspace(ch))
		ch = *(s + i++);
	if (ch == ascii_nul)
		flag = 0;
	};

/* process en passant target */

if (flag) {
	if (ch == '-')
		{
		epdptr->epd_epsq = sq_nil;
		ch = *(s + i++);
		if ((ch != ascii_nul) && !isspace(ch))
			flag = 0;
		}
	else {
		if (!EPDCheckFile((char)ch))
			flag = 0;
		else
			{
			file = EPDEvaluateFile((char)ch);
			ch = *(s + i++);
			if ((ch == ascii_nul) || !EPDCheckRank((char)ch))
				flag = 0;
			else
				{
				epdptr->epd_epsq = map_sq(EPDEvaluateRank((char)ch), file);
				ch = *(s + i++);
				if ((ch != ascii_nul) && !isspace(ch))
					flag = 0;
				};
			};
                }
}

/* skip whitespace (end-of-line is not an error) */

if (flag)
	while ((ch != ascii_nul) && isspace(ch))
		ch = *(s + i++);

/* process operation sequence (if any) */

if (flag)
	{
	while (flag && (ch != ascii_nul))
		{
		/* allocate a new operation */

		eopptr = EPDNewEOP();

		/* form opsym (first character) */

		if (IdentChar(ch))
			{
			eopptr->eop_opsym = EPDStringGrab("");
			eopptr->eop_opsym = EPDStringAppendChar(eopptr->eop_opsym, (char)ch);
			ch = *(s + i++);
			}
		else
			flag = 0;

		/* form remainder of opsym */

		while (IdentChar(ch))
			{
			eopptr->eop_opsym = EPDStringAppendChar(eopptr->eop_opsym, (char)ch);
			ch = *(s + i++);
			};

		/* skip whitespace */

		if (flag)
			{
			while (flag && (ch != ascii_nul) && isspace(ch))
				ch = *(s + i++);
			if (ch == ascii_nul)
				flag = 0;
			};

		/* process operand list */

		while (flag && (ch != ';'))
			{
			/* allocate operand value */

			eovptr = EPDNewEOV();

			/* set quoted string as appropriate */

			if (ch == '"')
				{
				quoteflag = 1;
				eovptr->eov_eob = eob_string;
				ch = *(s + i++);
				}
			else
				{
				quoteflag = 0;
				eovptr->eov_eob = eob_symbol;
				};

			eovptr->eov_str = EPDStringGrab("");

			if (quoteflag)
				{
				while (flag && (ch != '"'))
					{
					if (ch == ascii_nul)
						flag = 0;
					else
						{
						eovptr->eov_str =
							EPDStringAppendChar(eovptr->eov_str, (char)ch);
						ch = *(s + i++);
						};
					};
				if (ch == '"')
					ch = *(s + i++);
				}
			else
				{
				while (flag && !isspace(ch) && (ch != ';'))
					{
					if (ch == ascii_nul)
						flag = 0;
					else
						{
						eovptr->eov_str =
							EPDStringAppendChar(eovptr->eov_str, (char)ch);
						ch = *(s + i++);
						};
					};
				};

			/* append operand onto operation */

			if (flag)
				EPDAppendEOV(eopptr, eovptr);
			else
				EPDReleaseEOV(eovptr);

			/* skip whitespace */

			while (flag && (ch != ascii_nul) && isspace(ch))
				ch = *(s + i++);
			};

		/* process semicolon */

		if (flag) {
			if (ch == ';')
				ch = *(s + i++);
			else
				flag = 0;
                }

		/* append operation */

		if (flag)
			EPDAppendEOP(epdptr, eopptr);
		else
			EPDReleaseEOP(eopptr);

		/* skip whitespace (end-of-line is not an error) */

		if (flag)
			while (flag && (ch != ascii_nul) && isspace(ch))
				ch = *(s + i++);
		};
	};

/* check for fault */

if (!flag)
	{
	EPDReleaseEPD(epdptr);
	epdptr = NULL;
	};

/* normalize */

if (epdptr != NULL)
	EPDNormalize(epdptr);

return (epdptr);
}

/*--> EPDEncode: write an EPD structure to a string */
nonstatic
charptrT
EPDEncode(epdptrT epdptr)
{
charptrT ptr;
sqT sq;
cpT cp;
rankT rank;
fileT file;
siT bi, ps, ch;
char bv[tL];
eopptrT eopptr;
eovptrT eovptr;
charptrT s0, s1;

/* this does not reference the current position */

bi = 0;

/* normalize */

EPDNormalize(epdptr);

/* output board */

for (rank = rank_8; rank >= rank_1; rank--)
	{
	ps = 0;
	for (file = file_a; file <= file_h; file++)
		{
		sq = map_sq(rank, file);
		if ((sq % 2) == 0)
			cp = (epdptr->epd_nbv[sq >> 1] & nybbM);
		else
			cp = ((epdptr->epd_nbv[sq >> 1] >> nybbW) & nybbM);

		if (cp == cp_v0)
			ps++;
		else
			{
			if (ps != 0)
				{
				bv[bi++] = '0' + ps;
				ps = 0;
				};
			ch = ascpv[cv_p_cpv[cp]];
			if (cv_c_cpv[cp] == c_w)
				ch = map_upper(ch);
			else
				ch = map_lower(ch);
			bv[bi++] = ch;
			};
		};
	if (ps != 0)
		{
		bv[bi++] = '0' + ps;
		ps = 0;
		};
	if (rank != rank_1)
		bv[bi++] = '/';
	};
bv[bi++] = ascii_sp;

/* output active color (lower case) */

bv[bi++] = map_lower(asccv[epdptr->epd_actc]);
bv[bi++] = ascii_sp;

/* output castling availablility */

if (epdptr->epd_cast == 0)
	bv[bi++] = '-';
else
	{
	if (epdptr->epd_cast & cf_wk)
		bv[bi++] = map_upper(ascpv[p_k]);
	if (epdptr->epd_cast & cf_wq)
		bv[bi++] = map_upper(ascpv[p_q]);
	if (epdptr->epd_cast & cf_bk)
		bv[bi++] = map_lower(ascpv[p_k]);
	if (epdptr->epd_cast & cf_bq)
		bv[bi++] = map_lower(ascpv[p_q]);
	};
bv[bi++] = ascii_sp;

/* output ep capture square */

if (epdptr->epd_epsq == sq_nil)
	bv[bi++] = '-';
else
	{
	bv[bi++] = ascfv[map_file(epdptr->epd_epsq)];
	bv[bi++] = ascrv[map_rank(epdptr->epd_epsq)];
	};

/* NUL termination */

bv[bi++] = ascii_nul;

/* allocate and copy basic result */

ptr = EPDStringGrab(bv);

/* construct and append operations */

eopptr = epdptr->epd_headeop;
while (eopptr != NULL)
	{
	/* leading space */

	s0 = EPDStringGrab(" ");

	/* opcode */

	s0 = EPDStringAppendStr(s0, eopptr->eop_opsym);

	/* construct and append operands */

	eovptr = eopptr->eop_headeov;
	while (eovptr != NULL)
		{
		/* leading space */

		s1 = EPDStringGrab(" ");

		/* conjure operand value */

		switch (eovptr->eov_eob)
			{
			case eob_string:
				s1 = EPDStringAppendChar(s1, '"');
				s1 = EPDStringAppendStr(s1, eovptr->eov_str);
				s1 = EPDStringAppendChar(s1, '"');
				break;

			case eob_symbol:
				s1 = EPDStringAppendStr(s1, eovptr->eov_str);
				break;

			default:
				EPDSwitchFault("EPDEncode");
				break;
			};

		/* append */

		s0 = EPDStringAppendStr(s0, s1);
		EPDMemoryFree(s1);

		/* next operand */

		eovptr = eovptr->eov_next;
		};

	/* trailing semicolon */

	s0 = EPDStringAppendChar(s0, ';');

	/* append operation */

	ptr = EPDStringAppendStr(ptr, s0);
	EPDMemoryFree(s0);

	/* advance */

	eopptr = eopptr->eop_next;
	};

return (ptr);
}

/*--> EPDRealize: set the current position according to EPD */
nonstatic
void
EPDRealize(epdptrT epdptr)
{
sqT sq;
cpT cp;
eopptrT eopptr;
eovptrT eovptr;

/* this changes the current position */

for (sq = sq_a1; sq <= sq_h8; sq++)
	{
	if ((sq % 2) == 0)
		cp = (epdptr->epd_nbv[sq >> 1] & nybbM);
	else
		cp = ((epdptr->epd_nbv[sq >> 1] >> nybbW) & nybbM);
	EPDboard.rbv[sq] = cp;
	};

ese.ese_actc = epdptr->epd_actc;
ese.ese_cast = epdptr->epd_cast;
ese.ese_epsq = epdptr->epd_epsq;

eopptr = EPDLocateEOPCode(epdptr, epdso_hmvc);
if ((eopptr != NULL) && ((eovptr = eopptr->eop_headeov) != NULL))
	ese.ese_hmvc = atoi(eovptr->eov_str);
else
	ese.ese_hmvc = 0;

eopptr = EPDLocateEOPCode(epdptr, epdso_fmvn);
if ((eopptr != NULL) && ((eovptr = eopptr->eop_headeov) != NULL))
	ese.ese_fmvn = atoi(eovptr->eov_str);
else
	ese.ese_fmvn = 1;

EPDSetKings();

return;
}

/*--> EPDInitArray: set the current position to the initial array */
nonstatic
void
EPDInitArray(void)
{
sqT sq;

/* this changes the current position */

for (sq = sq_a1; sq <= sq_h8; sq++)
	EPDboard.rbv[sq] = cp_v0;

EPDboard.rbv[sq_a1] = EPDboard.rbv[sq_h1] = cp_wr;
EPDboard.rbv[sq_b1] = EPDboard.rbv[sq_g1] = cp_wn;
EPDboard.rbv[sq_c1] = EPDboard.rbv[sq_f1] = cp_wb;
EPDboard.rbv[sq_d1] = cp_wq;
EPDboard.rbv[sq_e1] = cp_wk;

for (sq = sq_a2; sq <= sq_h2; sq++)
	EPDboard.rbv[sq] = cp_wp;

EPDboard.rbv[sq_a8] = EPDboard.rbv[sq_h8] = cp_br;
EPDboard.rbv[sq_b8] = EPDboard.rbv[sq_g8] = cp_bn;
EPDboard.rbv[sq_c8] = EPDboard.rbv[sq_f8] = cp_bb;
EPDboard.rbv[sq_d8] = cp_bq;
EPDboard.rbv[sq_e8] = cp_bk;

for (sq = sq_a7; sq <= sq_h7; sq++)
	EPDboard.rbv[sq] = cp_bp;

ese.ese_actc = c_w;
ese.ese_cast = cf_wk | cf_wq | cf_bk | cf_bq;
ese.ese_epsq = sq_nil;
ese.ese_hmvc = 0;
ese.ese_fmvn = 1;

EPDSetKings();

return;
}

/*--> EPDPlayerString: return a pointer to the player name string */
nonstatic
charptrT
EPDPlayerString(cT c)
{

return (playerstrv[c]);
}

/*--> EPDSANEncodeChar: encode SAN character */
static
void
EPDSANEncodeChar(char ch)
{
if ((lsani < (sanL - 1)) || ((ch == '\0') && (lsani < sanL)))
	lsan[lsani++] = ch;
else
	EPDFatal("EPDSANEncodeChar: overflow");

return;
}

/*--> EPDSANEncodeStr: encode a SAN string */
static
void
EPDSANEncodeStr(charptrT s)
{
charptrT p;

p = s;
while (*p)
	EPDSANEncodeChar(*p++);

return;
}

/*--> EPDSANEncodeFile: encode SAN file from square */
static
void
EPDSANEncodeFile(sqT sq)
{
EPDSANEncodeChar(ascfv[map_file(sq)]);

return;
}

/*--> EPDSANEncodeRank: encode SAN rank from square */
static
void
EPDSANEncodeRank(sqT sq)
{
EPDSANEncodeChar(ascrv[map_rank(sq)]);

return;
}

/*--> EPDSANEncodeSq: encode SAN square */
static
void
EPDSANEncodeSq(sqT sq)
{
EPDSANEncodeFile(sq);
EPDSANEncodeRank(sq);

return;
}

/*--> EPDSANEncodeCI: encode an appropriate capture indicator */
static
void
EPDSANEncodeCI(siT index)
{
switch (index)
	{
	case 0:
		EPDSANEncodeChar('x');
		break;

	case 1:
		break;

	case 2:
		EPDSANEncodeChar(':');
		break;

	case 3:
		EPDSANEncodeChar('*');
		break;

	case 4:
		EPDSANEncodeChar('-');
		break;
	};

return;
}

/*--> EPDSANEncodeAux: encode SAN format move with variants */
static
void
EPDSANEncodeAux(mptrT mptr, sanT san, ssavT ssav)
{
siT i;

/* reset local index */

lsani = 0;

/* busted? */

if (mptr->m_flag & mf_bust)
	EPDSANEncodeChar('*');

/* process according to moving piece */

switch (cv_p_cpv[mptr->m_frcp])
	{
	case p_p:
		switch (mptr->m_scmv)
			{
			case scmv_reg:
				if (mptr->m_tocp != cp_v0)
					{
					EPDSANEncodeFile(mptr->m_frsq);
					if (ssav[ssa_edcr] == 1)
						EPDSANEncodeRank(mptr->m_frsq);
					EPDSANEncodeCI(ssav[ssa_capt]);
					if (ssav[ssa_ptar] == 0)
						EPDSANEncodeSq(mptr->m_tosq);
					else
						EPDSANEncodeFile(mptr->m_tosq);
					}
				else
					{
					EPDSANEncodeFile(mptr->m_frsq);
					if (ssav[ssa_edcr] == 1)
						EPDSANEncodeRank(mptr->m_frsq);
					if (ssav[ssa_move] == 1)
						EPDSANEncodeChar('-');
					if (ssav[ssa_edcf] == 1)
						EPDSANEncodeFile(mptr->m_tosq);
					EPDSANEncodeRank(mptr->m_tosq);
					};
				break;

			case scmv_epc:
				EPDSANEncodeFile(mptr->m_frsq);
				if (ssav[ssa_edcr] == 1)
					EPDSANEncodeRank(mptr->m_frsq);
				EPDSANEncodeCI(ssav[ssa_capt]);
				if (ssav[ssa_ptar] == 0)
					EPDSANEncodeSq(mptr->m_tosq);
				else
					EPDSANEncodeFile(mptr->m_tosq);
				if (ssav[ssa_epct] == 1)
					EPDSANEncodeStr("ep");
				break;

			case scmv_ppn:
			case scmv_ppb:
			case scmv_ppr:
			case scmv_ppq:
				if (mptr->m_tocp != cp_v0)
					{
					EPDSANEncodeFile(mptr->m_frsq);
					if (ssav[ssa_edcr] == 1)
						EPDSANEncodeRank(mptr->m_frsq);
					EPDSANEncodeCI(ssav[ssa_capt]);
					if (ssav[ssa_ptar] == 0)
						EPDSANEncodeSq(mptr->m_tosq);
					else
						EPDSANEncodeFile(mptr->m_tosq);
					}
				else
					{
					EPDSANEncodeFile(mptr->m_frsq);
					if (ssav[ssa_edcr] == 1)
						EPDSANEncodeRank(mptr->m_frsq);
					if (ssav[ssa_move] == 1)
						EPDSANEncodeChar('-');
					if (ssav[ssa_edcf] == 1)
						EPDSANEncodeFile(mptr->m_tosq);
					EPDSANEncodeRank(mptr->m_tosq);
					};
				switch (ssav[ssa_prom])
					{
					case 0:
						EPDSANEncodeChar('=');
						EPDSANEncodeChar(ascpv[cv_p_scmvv[mptr->m_scmv]]);
						break;
					case 1:
						EPDSANEncodeChar(ascpv[cv_p_scmvv[mptr->m_scmv]]);
						break;
					case 2:
						EPDSANEncodeChar('/');
						EPDSANEncodeChar(ascpv[cv_p_scmvv[mptr->m_scmv]]);
						break;
					case 3:
						EPDSANEncodeChar('(');
						EPDSANEncodeChar(ascpv[cv_p_scmvv[mptr->m_scmv]]);
						EPDSANEncodeChar(')');
						break;
					};
				break;
			};
		break;

	case p_n:
	case p_b:
	case p_r:
	case p_q:
		EPDSANEncodeChar(ascpv[cv_p_cpv[mptr->m_frcp]]);

		if (((mptr->m_flag & mf_sanf) || (ssav[ssa_edcf] == 1)) ||
			((mptr->m_flag & mf_sanr) && (ssav[ssa_edcf] == 2)))
			EPDSANEncodeFile(mptr->m_frsq);

		if (((mptr->m_flag & mf_sanr) || (ssav[ssa_edcr] == 1)) ||
			((mptr->m_flag & mf_sanf) && (ssav[ssa_edcr] == 2)))
			EPDSANEncodeRank(mptr->m_frsq);

		if (mptr->m_tocp != cp_v0)
			EPDSANEncodeCI(ssav[ssa_capt]);
		else
			if (ssav[ssa_move] == 1)
				EPDSANEncodeChar('-');
		EPDSANEncodeSq(mptr->m_tosq);
		break;

	case p_k:
		switch (mptr->m_scmv)
			{
			case scmv_reg:
				EPDSANEncodeChar(ascpv[p_k]);
				if (ssav[ssa_edcf] == 1)
					EPDSANEncodeFile(mptr->m_frsq);
				if (ssav[ssa_edcr] == 1)
					EPDSANEncodeRank(mptr->m_frsq);
				if (mptr->m_tocp != cp_v0)
					EPDSANEncodeCI(ssav[ssa_capt]);
				else
					if (ssav[ssa_move] == 1)
						EPDSANEncodeChar('-');
				EPDSANEncodeSq(mptr->m_tosq);
				break;

			case scmv_cks:
				switch (ssav[ssa_cast])
					{
					case 0:
						EPDSANEncodeStr("O-O");
						break;
					case 1:
						EPDSANEncodeStr("0-0");
						break;
					case 2:
						EPDSANEncodeStr("OO");
						break;
					case 3:
						EPDSANEncodeStr("00");
						break;
					case 4:
						EPDSANEncodeChar(ascpv[p_k]);
						if (ssav[ssa_edcf] == 1)
							EPDSANEncodeFile(mptr->m_frsq);
						if (ssav[ssa_edcr] == 1)
							EPDSANEncodeRank(mptr->m_frsq);
						if (ssav[ssa_move] == 1)
							EPDSANEncodeChar('-');
						EPDSANEncodeSq(mptr->m_tosq);
						break;
					};
				break;

			case scmv_cqs:
				switch (ssav[ssa_cast])
					{
					case 0:
						EPDSANEncodeStr("O-O-O");
						break;
					case 1:
						EPDSANEncodeStr("0-0-0");
						break;
					case 2:
						EPDSANEncodeStr("OOO");
						break;
					case 3:
						EPDSANEncodeStr("000");
						break;
					case 4:
						EPDSANEncodeChar(ascpv[p_k]);
						if (ssav[ssa_edcf] == 1)
							EPDSANEncodeFile(mptr->m_frsq);
						if (ssav[ssa_edcr] == 1)
							EPDSANEncodeRank(mptr->m_frsq);
						if (ssav[ssa_move] == 1)
							EPDSANEncodeChar('-');
						EPDSANEncodeSq(mptr->m_tosq);
						break;
					};
				break;
			};
		break;
	};

/* insert markers */

if ((mptr->m_flag & mf_chec) && !(mptr->m_flag & mf_chmt))
	switch (ssav[ssa_chec])
		{
		case 0:
			EPDSANEncodeChar('+');
			break;
		case 1:
			break;
		case 2:
			EPDSANEncodeStr("ch");
			break;
		};

if (mptr->m_flag & mf_chmt)
	switch (ssav[ssa_chmt])
		{
		case 0:
			EPDSANEncodeChar('#');
			break;
		case 1:
			break;
		case 2:
			EPDSANEncodeChar('+');
			break;
		case 3:
			EPDSANEncodeStr("++");
			break;
		};

if (mptr->m_flag & mf_draw)
	if (ssav[ssa_draw] == 1)
		EPDSANEncodeChar('=');

/* map to lower case if indicated */

if (ssav[ssa_case] == 1)
	for (i = 0; i < lsani; i++)
		lsan[i] = map_lower(lsan[i]);

/* pad and copy */

while (lsani < sanL)
	EPDSANEncodeChar('\0');

for (i = 0; i < sanL; i++)
	san[i] = lsan[i];

return;
}

/*--> EPDSANEncode: encode a move into a SAN string */
nonstatic
void
EPDSANEncode(mptrT mptr, sanT san)
{
ssaT ssa;
ssavT ssav;

/* select canonical encoding (zero point in variant space) */

for (ssa = 0; ssa < ssaL; ssa++)
	ssav[ssa] = 0;
EPDSANEncodeAux(mptr, san, ssav);

return;
}

/*--> EPDSANDecodeBump: increment a style vector and return overflow */
static
siT
EPDSANDecodeBump(ssavT ssav, ssavT bssav)
{
siT flag;
ssaT ssa;

flag = 1;
ssa = 0;
while (flag && (ssa < ssaL))
	{
	flag = 0;
	ssav[ssa]++;
	if (ssav[ssa] == bssav[ssa])
		{
		flag = 1;
		ssav[ssa] = 0;
		};
	ssa++;
	};

return (flag);
}

/*--> EPDSANDecodeFlex: locate a move from SAN (flexible interpretation) */
static
mptrT
EPDSANDecodeFlex(sanT san)
{
mptrT mptr;
ssavT ssav, bssav;
siT i, flag;
mptrT rmptr;
sanT lcsan, rsan;

/* set default return value */

mptr = NULL;

/* set minimal upper bounds */

for (i = 0; i < ssaL; i++)
	bssav[i] = 1;

/* scan for upper bound conditions */

rmptr = tse.tse_base;
for (i = 0; i < tse.tse_count; i++)
	{
	/* letter case */

	bssav[ssa_case] = 2;

	/* capturing */

	if ((rmptr->m_tocp != cp_v0) || (rmptr->m_scmv == scmv_epc))
		bssav[ssa_capt] = 5;

	/* checking */

	if (rmptr->m_flag & mf_chec)
		bssav[ssa_chec] = 3;

	/* castling */

	if ((rmptr->m_scmv == scmv_cks) || (rmptr->m_scmv == scmv_cqs))
		bssav[ssa_cast] = 5;

	/* promoting */

	if ((rmptr->m_scmv == scmv_ppn) || (rmptr->m_scmv == scmv_ppb) ||
		(rmptr->m_scmv == scmv_ppr) || (rmptr->m_scmv == scmv_ppq))
		bssav[ssa_prom] = 4;

	/* pawn destination target */

	if (cv_p_cpv[rmptr->m_frcp] == p_p)
		bssav[ssa_ptar] = 2;

	/* checkmating */

	if (rmptr->m_flag & mf_chmt)
		bssav[ssa_chmt] = 4;

	/* en passant capturing */

	if (rmptr->m_scmv == scmv_epc)
		bssav[ssa_epct] = 2;

	/* drawing */

	if (rmptr->m_flag & mf_draw)
		bssav[ssa_draw] = 2;

	/* moving (non-capturing) */

	if ((rmptr->m_tocp == cp_v0) &&
		(rmptr->m_scmv != scmv_epc))
		bssav[ssa_move] = 2;

	/* extra disambiguation: file */

	if (!(rmptr->m_flag & mf_sanf))
		bssav[ssa_edcf] = 3;

	/* extra disambiguation: rank */

	if (!(rmptr->m_flag & mf_sanr))
		bssav[ssa_edcr] = 3;

	rmptr++;
	};

/* make a lower case copy of the input */

for (i = 0; i < sanL; i++)
	lcsan[i] = map_lower(san[i]);

/* initialize the index style vector */

for (i = 0; i < ssaL; i++)
	ssav[i] = 0;

/* search */

flag = 0;
while (!flag && (mptr == NULL))
	{
	rmptr = tse.tse_base;
	i = 0;

	/* scan candidate moves */

	while ((mptr == NULL) && (i < tse.tse_count))
		{
		/* encode the current style version of a candidate */

		EPDSANEncodeAux(rmptr, rsan, ssav);

		/* select either original or lower case comparison */

		if (ssav[ssa_case] == 0)
			{
			if (strcmp(san, rsan) == 0)
				mptr = rmptr;
			}
		else
			{
			if (strcmp(lcsan, rsan) == 0)
				mptr = rmptr;
			};

		/* next candidate */

		rmptr++;
		i++;
		};

	/* update the overflow termination flag	*/

	flag = EPDSANDecodeBump(ssav, bssav);
	};

return (mptr);
}

/*--> EPDSANDecode: locate a move from SAN (strict interpretation) */
static
mptrT
EPDSANDecode(sanT san)
{
mptrT mptr;
mptrT rmptr;
sanT rsan;
siT i;

/* set default return value */

mptr = NULL;

/* assume current moveset properly generated */

rmptr = tse.tse_base;
i = 0;

/* search */

while ((mptr == NULL) && (i < tse.tse_count))
	{
	EPDSANEncode(rmptr, rsan);
	if (strcmp(san, rsan) == 0)
		mptr = rmptr;
	else
		{
		rmptr++;
		i++;
		};
	};

return (mptr);
}

/*--> EPDSANDecodeAux: locate a move from SAN */
nonstatic
mptrT
EPDSANDecodeAux(sanT san, siT strict)
{
mptrT mptr;

if (strict)
	mptr = EPDSANDecode(san);
else
	mptr = EPDSANDecodeFlex(san);

return (mptr);
}

/*--> EPDAttack: determine if a color attacks a square */
static
siT
EPDAttack(cT c, sqT sq)
{
siT flag;
dxT dx;
dvT dv;
xdvT xdv;
sqptrT sqptr0, sqptr1;
xsqptrT xsqptr0, xsqptr1;

/* clear result */

flag = 0;

/* set origin square pointers  */

sqptr0 = &EPDboard.rbv[sq];
xsqptr0 = &xb.xbv[map_xsq_sq(sq)];

/* process according to specified color */

if (c == c_w)
	{
	/* pawn attacks */

	if ((*(xsqptr0 + xdv_7) == cp_v0) && (*(sqptr0 + dv_7) == cp_wp))
		flag = 1;
	else
		if ((*(xsqptr0 + xdv_6) == cp_v0) && (*(sqptr0 + dv_6) == cp_wp))
			flag = 1;

	/* knight attacks */

	if (!flag)
		{
		dx = dx_8;
		while (!flag && (dx <= dx_f))
			if ((*(xsqptr0 + xdvv[dx]) == cp_v0) &&
				(*(sqptr0 + dvv[dx]) == cp_wn))
				flag = 1;
			else
				dx++;
		};

	/* orthogonal sweeps */

	if (!flag)
		{
		dx = dx_0;
		while (!flag && (dx <= dx_3))
			{
			dv = dvv[dx];
			xdv = xdvv[dx];
			sqptr1 = sqptr0;
			xsqptr1 = xsqptr0;
			while ((*(xsqptr1 += xdv) == cp_v0) &&
				(*(sqptr1 += dv) == cp_v0))
				;
			if ((*xsqptr1 == cp_v0) &&
				((*sqptr1 == cp_wq) || (*sqptr1 == cp_wr)))
				flag = 1;
			else
				dx++;
			};
		};

	/* diagonal sweeps */

	if (!flag)
		{
		dx = dx_4;
		while (!flag && (dx <= dx_7))
			{
			dv = dvv[dx];
			xdv = xdvv[dx];
			sqptr1 = sqptr0;
			xsqptr1 = xsqptr0;
			while ((*(xsqptr1 += xdv) == cp_v0) &&
				(*(sqptr1 += dv) == cp_v0))
				;
			if ((*xsqptr1 == cp_v0) &&
				((*sqptr1 == cp_wq) || (*sqptr1 == cp_wb)))
				flag = 1;
			else
				dx++;
			};
		};

	/* king attacks */

	if (!flag)
		{
		dx = dx_0;
		while (!flag && (dx <= dx_7))
			if ((*(xsqptr0 + xdvv[dx]) == cp_v0) &&
				(*(sqptr0 + dvv[dx]) == cp_wk))
				flag = 1;
			else
				dx++;
		};
	}
else
	{
	/* pawn attacks */

	if ((*(xsqptr0 + xdv_4) == cp_v0) && (*(sqptr0 + dv_4) == cp_bp))
		flag = 1;
	else
		if ((*(xsqptr0 + xdv_5) == cp_v0) && (*(sqptr0 + dv_5) == cp_bp))
			flag = 1;

	/* knight attacks */

	if (!flag)
		{
		dx = dx_8;
		while (!flag && (dx <= dx_f))
			if ((*(xsqptr0 + xdvv[dx]) == cp_v0) &&
				(*(sqptr0 + dvv[dx]) == cp_bn))
				flag = 1;
			else
				dx++;
		};

	/* orthogonal sweeps */

	if (!flag)
		{
		dx = dx_0;
		while (!flag && (dx <= dx_3))
			{
			dv = dvv[dx];
			xdv = xdvv[dx];
			sqptr1 = sqptr0;
			xsqptr1 = xsqptr0;
			while ((*(xsqptr1 += xdv) == cp_v0) &&
				(*(sqptr1 += dv) == cp_v0))
				;
			if ((*xsqptr1 == cp_v0) &&
				((*sqptr1 == cp_bq) || (*sqptr1 == cp_br)))
				flag = 1;
			else
				dx++;
			};
		};

	/* diagonal sweeps */

	if (!flag)
		{
		dx = dx_4;
		while (!flag && (dx <= dx_7))
			{
			dv = dvv[dx];
			xdv = xdvv[dx];
			sqptr1 = sqptr0;
			xsqptr1 = xsqptr0;
			while ((*(xsqptr1 += xdv) == cp_v0) &&
				(*(sqptr1 += dv) == cp_v0))
				;
			if ((*xsqptr1 == cp_v0) &&
				((*sqptr1 == cp_bq) || (*sqptr1 == cp_bb)))
				flag = 1;
			else
				dx++;
			};
		};

	/* king attacks */

	if (!flag)
		{
		dx = dx_0;
		while (!flag && (dx <= dx_7))
			if ((*(xsqptr0 + xdvv[dx]) == cp_v0) &&
				(*(sqptr0 + dvv[dx]) == cp_bk))
				flag = 1;
			else
				dx++;
		};
	};

return (flag);
}

/*--> EPDWhiteAttacks: check if White attacks a square */
static
siT
EPDWhiteAttacks(sqT sq)
{

return (EPDAttack(c_w, sq));
}

/*--> EPDBlackAttacks: check if White attacks a square */
static
siT
EPDBlackAttacks(sqT sq)
{

return (EPDAttack(c_b, sq));
}

/*--> EPDTestAKIC: test for active king in check */
static
siT
EPDTestAKIC(void)
{
siT flag;

if (ese.ese_actc == c_w)
	flag = EPDBlackAttacks(ese.ese_ksqv[c_w]);
else
	flag = EPDWhiteAttacks(ese.ese_ksqv[c_b]);

return (flag);
}

/*--> EPDTestPKIC: test for passive king in check */
static
siT
EPDTestPKIC(void)
{
siT flag;

if (ese.ese_actc == c_b)
	flag = EPDBlackAttacks(ese.ese_ksqv[c_w]);
else
	flag = EPDWhiteAttacks(ese.ese_ksqv[c_b]);

return (flag);
}

/*--> EPDCensus: calculate local census vectors */
static
void
EPDCensus(void)
{
cT c;
pT p;
sqT sq;
cpT cp;

/* clear census vectors */

for (c = c_w; c <= c_b; c++)
	{
	count_cv[c] = 0;
	for (p = p_p; p <= p_k; p++)
		count_cpv[c][p] = 0;
	};

/* calculate census vectors */

for (sq = sq_a1; sq <= sq_h8; sq++)
	{
	cp = EPDboard.rbv[sq];
	if (cp != cp_v0)
		{
		c = cv_c_cpv[cp];
		count_cv[c]++;
		count_cpv[c][cv_p_cpv[cp]]++;
		};
	};

return;
}

/*--> EPDIsLegal: determine if current position is legal */
nonstatic
siT
EPDIsLegal(void)
{
siT flag;
cT c;
fileT file;
siT apv[rcL];

/* set default return value: legal position */

flag = 1;

/* calculate the local census vectors */

EPDCensus();

/* calculate available promoted pawns */

for (c = c_w; c <= c_b; c++)
	apv[c] = fileL - count_cpv[c][p_p];

/* check white pawn count */

if (flag && (count_cpv[c_w][p_p] > fileL))
	flag = 0;

/* check black pawn count */

if (flag && (count_cpv[c_b][p_p] > fileL))
	flag = 0;

/* check white knight count */

if (flag && (count_cpv[c_w][p_n] > 2))
	{
	apv[c_w] -= (count_cpv[c_w][p_n] - 2);
	if (apv[c_w] < 0)
		flag = 0;
	};

/* check black knight count */

if (flag && (count_cpv[c_b][p_n] > 2))
	{
	apv[c_b] -= (count_cpv[c_b][p_n] - 2);
	if (apv[c_b] < 0)
		flag = 0;
	};

/* check white bishop count */

if (flag && (count_cpv[c_w][p_b] > 2))
	{
	apv[c_w] -= (count_cpv[c_w][p_b] - 2);
	if (apv[c_w] < 0)
		flag = 0;
	};

/* check black bishop count */

if (flag && (count_cpv[c_b][p_b] > 2))
	{
	apv[c_b] -= (count_cpv[c_b][p_b] - 2);
	if (apv[c_b] < 0)
		flag = 0;
	};

/* check white rook count */

if (flag && (count_cpv[c_w][p_r] > 2))
	{
	apv[c_w] -= (count_cpv[c_w][p_r] - 2);
	if (apv[c_w] < 0)
		flag = 0;
	};

/* check black rook count */

if (flag && (count_cpv[c_b][p_r] > 2))
	{
	apv[c_b] -= (count_cpv[c_b][p_r] - 2);
	if (apv[c_b] < 0)
		flag = 0;
	};

/* check white queen count */

if (flag && (count_cpv[c_w][p_q] > 1))
	{
	apv[c_w] -= (count_cpv[c_w][p_q] - 1);
	if (apv[c_w] < 0)
		flag = 0;
	};

/* check black queen count */

if (flag && (count_cpv[c_b][p_q] > 1))
	{
	apv[c_b] -= (count_cpv[c_b][p_q] - 1);
	if (apv[c_b] < 0)
		flag = 0;
	};

/* check white king count */

if (flag && (count_cpv[c_w][p_k] != 1))
	flag = 0;

/* check black king count */

if (flag && (count_cpv[c_b][p_k] != 1))
	flag = 0;

/* check pawn placement */

if (flag)
	{
	file = file_a;
	while (flag && (file <= file_h))
		if ((EPDboard.rbm[rank_1][file] == cp_wp) ||
			(EPDboard.rbm[rank_1][file] == cp_bp) ||
			(EPDboard.rbm[rank_8][file] == cp_wp) ||
			(EPDboard.rbm[rank_8][file] == cp_bp))
			flag = 0;
		else
			file++;
	};

/* check white kingside castling availability */

if (flag && (ese.ese_cast & cf_wk))
	if ((EPDboard.rbv[sq_e1] != cp_wk) || (EPDboard.rbv[sq_h1] != cp_wr))
		flag = 0;

/* check white queenside castling availability */

if (flag && (ese.ese_cast & cf_wq))
	if ((EPDboard.rbv[sq_e1] != cp_wk) || (EPDboard.rbv[sq_a1] != cp_wr))
		flag = 0;

/* check black kingside castling availability */

if (flag && (ese.ese_cast & cf_bk))
	if ((EPDboard.rbv[sq_e8] != cp_bk) || (EPDboard.rbv[sq_h8] != cp_br))
		flag = 0;

/* check black queenside castling availability */

if (flag && (ese.ese_cast & cf_bq))
	if ((EPDboard.rbv[sq_e8] != cp_bk) || (EPDboard.rbv[sq_a8] != cp_br))
		flag = 0;

/* check en passant target square */

if (flag && (ese.ese_epsq != sq_nil)) {
	if (ese.ese_actc == c_w)
		{
		if (map_rank(ese.ese_epsq) != rank_6)
			flag = 0;
		else
			if (EPDboard.rbv[ese.ese_epsq + dv_3] != cp_bp)
				flag = 0;
			else
				if (EPDboard.rbv[ese.ese_epsq] != cp_v0)
					flag = 0;
				else
					if (EPDboard.rbv[ese.ese_epsq + dv_1] != cp_v0)
						flag = 0;
		}
	else
		{
		if (map_rank(ese.ese_epsq) != rank_3)
			flag = 0;
		else
			if (EPDboard.rbv[ese.ese_epsq + dv_1] != cp_wp)
				flag = 0;
			else
				if (EPDboard.rbv[ese.ese_epsq] != cp_v0)
					flag = 0;
				else
					if (EPDboard.rbv[ese.ese_epsq + dv_3] != cp_v0)
						flag = 0;
		}
}

/* check for passive king in check */

if (flag && EPDTestPKIC())
	flag = 0;

return (flag);
}

/*--> EPDIsCheckmate: test for checkmate status */
nonstatic
siT
EPDIsCheckmate(void)
{
siT flag;

/* set default return value: no checkmate */

flag = 0;

/* generate legal moves (assume legal position) */

EPDGenMoves();

/* no legal moves and in check? */

if ((tse.tse_count == 0) && EPDTestAKIC())
	flag = 1;

return (flag);
}

/*--> EPDIsStalemate: test for stalemate status */
nonstatic
siT
EPDIsStalemate(void)
{
siT flag;

/* set default return value: no stalemate */

flag = 0;

/* generate legal moves (assume legal position) */

EPDGenMoves();

/* no legal moves and not in check? */

if ((tse.tse_count == 0) && !EPDTestAKIC())
	flag = 1;

return (flag);
}

/*--> EPDIsInsufficientMaterial: test for insufficient material */
nonstatic
siT
EPDIsInsufficientMaterial(void)
{
siT flag;

/* set default return value: no draw by insufficient material */

flag = 0;

/* calculate local census (assume legal position) */

EPDCensus();

/* only K vs K, K+N vs K, and K+B vs K are considered draws here */

if ((count_cv[c_w] == 1) && (count_cv[c_b] == 1))
	flag = 1;
else
	if ((count_cv[c_w] == 2) && (count_cv[c_b] == 1) &&
		((count_cpv[c_w][p_n] == 1) || (count_cpv[c_w][p_b] == 1)))
		flag = 1;
	else
		if ((count_cv[c_b] == 2) && (count_cv[c_w] == 1) &&
			((count_cpv[c_b][p_n] == 1) || (count_cpv[c_b][p_b] == 1)))
			flag = 1;

return (flag);
}

/*--> EPDIsFiftyMoveDraw: test for 50 move draw */
nonstatic
siT
EPDIsFiftyMoveDraw(void)
{
siT flag;

if (ese.ese_hmvc >= 100)
	flag = 1;
else
	flag = 0;

return (flag);
}

/*--> EPDIsThirdRepetition: test for third repetition */
nonstatic
siT
EPDIsThirdRepetition(gamptrT gamptr)
{
siT flag;
gpmptrT gpmptr;
siT count, limit, index, match;
sqT sq;
cpT cp0, cp1;
nbvT nbv;

/* point to the last game played move record */

gpmptr = gamptr->gam_tailgpm;

/* set the repetion count (current position counts as one) */

count = 1;

/* set the limit on the number of records to check */

limit = ese.ese_hmvc;

/* construct the nybble board for the current position */

for (sq = sq_a1; sq <= sq_h8; sq += 2)
	{
	cp0 = EPDboard.rbv[sq + 0];
	cp1 = EPDboard.rbv[sq + 1];
	nbv[sq >> 1] = ((cp1 << nybbW) | cp0);
	};

/* loop backwards */

while ((gpmptr != NULL) && (limit > 0) && (count < 3))
	{
	/* check for match against current position */

	if ((ese.ese_actc == gpmptr->gpm_ese.ese_actc) &&
		(ese.ese_cast == gpmptr->gpm_ese.ese_cast) &&
		(ese.ese_epsq == gpmptr->gpm_ese.ese_epsq))
		{
		/* scalars matched, check for board match */

		match = 1;
		index = 0;
		while (match && (index < nbL))
			{
			if (nbv[index] == gpmptr->gpm_nbv[index])
				index++;
			else
				match = 0;
			};

		/* complete match? */

		if (match)
			count++;
		};

	/* retreat to previous played move record */

	gpmptr = gpmptr->gpm_prev;

	/* one less to go */

	limit--;
	};

/* set return value */

if (count == 3)
	flag = 1;
else
	flag = 0;

return (flag);
}

/*--> EPDIsDraw: determine if the current position is a draw */
nonstatic
siT
EPDIsDraw(gamptrT gamptr)
{
siT flag;

if (EPDIsFiftyMoveDraw() ||
	EPDIsInsufficientMaterial() ||
	EPDIsStalemate() ||
	EPDIsThirdRepetition(gamptr))
	flag = 1;
else
	flag = 0;

return (flag);
}

/*--> EPDMateInOne: return a mating move if one exists */
nonstatic
mptrT
EPDMateInOne(void)
{
mptrT mptr;
mptrT rmptr;
siT index;

/* set default return value (no mate in one) */

mptr = NULL;

/* generate legal moves (assume legal position) */

EPDGenMoves();

/* try to locate a mating move */

rmptr = tse.tse_base;
index = 0;
while ((mptr == NULL) && (index < tse.tse_count))
	if (rmptr->m_flag & mf_chmt)
		{
		ret_m = *rmptr;
		mptr = &ret_m;
		}
	else
		{
		rmptr++;
		index++;
		};

return (mptr);
}

/*--> EPDGeneratePL: generate psuedolegal moves */
static
void
EPDGeneratePL(void)
{
dxT dx;
dvT dv;
xdvT xdv;
xsqptrT xsqptr0, xsqptr1;
fileT frfile;
rankT frrank;
mT gen_m;

/* set up the generation base */

if (ply == 0)
	treeptr = tse.tse_base = treebaseptr;
else
	treeptr = tse.tse_base =
		(tseptr - 1)->tse_base + (tseptr - 1)->tse_count;

/* test against safety margin */

if ((treeptr - treebaseptr) >= (treeL - treemarginL))
	EPDFatal("EPDGeneratePL: move tree size safety limit exceeded");

/* set up current generation items */

tse.tse_curr = treeptr;
tse.tse_count = 0;

/* set the psuedoinvariant generated move template components */

gen_m.m_scmv = scmv_reg;
gen_m.m_flag = 0;

/* look at each origin square of the active color */

for (gen_m.m_frsq = sq_a1; gen_m.m_frsq <= sq_h8; gen_m.m_frsq++)
	{
	/* get origin square and moving piece */

	gen_m.m_frcp = EPDboard.rbv[gen_m.m_frsq];

	/* continue if it is an active piece */

	if (cv_c_cpv[gen_m.m_frcp] == ese.ese_actc)
		{

		/* generate moves for active color piece */

		xsqptr0 = &xb.xbv[map_xsq_sq(gen_m.m_frsq)];
		switch (cv_p_cpv[gen_m.m_frcp])
			{
			case p_p:
				/* pawn moves: a bit tricky; colors done separately */

				frfile = map_file(gen_m.m_frsq);
				frrank = map_rank(gen_m.m_frsq);
				if (ese.ese_actc == c_w)
					{
					/* one square non-capture */

					gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq = gen_m.m_frsq + dv_1];
					if (gen_m.m_tocp == cp_v0) {
						if (frrank != rank_7)
							{
							/* non-promotion */

							*treeptr++ = gen_m;
							tse.tse_count++;
							}
						else
							{
							/* promotion */

							for (gen_m.m_scmv = scmv_ppn;
								gen_m.m_scmv <= scmv_ppq; gen_m.m_scmv++)
								{
								*treeptr++ = gen_m;
								tse.tse_count++;
								}
							gen_m.m_scmv = scmv_reg;
							};
                                        }

					/* two squares forward */

					if ((frrank == rank_2) &&
						Vacant(gen_m.m_frsq + dv_1) &&
						Vacant(gen_m.m_frsq + (2 * dv_1)))
						{
						gen_m.m_tosq = gen_m.m_frsq + (2 * dv_1);
						gen_m.m_tocp = cp_v0;
						*treeptr++ = gen_m;
						tse.tse_count++;
						};

					/* capture to left */

					if (frfile != file_a)
						{
						gen_m.m_tosq = gen_m.m_frsq + dv_5;
						gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq];
						if (cv_c_cpv[gen_m.m_tocp] == inv_cv[ese.ese_actc]) {
							if (frrank != rank_7)
								{
								/* non-promote */

								*treeptr++ = gen_m;
								tse.tse_count++;
								}
							else
								{
								/* promote */

								for (gen_m.m_scmv = scmv_ppn;
									gen_m.m_scmv <= scmv_ppq; gen_m.m_scmv++)
									{
									*treeptr++ = gen_m;
									tse.tse_count++;
									};
								gen_m.m_scmv = scmv_reg;
								};
                                           }
						};

					/* capture to right */

					if (frfile != file_h)
						{
						gen_m.m_tosq = gen_m.m_frsq + dv_4;
						gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq];
						if (cv_c_cpv[gen_m.m_tocp] == inv_cv[ese.ese_actc]) {
							if (frrank != rank_7)
								{
								/* non-promote */

								*treeptr++ = gen_m;
								tse.tse_count++;
								}
							else
								{
								/* promote */

								for (gen_m.m_scmv = scmv_ppn;
									gen_m.m_scmv <= scmv_ppq; gen_m.m_scmv++)
									{
									*treeptr++ = gen_m;
									tse.tse_count++;
									};
								gen_m.m_scmv = scmv_reg;
								};
                                                         }
						};

					/* en passant */

					if ((frrank == rank_5) && (ese.ese_epsq != sq_nil))
						{
						/* capture to left */

						if ((frfile != file_a) &&
							((gen_m.m_tosq = gen_m.m_frsq + dv_5) ==
							ese.ese_epsq))
							{
							gen_m.m_tocp = cp_v0;
							gen_m.m_scmv = scmv_epc;
							*treeptr++ = gen_m;
							tse.tse_count++;
							gen_m.m_scmv = scmv_reg;
							};

						/* capture to right */

						if ((frfile != file_h) &&
							((gen_m.m_tosq = gen_m.m_frsq + dv_4) ==
							ese.ese_epsq))
							{
							gen_m.m_tocp = cp_v0;
							gen_m.m_scmv = scmv_epc;
							*treeptr++ = gen_m;
							tse.tse_count++;
							gen_m.m_scmv = scmv_reg;
							};
						};
					}
				else
					{
					/* one square non-capture */

					gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq = gen_m.m_frsq + dv_3];
					if (gen_m.m_tocp == cp_v0) {
						if (frrank != rank_2)
							{
							/* non-promotion */

							*treeptr++ = gen_m;
							tse.tse_count++;
							}
						else
							{
							/* promotion */

							for (gen_m.m_scmv = scmv_ppn;
								gen_m.m_scmv <= scmv_ppq; gen_m.m_scmv++)
								{
								*treeptr++ = gen_m;
								tse.tse_count++;
								}
							gen_m.m_scmv = scmv_reg;
							};
                                        }

					/* two squares forward */

					if ((frrank == rank_7) &&
						Vacant(gen_m.m_frsq + dv_3) &&
						Vacant(gen_m.m_frsq + (2 * dv_3)))
						{
						gen_m.m_tosq = gen_m.m_frsq + (2 * dv_3);
						gen_m.m_tocp = cp_v0;
						*treeptr++ = gen_m;
						tse.tse_count++;
						};

					/* capture to left */

					if (frfile != file_a)
						{
						gen_m.m_tosq = gen_m.m_frsq + dv_6;
						gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq];
						if (cv_c_cpv[gen_m.m_tocp] == inv_cv[ese.ese_actc]) {
							if (frrank != rank_2)
								{
								/* non-promote */

								*treeptr++ = gen_m;
								tse.tse_count++;
								}
							else
								{
								/* promote */

								for (gen_m.m_scmv = scmv_ppn;
									gen_m.m_scmv <= scmv_ppq; gen_m.m_scmv++)
									{
									*treeptr++ = gen_m;
									tse.tse_count++;
									};
								gen_m.m_scmv = scmv_reg;
								};
                                                }
						};

					/* capture to right */

					if (frfile != file_h)
						{
						gen_m.m_tosq = gen_m.m_frsq + dv_7;
						gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq];
						if (cv_c_cpv[gen_m.m_tocp] == inv_cv[ese.ese_actc]) {
							if (frrank != rank_2)
								{
								/* non-promote */

								*treeptr++ = gen_m;
								tse.tse_count++;
								}
							else
								{
								/* promote */

								for (gen_m.m_scmv = scmv_ppn;
									gen_m.m_scmv <= scmv_ppq; gen_m.m_scmv++)
									{
									*treeptr++ = gen_m;
									tse.tse_count++;
									};
								gen_m.m_scmv = scmv_reg;
								};
                                                       }
						};

					/* en passant */

					if ((frrank == rank_4) && (ese.ese_epsq != sq_nil))
						{
						/* capture to left */

						if ((frfile != file_a) &&
							((gen_m.m_tosq = gen_m.m_frsq + dv_6) ==
							ese.ese_epsq))
							{
							gen_m.m_tocp = cp_v0;
							gen_m.m_scmv = scmv_epc;
							*treeptr++ = gen_m;
							tse.tse_count++;
							gen_m.m_scmv = scmv_reg;
							};

						/* capture to right */

						if ((frfile != file_h) &&
							((gen_m.m_tosq = gen_m.m_frsq + dv_7) ==
							ese.ese_epsq))
							{
							gen_m.m_tocp = cp_v0;
							gen_m.m_scmv = scmv_epc;
							*treeptr++ = gen_m;
							tse.tse_count++;
							gen_m.m_scmv = scmv_reg;
							};
						};
					};
				break;

			case p_n:
				/* knight moves: very simple */

				for (dx = dx_8; dx <= dx_f; dx++)
					if (*(xsqptr0 + xdvv[dx]) == cp_v0)
						{
						gen_m.m_tocp =
							EPDboard.rbv[gen_m.m_tosq = gen_m.m_frsq + dvv[dx]];
						if (cv_c_cpv[gen_m.m_tocp] != ese.ese_actc)
							{
							*treeptr++ = gen_m;
							tse.tse_count++;
							};
						};
				break;

			case p_b:
				/* bishop moves: diagonal sweeper */

				for (dx = dx_4; dx <= dx_7; dx++)
					{
					dv = dvv[dx];
					xdv = xdvv[dx];
					gen_m.m_tosq = gen_m.m_frsq;
					xsqptr1 = xsqptr0;
					while ((*(xsqptr1 += xdv) == cp_v0) &&
						((gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq += dv]) ==
						cp_v0))
						{
						*treeptr++ = gen_m;
						tse.tse_count++;
						};
					if ((*xsqptr1 == cp_v0) &&
						(cv_c_cpv[gen_m.m_tocp] == inv_cv[ese.ese_actc]))
						{
						*treeptr++ = gen_m;
						tse.tse_count++;
						};
					};
				break;

			case p_r:
				/* rook moves: orthogonal sweeper */

				for (dx = dx_0; dx <= dx_3; dx++)
					{
					dv = dvv[dx];
					xdv = xdvv[dx];
					gen_m.m_tosq = gen_m.m_frsq;
					xsqptr1 = xsqptr0;
					while ((*(xsqptr1 += xdv) == cp_v0) &&
						((gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq += dv]) ==
						cp_v0))
						{
						*treeptr++ = gen_m;
						tse.tse_count++;
						};
					if ((*xsqptr1 == cp_v0) &&
						(cv_c_cpv[gen_m.m_tocp] == inv_cv[ese.ese_actc]))
						{
						*treeptr++ = gen_m;
						tse.tse_count++;
						};
					};
				break;

			case p_q:
				/* queen moves: orthogonal and diagonal sweeper */

				for (dx = dx_0; dx <= dx_7; dx++)
					{
					dv = dvv[dx];
					xdv = xdvv[dx];
					gen_m.m_tosq = gen_m.m_frsq;
					xsqptr1 = xsqptr0;
					while ((*(xsqptr1 += xdv) == cp_v0) &&
						((gen_m.m_tocp = EPDboard.rbv[gen_m.m_tosq += dv]) ==
						cp_v0))
						{
						*treeptr++ = gen_m;
						tse.tse_count++;
						};
					if ((*xsqptr1 == cp_v0) &&
						(cv_c_cpv[gen_m.m_tocp] == inv_cv[ese.ese_actc]))
						{
						*treeptr++ = gen_m;
						tse.tse_count++;
						};
					};
				break;

			case p_k:
				/* king moves: one square adjacent regular */

				for (dx = dx_0; dx <= dx_7; dx++)
					if (*(xsqptr0 + xdvv[dx]) == cp_v0)
						{
						gen_m.m_tocp =
							EPDboard.rbv[gen_m.m_tosq = gen_m.m_frsq + dvv[dx]];
						if (cv_c_cpv[gen_m.m_tocp] != ese.ese_actc)
							{
							*treeptr++ = gen_m;
							tse.tse_count++;
							};
						};

				/* castling; process according to active color */

				if (ese.ese_actc == c_w)
					{
					if ((ese.ese_cast & cf_wk) && !EPDBlackAttacks(sq_e1) &&
						Vacant(sq_f1) && !EPDBlackAttacks(sq_f1) &&
						Vacant(sq_g1) && !EPDBlackAttacks(sq_g1))
						{
						gen_m.m_tosq = sq_g1;
						gen_m.m_tocp = cp_v0;
						gen_m.m_scmv = scmv_cks;
						*treeptr++ = gen_m;
						tse.tse_count++;
						gen_m.m_scmv = scmv_reg;
						};

					if ((ese.ese_cast & cf_wq) && !EPDBlackAttacks(sq_e1) &&
						Vacant(sq_d1) && !EPDBlackAttacks(sq_d1) &&
						Vacant(sq_c1) && !EPDBlackAttacks(sq_c1) &&
						Vacant(sq_b1))
						{
						gen_m.m_tosq = sq_c1;
						gen_m.m_tocp = cp_v0;
						gen_m.m_scmv = scmv_cqs;
						*treeptr++ = gen_m;
						tse.tse_count++;
						gen_m.m_scmv = scmv_reg;
						};
					}
				else
					{
					if ((ese.ese_cast & cf_bk) && !EPDWhiteAttacks(sq_e8) &&
						Vacant(sq_f8) && !EPDWhiteAttacks(sq_f8) &&
						Vacant(sq_g8) && !EPDWhiteAttacks(sq_g8))
						{
						gen_m.m_tosq = sq_g8;
						gen_m.m_tocp = cp_v0;
						gen_m.m_scmv = scmv_cks;
						*treeptr++ = gen_m;
						tse.tse_count++;
						gen_m.m_scmv = scmv_reg;
						};

					if ((ese.ese_cast & cf_bq) && !EPDWhiteAttacks(sq_e8) &&
						Vacant(sq_d8) && !EPDWhiteAttacks(sq_d8) &&
						Vacant(sq_c8) && !EPDWhiteAttacks(sq_c8) &&
						Vacant(sq_b8))
						{
						gen_m.m_tosq = sq_c8;
						gen_m.m_tocp = cp_v0;
						gen_m.m_scmv = scmv_cqs;
						*treeptr++ = gen_m;
						tse.tse_count++;
						gen_m.m_scmv = scmv_reg;
						};
					};
				break;
			};
		};
	};

return;
}

/*--> EPDSameMoveRef: check if two move references are the same move */
static
siT
EPDSameMoveRef(mptrT mptr0, mptrT mptr1)
{
siT flag;

if ((mptr0->m_tosq == mptr1->m_tosq) &&
	(mptr0->m_frsq == mptr1->m_frsq) &&
	(mptr0->m_frcp == mptr1->m_frcp) &&
	(mptr0->m_tocp == mptr1->m_tocp) &&
	(mptr0->m_scmv == mptr1->m_scmv))
	flag = 1;
else
	flag = 0;

return (flag);
}

/*--> EPDFindMove: locate the move in the current generation set */
static
mptrT
EPDFindMove(mptrT mptr)
{
mptrT rmptr;
siT flag;
siT index;

rmptr = tse.tse_base;
flag = 0;
index = 0;

while (!flag && (index < tse.tse_count))
	if (EPDSameMoveRef(mptr, rmptr))
		flag = 1;
	else
		{
		rmptr++;
		index++;
		};

if (!flag)
	rmptr = NULL;

return (rmptr);
}

/*--> EPDExecute: execute the supplied move */
static
void
EPDExecute(mptrT mptr)
{
sqT pcsq;
cpT ppcp;

/* test for overflow */

if (ply == (pmhL - 1))
	EPDFatal("EPDExecute: played move history overflow");

/* save old environment and generation records */

*eseptr++ = ese;
*tseptr++ = tse;

/* set the legality tested flag */

mptr->m_flag |= mf_exec;

/* process according to move case */

switch (mptr->m_scmv)
	{
	case scmv_reg:
		EPDboard.rbv[mptr->m_frsq] = cp_v0;
		EPDboard.rbv[mptr->m_tosq] = mptr->m_frcp;
		break;

	case scmv_epc:
		if (ese.ese_actc == c_w)
			pcsq = mptr->m_tosq + dv_3;
		else
			pcsq = mptr->m_tosq + dv_1;
		EPDboard.rbv[mptr->m_frsq] = cp_v0;
		EPDboard.rbv[mptr->m_tosq] = mptr->m_frcp;
		EPDboard.rbv[pcsq] = cp_v0;
		break;

	case scmv_cks:
		if (ese.ese_actc == c_w)
			{
			EPDboard.rbv[sq_e1] = cp_v0;
			EPDboard.rbv[sq_g1] = cp_wk;
			EPDboard.rbv[sq_h1] = cp_v0;
			EPDboard.rbv[sq_f1] = cp_wr;
			}
		else
			{
			EPDboard.rbv[sq_e8] = cp_v0;
			EPDboard.rbv[sq_g8] = cp_bk;
			EPDboard.rbv[sq_h8] = cp_v0;
			EPDboard.rbv[sq_f8] = cp_br;
			};
		break;

	case scmv_cqs:
		if (ese.ese_actc == c_w)
			{
			EPDboard.rbv[sq_e1] = cp_v0;
			EPDboard.rbv[sq_c1] = cp_wk;
			EPDboard.rbv[sq_a1] = cp_v0;
			EPDboard.rbv[sq_d1] = cp_wr;
			}
		else
			{
			EPDboard.rbv[sq_e8] = cp_v0;
			EPDboard.rbv[sq_c8] = cp_bk;
			EPDboard.rbv[sq_a8] = cp_v0;
			EPDboard.rbv[sq_d8] = cp_br;
			};
		break;

	case scmv_ppn:
		if (ese.ese_actc == c_w)
			ppcp = cp_wn;
		else
			ppcp = cp_bn;
		EPDboard.rbv[mptr->m_frsq] = cp_v0;
		EPDboard.rbv[mptr->m_tosq] = ppcp;
		break;

	case scmv_ppb:
		if (ese.ese_actc == c_w)
			ppcp = cp_wb;
		else
			ppcp = cp_bb;
		EPDboard.rbv[mptr->m_frsq] = cp_v0;
		EPDboard.rbv[mptr->m_tosq] = ppcp;
		break;

	case scmv_ppr:
		if (ese.ese_actc == c_w)
			ppcp = cp_wr;
		else
			ppcp = cp_br;
		EPDboard.rbv[mptr->m_frsq] = cp_v0;
		EPDboard.rbv[mptr->m_tosq] = ppcp;
		break;

	case scmv_ppq:
		if (ese.ese_actc == c_w)
			ppcp = cp_wq;
		else
			ppcp = cp_bq;
		EPDboard.rbv[mptr->m_frsq] = cp_v0;
		EPDboard.rbv[mptr->m_tosq] = ppcp;
		break;
	};

/* set values for updated environment record: active color */

ese.ese_actc = inv_cv[ese.ese_actc];

/* set values for updated environment record: castling availablity */

if (ese.ese_cast != 0)
	{
	if (ese.ese_cast & cf_wk)
		if ((mptr->m_frsq == sq_e1) || (mptr->m_frsq == sq_h1) ||
			(mptr->m_tosq == sq_h1))
			ese.ese_cast &= ~cf_wk;

	if (ese.ese_cast & cf_wq)
		if ((mptr->m_frsq == sq_e1) || (mptr->m_frsq == sq_a1) ||
			(mptr->m_tosq == sq_a1))
			ese.ese_cast &= ~cf_wq;

	if (ese.ese_cast & cf_bk)
		if ((mptr->m_frsq == sq_e8) || (mptr->m_frsq == sq_h8) ||
			(mptr->m_tosq == sq_h8))
			ese.ese_cast &= ~cf_bk;

	if (ese.ese_cast & cf_bq)
		if ((mptr->m_frsq == sq_e8) || (mptr->m_frsq == sq_a8) ||
			(mptr->m_tosq == sq_a8))
			ese.ese_cast &= ~cf_bq;
	};

/* set values for updated environment record: en passant */

if (ese.ese_actc == c_b) {
	if ((mptr->m_frcp == cp_wp) &&
		(map_rank(mptr->m_frsq) == rank_2) &&
		(map_rank(mptr->m_tosq) == rank_4))
		ese.ese_epsq = mptr->m_frsq + dv_1;
	else
		ese.ese_epsq = sq_nil;
}
else {
	if ((mptr->m_frcp == cp_bp) &&
		(map_rank(mptr->m_frsq) == rank_7) &&
		(map_rank(mptr->m_tosq) == rank_5))
		ese.ese_epsq = mptr->m_frsq + dv_3;
	else
		ese.ese_epsq = sq_nil;
}

/* set values for updated environment record: halfmove clock */

if ((mptr->m_tocp != cp_v0) || (cv_p_cpv[mptr->m_frcp] == p_p))
	ese.ese_hmvc = 0;
else
	ese.ese_hmvc++;

/* set values for updated environment record: fullmove number */

if (ese.ese_actc == c_w)
	ese.ese_fmvn++;

/* set values for updated environment record: king locations */

switch (mptr->m_frcp)
	{
	case cp_wk:
		ese.ese_ksqv[c_w] = mptr->m_tosq;
		break;
	case cp_bk:
		ese.ese_ksqv[c_b] = mptr->m_tosq;
		break;
	default:
		break;
	};

/* check/bust flags */

if (EPDTestAKIC())
	mptr->m_flag |= mf_chec;
if (EPDTestPKIC())
	mptr->m_flag |= mf_bust;

/* increment ply index */

ply++;

return;
}

/*--> EPDExecuteUpdate: update the current move pointer, then execute */
nonstatic
void
EPDExecuteUpdate(mptrT mptr)
{
tse.tse_curr = EPDFindMove(mptr);
if (tse.tse_curr == NULL)
	EPDFatal("EPDExecuteUpdate: can't find move");

EPDExecute(tse.tse_curr);

return;
}

/*--> EPDRetract: retract the supplied move */
static
void
EPDRetract(mptrT mptr)
{
/* decrement ply */

ply--;

/* restore the current environment and generation */

ese = *--eseptr;
tse = *--tseptr;

/* process by move case */

switch (mptr->m_scmv)
	{
	case scmv_reg:
		EPDboard.rbv[mptr->m_tosq] = mptr->m_tocp;
		EPDboard.rbv[mptr->m_frsq] = mptr->m_frcp;
		break;

	case scmv_epc:
		EPDboard.rbv[mptr->m_tosq] = cp_v0;
		EPDboard.rbv[mptr->m_frsq] = mptr->m_frcp;
		if (ese.ese_actc == c_w)
			EPDboard.rbv[mptr->m_tosq + dv_3] = cp_bp;
		else
			EPDboard.rbv[mptr->m_tosq + dv_1] = cp_wp;
		break;

	case scmv_cks:
		if (ese.ese_actc == c_w)
			{
			EPDboard.rbv[sq_g1] = cp_v0;
			EPDboard.rbv[sq_e1] = cp_wk;
			EPDboard.rbv[sq_f1] = cp_v0;
			EPDboard.rbv[sq_h1] = cp_wr;
			}
		else
			{
			EPDboard.rbv[sq_g8] = cp_v0;
			EPDboard.rbv[sq_e8] = cp_bk;
			EPDboard.rbv[sq_f8] = cp_v0;
			EPDboard.rbv[sq_h8] = cp_br;
			}
		break;

	case scmv_cqs:
		if (ese.ese_actc == c_w)
			{
			EPDboard.rbv[sq_c1] = cp_v0;
			EPDboard.rbv[sq_e1] = cp_wk;
			EPDboard.rbv[sq_d1] = cp_v0;
			EPDboard.rbv[sq_a1] = cp_wr;
			}
		else
			{
			EPDboard.rbv[sq_c8] = cp_v0;
			EPDboard.rbv[sq_e8] = cp_bk;
			EPDboard.rbv[sq_d8] = cp_v0;
			EPDboard.rbv[sq_a8] = cp_br;
			};
		break;

	case scmv_ppn:
	case scmv_ppb:
	case scmv_ppr:
	case scmv_ppq:
		EPDboard.rbv[mptr->m_tosq] = mptr->m_tocp;
		EPDboard.rbv[mptr->m_frsq] = mptr->m_frcp;
		break;
	};

return;
}

/*--> EPDRetractUpdate: retract last executed move */
nonstatic
void
EPDRetractUpdate(void)
{
mptrT mptr;

mptr = (tseptr - 1)->tse_curr;
EPDRetract(mptr);

return;
}

/*--> EPDRetractAll: retract all moves in current variation */
nonstatic
void
EPDRetractAll(void)
{
while (ply > 0)
	EPDRetractUpdate();

return;
}

/*--> EPDCollapse: collapse the played move stack */
nonstatic
void
EPDCollapse(void)
{
/* process for nonzero ply */

if (ply > 0)
	{
	/* reset the stack pointers */

	treeptr = treebaseptr;
	eseptr = esebaseptr;
	tseptr = tsebaseptr;

	/* reset the ply */

	ply = 0;
	};

return;
}

/*--> EPDReset: collapse, then reset starting position */
nonstatic
void
EPDReset(void)
{
EPDCollapse();
EPDInitArray();

return;
}

/*--> EPDMLExec: execute the current move list */
static
void
EPDMLExec(void)
{
siT i;
mptrT mptr;

/* test and mark each move for legality and checking status */

mptr = tse.tse_base;
for (i = 0; i < tse.tse_count; i++)
	{
	tse.tse_curr = mptr;
	EPDExecute(mptr);
	EPDRetract(mptr);
	mptr++;
	};

return;
}

/*--> EPDMLPolice: remove illegal moves the current move list */
static
void
EPDMLPolice(void)
{
mptrT tptr, mptr;
siT i, bust;
mT t_m;

/* move illegal moves to end of list */

mptr = tse.tse_base;
bust = 0;
i = 0;
while (i < (tse.tse_count - bust))
	if (mptr->m_flag & mf_bust)
		{
		tptr = (tse.tse_base + (tse.tse_count - 1)) - bust;
		t_m = *mptr;
		*mptr = *tptr;
		*tptr = t_m;
		bust++;
		}
	else
		{
		mptr++;
		i++;
		};

/* shrink */

tse.tse_count -= bust;

return;
}

/*--> EPDMLDisambiguate: insert disambiguation flags in move list */
static
void
EPDMLDisambiguate(void)
{
siT i, j, tmc, rmc, fmc;
mptrT mptr0, mptr1;
pT p;
rankT rank;
fileT file;

/* it's magic */

mptr0 = tse.tse_base;
for (i = 0; i < tse.tse_count; i++)
	{
	/* the outer loop disambiguates a single move per cycle */

	p = cv_p_cpv[mptr0->m_frcp];
	if ((p != p_p) && (p != p_k))
		{
		rank = map_rank(mptr0->m_frsq);
		file = map_file(mptr0->m_frsq);
		tmc = rmc = fmc = 0;
		mptr1 = tse.tse_base;
		for (j = 0; j < tse.tse_count; j++)
			{
			/* the inner loop examines all possible sibling puns */

			if ((i != j) && (mptr0->m_frcp == mptr1->m_frcp) &&
				(mptr0->m_tosq == mptr1->m_tosq))
				{
				tmc++;
				if (map_rank(mptr1->m_frsq) == rank)
					rmc++;
				if (map_file(mptr1->m_frsq) == file)
					fmc++;
				};
			mptr1++;
			};

		/* check pun count for outer loop move */

		if (tmc > 0)
			{
			/* file disambiguation has priority */

			if ((rmc > 0) || ((rmc == 0) && (fmc == 0)))
				mptr0->m_flag |= mf_sanf;

			/* rank disambiguation may be needed */

			if (fmc > 0)
				mptr0->m_flag |= mf_sanr;
			};
		};
	mptr0++;
	};

return;
}

/*--> EPDMLScanMate: scan current move list for mating moves */
static
void
EPDMLScanMate(void)
{
siT i, j, mateflag, drawflag, moveflag;
mptrT mptr0, mptr1;

/* scan */

mptr0 = tse.tse_base;
for (i = 0; i < tse.tse_count; i++)
	{
	tse.tse_curr = mptr0;
	EPDExecute(mptr0);

	/* now at next higher ply, generate psuedolegal set */

	EPDGeneratePL();

	/* try to find at least one legal move */

	mptr1 = tse.tse_base;
	moveflag = 0;
	j = 0;
	while (!moveflag && (j < tse.tse_count))
		{
		tse.tse_curr = mptr1;
		EPDExecute(mptr1);
		EPDRetract(mptr1);
		if (!(mptr1->m_flag & mf_bust))
			moveflag = 1;
		else
			{
			mptr1++;
			j++;
			};
		};

	/* any second level moves detected? */

	if (moveflag != 0)
		{
		/* not a mate */

		mateflag = drawflag = 0;
		}
	else
		{
		/* a mating move is detected */

		if (EPDTestAKIC())
			{
			mateflag = 1;
			drawflag = 0;
			}
		else
			{
			drawflag = 1;
			mateflag = 0;
			};
		};

	/* undo execution */

	EPDRetract(mptr0);

	/* now back at lower ply */

	if (mateflag)
		mptr0->m_flag |= mf_chmt;
	else
		if (drawflag)
			mptr0->m_flag |= (mf_draw | mf_stmt);

	/* next move */

	mptr0++;
	};

return;
}

/*--> EPDGenClean: generate move list with first level processing */
static
void
EPDGenClean(void)
{
/* basic psuedolegal generation */

EPDGeneratePL();

/* set legality flags, remove illegal moves, and disambiguate */

EPDMLExec();
EPDMLPolice();
EPDMLDisambiguate();

return;
}

/*--> EPDGenMoves: generate legal moves and set mate flags */
nonstatic
void
EPDGenMoves(void)
{
/* perform basic first level generation */

EPDGenClean();

/* handle two ply draw and checkmate detection */

EPDMLScanMate();

return;
}

/*--> EPDFetchMoveCount: fetch the move count */
nonstatic
siT
EPDFetchMoveCount(void)
{

return (tse.tse_count);
}

/*--> EPDFetchMove: fetch the nth move */
nonstatic
mptrT
EPDFetchMove(siT index)
{
ret_m = *(tse.tse_base + index);

return (&ret_m);
}

/*--> EPDSetMoveFlags: set move flags from current generation set */
nonstatic
void
EPDSetMoveFlags(mptrT mptr)
{
mptrT rmptr;

rmptr = EPDFindMove(mptr);
if (rmptr != NULL)
	mptr->m_flag = rmptr->m_flag;

return;
}

/*--> EPDSortSAN: ASCII SAN sort move list */
nonstatic
void
EPDSortSAN(void)
{
mptrT mptr, mptr0, mptr1;
siT i, j, pair, pass, flag;
sanptrT sanptr, sptr0, sptr1;
char t_ch;
mT t_m;

/* allocate the SAN string vector */

sanptr = (sanptrT) EPDMemoryGrab(sizeof(sanT) * tse.tse_count);

/* construct the SAN string entries */

mptr = tse.tse_base;
for (i = 0; i < tse.tse_count; i++)
	EPDSANEncode(mptr++, *(sanptr + i));

/* a low tech bubble sort */

flag = 1;
pass = 0;
while (flag && (pass < (tse.tse_count - 1)))
	{
	sptr0 = sanptr;
	sptr1 = sanptr + 1;
	mptr0 = tse.tse_base;
	mptr1 = tse.tse_base + 1;
	flag = 0;
	pair = 0;
	while (pair < (tse.tse_count - pass - 1))
		{
		/* case sensitive ascending order */

		if (strcmp((charptrT) sptr0, (charptrT) sptr1) > 0)
			{
			flag = 1;
			for (j = 0; j < sanL; j++)
				{
				t_ch = (*sptr0)[j];
				(*sptr0)[j] = (*sptr1)[j];
				(*sptr1)[j] = t_ch;
				};
			t_m = *mptr0;
			*mptr0 = *mptr1;
			*mptr1 = t_m;
			};
		sptr0++;
		sptr1++;
		mptr0++;
		mptr1++;
		pair++;
		};
	pass++;
	};

EPDMemoryFree(sanptr);

return;
}

/*--> EPDRepairMove: repair a move operation */
static
void
EPDRepairMove(eopptrT eopptr)
{
eovptrT eovptr;
mptrT mptr;
mT m;
sanT san;

/* repair a single move from the current position */

eovptr = eopptr->eop_headeov;
if (eovptr != NULL)
	{
	mptr = EPDSANDecodeAux(eovptr->eov_str, 0);
	if (mptr != NULL)
		{
		m = *mptr;
		EPDSANEncode(&m, san);
		if (strcmp(eovptr->eov_str, san) != 0)
			EPDReplaceEOVStr(eovptr, san);
		};
	};

return;
}

/*--> EPDRepairMoveset: repair a moveset operation */
static
void
EPDRepairMoveset(eopptrT eopptr)
{
eovptrT eovptr;
mptrT mptr;
mT m;
sanT san;

/* check each move from the current position */

eovptr = eopptr->eop_headeov;
while (eovptr != NULL)
	{
	mptr = EPDSANDecodeAux(eovptr->eov_str, 0);
	if (mptr != NULL)
		{
		m = *mptr;
		EPDSANEncode(&m, san);
		if (strcmp(eovptr->eov_str, san) != 0)
			EPDReplaceEOVStr(eovptr, san);
		};

	eovptr = eovptr->eov_next;
	};

return;
}

/*--> EPDRepairVariation: repair a variation operation */
static
void
EPDRepairVariation(eopptrT eopptr)
{
eovptrT eovptr;
mptrT mptr;
mT m;
sanT san;

/* play move sequence from the current position */

eovptr = eopptr->eop_headeov;
while (eovptr != NULL)
	{
	mptr = EPDSANDecodeAux(eovptr->eov_str, 0);
	if (mptr == NULL)
		eovptr = NULL;
	else
		{
		m = *mptr;
		EPDSANEncode(&m, san);
		if (strcmp(eovptr->eov_str, san) != 0)
			EPDReplaceEOVStr(eovptr, san);

		tse.tse_curr = EPDFindMove(mptr);
		if (tse.tse_curr == NULL)
			EPDFatal("EPDRepairVariation: can't find move");

		EPDExecute(mptr);
		EPDGenMoves();

		eovptr = eovptr->eov_next;
		}
	};

/* retract any executed moves */

EPDRetractAll();

return;
}

/*--> EPDPurgeOpFile: purge operation from input file to output file */
nonstatic
siT
EPDPurgeOpFile(charptrT opsym, charptrT fn0, charptrT fn1)
{
siT flag;
fptrT fptr0, fptr1;
epdptrT epdptr;
eopptrT eopptr;
charptrT eptr;
char ev[epdL];

/* set default return value (success) */

flag = 1;

/* clear the input and output file pointers */

fptr0 = fptr1 = NULL;

/* open the input file for reading */

if (flag)
	{
	fptr0 = fopen(fn0, "r");
	if (fptr0 == NULL)
		flag = 0;
	};

/* open the output file for writing */

if (flag)
	{
	fptr1 = fopen(fn1, "w");
	if (fptr1 == NULL)
		flag = 0;
	};

/* scan entire file */

while (flag && (fgets(ev, (epdL - 1), fptr0) != NULL))
	{
	/* decode a record */

	epdptr = EPDDecode(ev);

	/* check record decode validity */

	if (epdptr == NULL)
		flag = 0;
	else
		{
		/* locate the operation to be purged */

		eopptr = EPDLocateEOP(epdptr, opsym);
		if (eopptr != NULL)
			{
			EPDUnthreadEOP(epdptr, eopptr);
			EPDReleaseEOP(eopptr);
			};

		/* encode the record (includes normalization) */

		eptr = EPDEncode(epdptr);

		/* release EPD structure */

		EPDReleaseEPD(epdptr);

		/* check result */

		if (eptr == NULL)
			flag = 0;
		else
			{
			fprintf(fptr1, "%s\n", eptr);
			EPDMemoryFree(eptr);
			};
		};
	};

/* close input and output files */

if (fptr0 != NULL)
	fclose(fptr0);

if (fptr1 != NULL)
	fclose(fptr1);

return (flag);
}

/*--> EPDRepairEPD: repair an EPD structure */
nonstatic
siT
EPDRepairEPD(epdptrT epdptr)
{
siT flag;
eopptrT eopptr;

/* set default return value: repair successful */

flag = 1;

/* set up the position as the current position */

EPDRealize(epdptr);

/* check legality */

if (!EPDIsLegal())
	flag = 0;
else
	{
	/* generate moves and notation */

	EPDGenMoves();

	/* repair moveset "am" */

	eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso_am]);
	if (eopptr != NULL)
		EPDRepairMoveset(eopptr);

	/* repair moveset "bm" */

	eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso_bm]);
	if (eopptr != NULL)
		EPDRepairMoveset(eopptr);

	/* repair move "pm" */

	eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso_pm]);
	if (eopptr != NULL)
		EPDRepairMove(eopptr);

	/* repair variation "pv" */

	eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso_pv]);
	if (eopptr != NULL)
		EPDRepairVariation(eopptr);

	/* repair move "sm" */

	eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso_sm]);
	if (eopptr != NULL)
		EPDRepairMove(eopptr);

	/* repair variation "sv" */

	eopptr = EPDLocateEOP(epdptr, epdsostrv[epdso_sv]);
	if (eopptr != NULL)
		EPDRepairVariation(eopptr);
	};

return (flag);
}

/*--> EPDRepairFile: repair input file to output file */
nonstatic
siT
EPDRepairFile(charptrT fn0, charptrT fn1)
{
siT flag;
fptrT fptr0, fptr1;
epdptrT epdptr;
charptrT eptr;
char ev[epdL];

/* set default return value (success) */

flag = 1;

/* clear the input and output file pointers */

fptr0 = fptr1 = NULL;

/* open the input file for reading */

if (flag)
	{
	fptr0 = fopen(fn0, "r");
	if (fptr0 == NULL)
		flag = 0;
	};

/* open the output file for writing */

if (flag)
	{
	fptr1 = fopen(fn1, "w");
	if (fptr1 == NULL)
		flag = 0;
	};

/* scan entire file */

while (flag && (fgets(ev, (epdL - 1), fptr0) != NULL))
	{
	/* decode a record */

	epdptr = EPDDecode(ev);

	/* check record decode validity */

	if (epdptr == NULL)
		flag = 0;
	else
		{
		/* make repairs */

		flag = EPDRepairEPD(epdptr);

		/* continue if repair okay */

		if (flag)
			{
			/* encode the normalized record */

			eptr = EPDEncode(epdptr);

			/* check result */

			if (eptr == NULL)
				flag = 0;
			else
				{
				fprintf(fptr1, "%s\n", eptr);
				EPDMemoryFree(eptr);
				};
			};

		/* release EPD structure */

		EPDReleaseEPD(epdptr);
		};
	};

/* close input and output files */

if (fptr0 != NULL)
	fclose(fptr0);

if (fptr1 != NULL)
	fclose(fptr1);

return (flag);
}

/*--> EPDNormalizeFile: normalize input file to output file */
nonstatic
siT
EPDNormalizeFile(charptrT fn0, charptrT fn1)
{
siT flag;
fptrT fptr0, fptr1;
epdptrT epdptr;
charptrT eptr;
char ev[epdL];

/* set default return value (success) */

flag = 1;

/* clear the input and output file pointers */

fptr0 = fptr1 = NULL;

/* open the input file for reading */

if (flag)
	{
	fptr0 = fopen(fn0, "r");
	if (fptr0 == NULL)
		flag = 0;
	};

/* open the output file for writing */

if (flag)
	{
	fptr1 = fopen(fn1, "w");
	if (fptr1 == NULL)
		flag = 0;
	};

/* scan entire file */

while (flag && (fgets(ev, (epdL - 1), fptr0) != NULL))
	{
	/* decode a record */

	epdptr = EPDDecode(ev);

	/* check record decode validity */

	if (epdptr == NULL)
		flag = 0;
	else
		{
		/* encode the record (this includes normalization) */

		eptr = EPDEncode(epdptr);

		/* release EPD structure */

		EPDReleaseEPD(epdptr);

		/* check result */

		if (eptr == NULL)
			flag = 0;
		else
			{
			fprintf(fptr1, "%s\n", eptr);
			EPDMemoryFree(eptr);
			};
		};
	};

/* close input and output files */

if (fptr0 != NULL)
	fclose(fptr0);

if (fptr1 != NULL)
	fclose(fptr1);

return (flag);
}

/*--> EPDScoreFile: score a benchmark file */
nonstatic
siT
EPDScoreFile(charptrT fn, bmsptrT bmsptr)
{
siT flag;
siT skipflag;
siT am_flag, bm_flag;
siT solved;
fptrT fptr;
epdptrT epdptr;
eopptrT am_eopptr, bm_eopptr, acd_eopptr, acn_eopptr, acs_eopptr;
eopptrT sm_eopptr, sv_eopptr, pm_eopptr, pv_eopptr;
charptrT result;
char ev[epdL];

/* set default return value (success) */

flag = 1;

/* clear the input file pointer */

fptr = NULL;

/* preset the summary structure */

bmsptr->bms_acdflag = bmsptr->bms_acnflag = bmsptr->bms_acsflag = 1;
bmsptr->bms_total = bmsptr->bms_solve = bmsptr->bms_unsol = 0;
bmsptr->bms_total_acd = bmsptr->bms_solve_acd = bmsptr->bms_unsol_acd = 0;
bmsptr->bms_total_acn = bmsptr->bms_solve_acn = bmsptr->bms_unsol_acn = 0;
bmsptr->bms_total_acs = bmsptr->bms_solve_acs = bmsptr->bms_unsol_acs = 0;

/* open the input file for reading */

if (flag)
	{
	fptr = fopen(fn, "r");
	if (fptr == NULL)
		flag = 0;
	};

/* scan entire file */

while (flag && (fgets(ev, (epdL - 1), fptr) != NULL))
	{
	/* decode a record */

	epdptr = EPDDecode(ev);

	/* check record decode validity */

	if (epdptr == NULL)
		flag = 0;
	else
		{
		/* clear the move result pointer */

		result = NULL;

		/* initialize various operation pointers */

		am_eopptr = EPDLocateEOPCode(epdptr, epdso_am);
		bm_eopptr = EPDLocateEOPCode(epdptr, epdso_bm);
		acd_eopptr = EPDLocateEOPCode(epdptr, epdso_acd);
		acn_eopptr = EPDLocateEOPCode(epdptr, epdso_acn);
		acs_eopptr = EPDLocateEOPCode(epdptr, epdso_acs);
		sm_eopptr = EPDLocateEOPCode(epdptr, epdso_sm);
		sv_eopptr = EPDLocateEOPCode(epdptr, epdso_sv);
		pm_eopptr = EPDLocateEOPCode(epdptr, epdso_pm);
		pv_eopptr = EPDLocateEOPCode(epdptr, epdso_pv);

		/* test for am/bm existence */

		if ((am_eopptr == NULL) && (bm_eopptr == NULL))
			skipflag = 1;
		else
			skipflag = 0;

		/* try to locate a result move (note priority) */

		if (!skipflag)
			{
			if (result == NULL)
				if ((pv_eopptr != NULL) && (pv_eopptr->eop_headeov != NULL))
					result = pv_eopptr->eop_headeov->eov_str;

			if (result == NULL)
				if ((pm_eopptr != NULL) && (pm_eopptr->eop_headeov != NULL))
					result = pm_eopptr->eop_headeov->eov_str;

			if (result == NULL)
				if ((sv_eopptr != NULL) && (sv_eopptr->eop_headeov != NULL))
					result = sv_eopptr->eop_headeov->eov_str;

			if (result == NULL)
				if ((sm_eopptr != NULL) && (sm_eopptr->eop_headeov != NULL))
					result = sm_eopptr->eop_headeov->eov_str;

			if (result == NULL)
				skipflag = 1;
			};

		/* determine solve status */

		if (!skipflag)
			{
			/* check for clearance with the am set */

			if ((am_eopptr == NULL) ||
				(EPDLocateEOV(am_eopptr, result) == NULL))
				am_flag = 1;
			else
				am_flag = 0;

			/* check for clearance with the bm set */

			if ((bm_eopptr == NULL) ||
				(EPDLocateEOV(bm_eopptr, result) != NULL))
				bm_flag = 1;
			else
				bm_flag = 0;

			/* set solution flag */

			solved = am_flag && bm_flag;
			};

		/* update statistics block */

		if (!skipflag)
			{
			/* clear acd flag if acd is missing */

			if ((acd_eopptr == NULL) || (acd_eopptr->eop_headeov == NULL))
				bmsptr->bms_acdflag = 0;

			/* clear acn flag if acn is missing */

			if ((acn_eopptr == NULL) || (acn_eopptr->eop_headeov == NULL))
				bmsptr->bms_acnflag = 0;

			/* clear acs flag if acs is missing */

			if ((acs_eopptr == NULL) || (acs_eopptr->eop_headeov == NULL))
				bmsptr->bms_acsflag = 0;

			/* increment record count */

			bmsptr->bms_total++;

			/* fold in acd value */

			if (bmsptr->bms_acdflag)
				{
				bmsptr->bms_total_acd +=
					atoi(acd_eopptr->eop_headeov->eov_str);
				if (solved)
					bmsptr->bms_solve_acd +=
						atoi(acd_eopptr->eop_headeov->eov_str);
				else
					bmsptr->bms_unsol_acd +=
						atoi(acd_eopptr->eop_headeov->eov_str);
				};

			/* fold in acn value */

			if (bmsptr->bms_acnflag)
				{
				bmsptr->bms_total_acn +=
					atoi(acn_eopptr->eop_headeov->eov_str);
				if (solved)
					bmsptr->bms_solve_acn +=
						atoi(acn_eopptr->eop_headeov->eov_str);
				else
					bmsptr->bms_unsol_acn +=
						atoi(acn_eopptr->eop_headeov->eov_str);
				};

			/* fold in acs value */

			if (bmsptr->bms_acsflag)
				{
				bmsptr->bms_total_acs +=
					atoi(acs_eopptr->eop_headeov->eov_str);
				if (solved)
					bmsptr->bms_solve_acs +=
						atoi(acs_eopptr->eop_headeov->eov_str);
				else
					bmsptr->bms_unsol_acs +=
						atoi(acs_eopptr->eop_headeov->eov_str);
				};

			/* update remaining items according to solved status */

			if (solved)
				bmsptr->bms_solve++;
			else
				bmsptr->bms_unsol++;
			};

		/* release EPD structure */

		EPDReleaseEPD(epdptr);
		};
	};

/* close input file */

if (fptr != NULL)
	fclose(fptr);

return (flag);
}

/*--> EPDEnumerate: enumeration of current position */
static
liT
EPDEnumerate(siT depth)
{
liT total;
mptrT mptr;
siT i;

/* enumerate current position to the indicated depth */

if (depth == 0)
	total = 1;
else
	{
	total = 0;
	EPDGeneratePL();
	mptr = tse.tse_base;
	for (i = 0; i < tse.tse_count; i++)
		{
		tse.tse_curr = mptr;
		EPDExecute(mptr);
		if (!(mptr->m_flag & mf_bust))
			total += EPDEnumerate((siT)(depth - 1));
		EPDRetract(mptr);
		mptr++;
		};
	};

return (total);
}

/*--> EPDEnumerateFile: enumerate input file to output file */
nonstatic
siT
EPDEnumerateFile(siT depth, charptrT fn0, charptrT fn1, liptrT totalptr)
{
siT flag;
fptrT fptr0, fptr1;
time_t start_time;
liT acn, acs;
epdptrT epdptr;
charptrT eptr;
char ev[epdL];

/* set default return value (success) */

flag = 1;

/* clear the grand total */

*totalptr = 0;

/* clear the input and output file pointers */

fptr0 = fptr1 = NULL;

/* open the input file for reading */

if (flag)
	{
	fptr0 = fopen(fn0, "r");
	if (fptr0 == NULL)
		flag = 0;
	};

/* open the output file for writing */

if (flag)
	{
	fptr1 = fopen(fn1, "w");
	if (fptr1 == NULL)
		flag = 0;
	};

/* scan entire file */

while (flag && (fgets(ev, (epdL - 1), fptr0) != NULL))
	{
	/* decode a record */

	epdptr = EPDDecode(ev);

	/* check record decode validity */

	if (epdptr == NULL)
		flag = 0;
	else
		{
		/* set up the current position */

		EPDRealize(epdptr);

		/* check legality */

		if (!EPDIsLegal())
			flag = 0;
		else
			{
			/* perform enumeration */

			start_time = time(NULL);
			acn = EPDEnumerate(depth);
			acs = time(NULL) - start_time;

			/* update the grand total */

			*totalptr += acn;

			/* record the updated field: acd */

			EPDAddOpInt(epdptr, epdso_acd, depth);

			/* record the updated field: acn */

			EPDAddOpInt(epdptr, epdso_acn, acn);

			/* record the updated field: acs */

			EPDAddOpInt(epdptr, epdso_acs, acs);

			/* encode the record */

			EPDNormalize(epdptr);
			eptr = EPDEncode(epdptr);

			/* check result */

			if (eptr == NULL)
				flag = 0;
			else
				{
				fprintf(fptr1, "%s\n", eptr);
				EPDMemoryFree(eptr);
				};
			};

		/* release EPD structure */

		EPDReleaseEPD(epdptr);
		};
	};

/* close input and output files */

if (fptr0 != NULL)
	fclose(fptr0);

if (fptr1 != NULL)
	fclose(fptr1);

return (flag);
}

/*--> EPDMoveList: generate a string representation of a move list */
nonstatic
charptrT
EPDMoveList(gamptrT gamptr)
{
charptrT s;
charptrT b;
siT count;
gpmptrT gpmptr;
mT m;
siT sn;
cT sc, c;
siT pi, index, limit, length, n, column;
sanT san;
char tv[tL];

/* calculate upper bound on storage requirement */

count = EPDGamePlyCount(gamptr);
limit = (((count + 1) / 2) * 5) + 4 + (count * 8) + 8 + 1;
b = (charptrT) EPDMemoryGrab(limit);

/* set the inital played move pointer */

gpmptr = gamptr->gam_headgpm;

/* set up starting color and starting move number */

if (count == 0)
	sc = c_w;
else
	sc = gpmptr->gpm_ese.ese_actc;

if (count == 0)
	sn = 1;
else
	sn = gpmptr->gpm_ese.ese_fmvn;

/* more set up */

pi = 0;
index = 0;
c = sc;
n = sn;
column = 0;

/* loop through moves */

for (pi = 0; pi < count; pi++)
	{
	/* handle move number indication */

	if ((c == c_w) || ((pi == 0) && (sc == c_b)))
		{
		sprintf(tv, "%hd.", n);
		length = strlen(tv);

		if ((column + 1 + length) >= columnL)
			{
			strcpy((b + index), "\n");
			index++;
			column = 0;
			};

		if (column != 0)
			{
			strcpy((b + index), " ");
			index++;
			column++;
			};

		strcpy((b + index), tv);
		index += length;
		column += length;

		n++;
		};

	/* handle ellipsis */

	if ((pi == 0) && (sc == c_b))
		{
		sprintf(tv, "...");
		length = strlen(tv);

		if ((column + 1 + length) >= columnL)
			{
			strcpy((b + index), "\n");
			index++;
			column = 0;
			};

		if (column != 0)
			{
			strcpy((b + index), " ");
			index++;
			column++;
			};

		strcpy((b + index), tv);
		index += length;
		column += length;
		};

	/* handle move */

	m = gpmptr->gpm_m;
	EPDSANEncode(&m, san);
	length = strlen(san);

	if ((column + 1 + length) >= columnL)
		{
		strcpy((b + index), "\n");
		index++;
		column = 0;
		};

	if (column != 0)
		{
		strcpy((b + index), " ");
		index++;
		column++;
		};

	strcpy((b + index), san);
	index += length;
	column += length;

	gpmptr = gpmptr->gpm_next;
	c = inv_cv[c];
	};

/* append game termination marker */

sprintf(tv, "%s", gtimstrv[gamptr->gam_gtim]);
length = strlen(tv);

if ((column + 1 + length) >= columnL)
	{
	strcpy((b + index), "\n");
	index++;
	column = 0;
	};

if (column != 0)
	{
	strcpy((b + index), " ");
	index++;
	column++;
	};

strcpy((b + index), tv);
index += length;
column += length;

/* closing newline */

if (column != 0)
	strcpy((b + index), "\n");

/* allocate and copy to result */

s = EPDStringGrab(b);
EPDMemoryFree(b);

return (s);
}

/*--> EPDPGNFetchTagIndex: return a PGN Seven Tag Roster tag index */
nonstatic
pgnstrT
EPDPGNFetchTagIndex(charptrT s)
{
pgnstrT pgnstr;
pgnstrT rstr;

pgnstr = pgnstr_nil;
rstr = 0;
while ((pgnstr == pgnstr_nil) && (rstr < pgnstrL))
	if (strcmp(s, EPDPGNFetchTagName(rstr)) == 0)
		pgnstr = rstr;
	else
		rstr++;

return (pgnstr);
}

/*--> EPDPGNFetchTagName: return a PGN Seven Tag Roster tag name */
nonstatic
charptrT
EPDPGNFetchTagName(pgnstrT pgnstr)
{

return (pgnstrstrv[pgnstr]);
}

/*--> EPDPGNGetSTR: return a string from the Seven Tag Roster */
nonstatic
charptrT
EPDPGNGetSTR(gamptrT gamptr, pgnstrT pgnstr)
{

return (gamptr->gam_strv[pgnstr]);
}

/*--> EPDPGNPutSTR: enter a string into the Seven Tag Roster */
nonstatic
void
EPDPGNPutSTR(gamptrT gamptr, pgnstrT pgnstr, charptrT s)
{
if (gamptr->gam_strv[pgnstr] != NULL)
	EPDMemoryFree(gamptr->gam_strv[pgnstr]);

gamptr->gam_strv[pgnstr] = EPDStringGrab(s);

return;
}

/*--> EPDPGNGenSTR: return a string with the entire STR */
nonstatic
charptrT
EPDPGNGenSTR(gamptrT gamptr)
{
charptrT s;
pgnstrT pgnstr;
char tv[tL];

s = EPDStringGrab("");

for (pgnstr = 0; pgnstr < pgnstrL; pgnstr++)
	{
	sprintf(tv, "[%s \"%s\"]\n",
		pgnstrstrv[pgnstr], gamptr->gam_strv[pgnstr]);
	s = EPDStringAppendStr(s, tv);
	};

return (s);
}

/*--> EPDPGNHistory: generate a string for PGN version of history */
nonstatic
charptrT
EPDPGNHistory(gamptrT gamptr)
{
charptrT s;
charptrT ms;

s = EPDPGNGenSTR(gamptr);
s = EPDStringAppendChar(s, '\n');
ms = EPDMoveList(gamptr);
s = EPDStringAppendStr(s, ms);
EPDMemoryFree(ms);
s = EPDStringAppendChar(s, '\n');

return (s);
}

/*--> EPDCopyInPTP: copy STR into an EDP structure (ptp operation) */
nonstatic
void
EPDCopyInPTP(gamptrT gamptr, epdptrT epdptr)
{
eopptrT eopptr;
pgnstrT pgnstr;

if (epdptr != NULL)
	{
	EPDDropIfLocEOPCode(epdptr, epdso_ptp);
	eopptr = EPDCreateEOPCode(epdso_ptp);

	for (pgnstr = 0; pgnstr < pgnstrL; pgnstr++)
		{
		EPDAppendEOV(eopptr,
			EPDCreateEOVSym(EPDPGNFetchTagName(pgnstr)));
		EPDAppendEOV(eopptr,
			EPDCreateEOVStr(EPDPGNGetSTR(gamptr, pgnstr)));
		};

	EPDAppendEOP(epdptr, eopptr);
	};

return;
}

/*--> EPDCopyOutPTP: copy STR from an EDP structure (ptp operation) */
nonstatic
void
EPDCopyOutPTP(gamptrT gamptr, epdptrT epdptr)
{
eopptrT eopptr;
eovptrT eovptr;
pgnstrT pgnstr;

if (epdptr != NULL)
	{
	eopptr = EPDLocateEOPCode(epdptr, epdso_ptp);
	eovptr = eopptr->eop_headeov;
	while ((eovptr != NULL) && (eovptr->eov_next != NULL))
		{
		pgnstr = EPDPGNFetchTagIndex(eovptr->eov_str);
		if (pgnstr != pgnstr_nil)
			EPDPGNPutSTR(gamptr, pgnstr, eovptr->eov_next->eov_str);
		eovptr = eovptr->eov_next->eov_next;
		};
	};

return;
}

/*--> EPDFetchRefcomStr: return  pointer of indicated refcom string */
nonstatic
charptrT
EPDFetchRefcomStr(refcomT refcom)
{

return (refcomstrv[refcom]);
}

/*--> EPDFetchRefreqStr: return  pointer of indicated refreq string */
nonstatic
charptrT
EPDFetchRefreqStr(refreqT refreq)
{

return (refreqstrv[refreq]);
}

/*--> EPDFetchRefcomIndex: return a referee command index */
nonstatic
refcomT
EPDFetchRefcomIndex(charptrT s)
{
refcomT refcom;
refcomT rcom;

refcom = refcom_nil;
rcom = 0;
while ((refcom == refcom_nil) && (rcom < refcomL))
	if (strcmp(s, EPDFetchRefcomStr(rcom)) == 0)
		refcom = rcom;
	else
		rcom++;

return (refcom);
}

/*--> EPDFetchRefreqIndex: return a referee request index */
nonstatic
refreqT
EPDFetchRefreqIndex(charptrT s)
{
refreqT refreq;
refreqT rreq;

refreq = refreq_nil;
rreq = 0;
while ((refreq == refreq_nil) && (rreq < refreqL))
	if (strcmp(s, EPDFetchRefreqStr(rreq)) == 0)
		refreq = rreq;
	else
		rreq++;

return (refreq);
}

/*--> EPDExtractRefcomIndex: extract a referee command index */
nonstatic
refreqT
EPDExtractRefcomIndex(epdptrT epdptr)
{
refcomT refcom;
eopptrT eopptr;
eovptrT eovptr;

/* set default return value */

refcom = refreq_nil;

if (epdptr != NULL)
	if ((eopptr = EPDLocateEOPCode(epdptr, epdso_refcom)) != NULL)
		if ((eovptr = eopptr->eop_headeov) != NULL)
			refcom = EPDFetchRefcomIndex(eovptr->eov_str);

return (refcom);
}

/*--> EPDExtractRefreqIndex: extract a referee request index */
nonstatic
refreqT
EPDExtractRefreqIndex(epdptrT epdptr)
{
refreqT refreq;
eopptrT eopptr;
eovptrT eovptr;

/* set default return value */

refreq = refreq_nil;

if (epdptr != NULL)
	if ((eopptr = EPDLocateEOPCode(epdptr, epdso_refreq)) != NULL)
		if ((eovptr = eopptr->eop_headeov) != NULL)
			refreq = EPDFetchRefreqIndex(eovptr->eov_str);

return (refreq);
}

/*--> EPDComm: slave to Duplex autoplay program */
nonstatic
siT
EPDComm(refintptrT refintptr, charptrT pipebase)
{
siT flag;
siT done;
siT flow;
refcomT refcom;
charptrT pfnv[flowL];
fptrT pfptrv[flowL];
epdptrT epdptr0, epdptr1;
charptrT eptr;
char ev[epdL];

/* set default result: success */

flag = 1;

/* set up the EPD Kit for a new game */

EPDInitArray();

/* generate pipe file names and clear their pointers */

for (flow = 0; flow < flowL; flow++)
	{
	pfnv[flow] = EPDStringGrab(pipebase);
	pfnv[flow] = EPDStringAppendStr(pfnv[flow], ".pc");
	pfnv[flow] = EPDStringAppendChar(pfnv[flow], (char)(flow + '0'));
	pfptrv[flow] = NULL;
	};

/* pipe files already created by Duplex, attempt open */

flow = 0;
while (flag && (flow < flowL))
	{
	pfptrv[flow] = fopen(pfnv[flow], "a+");
	if (pfptrv[flow] == NULL)
		flag = 0;
	else
		flow++;
	};

/* sign on to Duplex */

if (flag)
	{
	epdptr0 = EPDGetCurrentPosition();
	EPDAddOpSym(epdptr0, epdso_refreq, refreqstrv[refreq_sign_on]);
	eptr = EPDEncode(epdptr0);
	EPDReleaseEPD(epdptr0);
	fprintf(pfptrv[0], "%s\n", eptr);
	fflush(pfptrv[0]);
	EPDMemoryFree(eptr);
	};

/* run event cycle loop */

done = 0;
while (flag && !done)
	{
	/* read an incoming EPD message */

	if (fgets(ev, (epdL - 1), pfptrv[1]) == NULL)
		flag = 0;
	else
		{
		/* decode the message */

		epdptr1 = EPDDecode(ev);
		if ((epdptr1 == NULL) ||
			((refcom = EPDExtractRefcomIndex(epdptr1)) == refcom_nil))
			flag = 0;
		else
			{
			/* send the message to the callback routine */

			epdptr0 = (*refintptr)(epdptr1, &flag);

			/* release input storage */

			EPDReleaseEPD(epdptr1);

			/* construct and transmit output string */

			if (flag && (epdptr0 != NULL))
				{
				eptr = EPDEncode(epdptr0);
				fprintf(pfptrv[0], "%s\n", eptr);
				fflush(pfptrv[0]);
				EPDMemoryFree(eptr);
				};

			/* release output storage */

			if (epdptr0 != NULL)
				EPDReleaseEPD(epdptr0);

			/* set the completion flag on disconnect */

			if (flag && !done && (refcom == refcom_disconnect))
				done = 1;
			};
		};
	};

/* close pipes and release pipe file names */

for (flow = 0; flow < flowL; flow++)
	{
	if (pfptrv[flow] != NULL)
		fclose(pfptrv[flow]);
	EPDMemoryFree(pfnv[flow]);
	};

return (flag);
}

/*--> EPDTBClassFileName: return an allocated class name */
static
charptrT
EPDTBClassFileName(charptrT dirstr, tbidT tbid, cT c)
{
charptrT name;

#if (!defined(__MWERKS__))
if (dirstr == NULL)
	name = EPDStringGrab("");
else
	{
	name = EPDStringGrab(dirstr);
	name = EPDStringAppendChar(name, '/');
	};
#else
if (dirstr == NULL)
	name = EPDStringGrab("");
else
	{
	name = EPDStringGrab(":");
	name = EPDStringAppendStr(name, dirstr);
	name = EPDStringAppendChar(name, ':');
	};
#endif

name = EPDStringAppendStr(name, (tbbaseptr + tbid)->tb_name);
name = EPDStringAppendStr(name, ctmext_strv[c]);

return (name);
}

/*--> EPDTBGetSignature: get the signature for a distribution */
static
liT
EPDTBGetSignature(distvT distv)
{
liT sig;
cT c;
pT p;

sig = 0;
for (c = c_w; c <= c_b; c++)
	for (p = p_p; p <= p_q; p++)
		sig = (sig << 3) | distv[c][p];

return (sig);
}

/*--> EPDTBGetInvertedSignature: get the inverted signature for a distribution */
static
liT
EPDTBGetInvertedSignature(distvT distv)
{
liT sig;
cT c;
pT p;

sig = 0;
for (c = c_w; c <= c_b; c++)
	for (p = p_p; p <= p_q; p++)
		sig = (sig << 3) | distv[inv_cv[c]][p];

return (sig);
}

/*--> EPDTBLocateClass: locate a class for a piece distribution */
static
tbidT
EPDTBLocateClass(distvT distv, siptrT invertptr)
{
tbidT tbid;
siT flag;
tbptrT classptr;
liT sig;

/* set "not found" */

flag = 0;

/* get the piece distribution signature */

sig = EPDTBGetSignature(distv);

/* search for signature match */

classptr = tbbaseptr;
tbid = 0;
while (!flag && (tbid < tbidL))
	if (sig == classptr->tb_sig0)
		{
		flag = 1;
		*invertptr = 0;
		}
	else
		if (sig == classptr->tb_sig1)
			{
			flag = 1;
			*invertptr = 1;
			}
		else
			{
			classptr++;
			tbid++;
			};

/* if not found, set return to nil */

if (!flag)
	tbid = tbid_nil;

return (tbid);
}

/*--> EPDTBCopyPosition: copy a position vector */
static
void
EPDTBCopyPosition(posvT posv0, posvT posv1)
{
cT c;
siT i;

for (c = c_w; c <= c_b; c++)
	for (i = 0; i < tbmecL; i++)
		posv1[c][i] = posv0[c][i];

return;
}

/*--> EPDTBReflectX: reflect a position along the X axis */
static
void
EPDTBReflectX(tbptrT classptr, posvT posv)
{
cT c;
siT i;

for (c = c_w; c <= c_b; c++)
	for (i = 0; i < classptr->tb_mcv[c]; i++)
		posv[c][i] ^= 0x38;

return;
}

/*--> EPDTBReflectY: reflect a position along the Y axis */
static
void
EPDTBReflectY(tbptrT classptr, posvT posv)
{
cT c;
siT i;

for (c = c_w; c <= c_b; c++)
	for (i = 0; i < classptr->tb_mcv[c]; i++)
		posv[c][i] ^= 0x07;

return;
}

/*--> EPDTBReflectXY: reflect a position along the X=Y axis */
static
void
EPDTBReflectXY(tbptrT classptr, posvT posv)
{
cT c;
siT i;
rankT rank;
fileT file;

for (c = c_w; c <= c_b; c++)
	for (i = 0; i < classptr->tb_mcv[c]; i++)
		{
		rank = map_rank(posv[c][i]);
		file = map_file(posv[c][i]);
		posv[c][i] = map_sq(file, rank);
		};

return;
}

/*--> EPDTBNormalize: normalize a position */
static
void
EPDTBNormalize(tbptrT classptr, posvT posv)
{
sqT sq;
cT pivot_c;
siT pivot_slot;

/* get pivot color and slot */

pivot_c = classptr->tb_pivot_c;
pivot_slot = classptr->tb_pivot_slot;

/* determine normalization mode */

if (classptr->tb_fold == fold_flank)
	{
	/* class has at least one pawn */

	sq = posv[pivot_c][pivot_slot];
	if (map_file(sq) > file_d)
		EPDTBReflectY(classptr, posv);
	}
else
	{
	/* class has no pawns */

	sq = posv[pivot_c][pivot_slot];
	if (map_rank(sq) > rank_4)
		EPDTBReflectX(classptr, posv);

	sq = posv[pivot_c][pivot_slot];
	if (map_file(sq) > file_d)
		EPDTBReflectY(classptr, posv);

	sq = posv[pivot_c][pivot_slot];
	if (map_rank(sq) > map_file(sq))
		EPDTBReflectXY(classptr, posv);
	};

return;
}

/*--> EPDTBGenIndex: generate a file index for a class and position */
static
indexT
EPDTBGenIndex(tbptrT classptr, posvT posv)
{
indexT index;
liT factor=0;
posvT nposv;
cT c;
siT i;

EPDTBCopyPosition(posv, nposv);
EPDTBNormalize(classptr, nposv);

index = 0;
for (c = c_w; c <= c_b; c++)
	for (i = 0; i < classptr->tb_mcv[c]; i++)
		{
		switch (classptr->tb_scalev[c][i])
			{
			case sqL:
				factor = nposv[c][i];
				break;
			case ff_flankL:
				factor = flank_mapv[nposv[c][i]];
				break;
			case ff_triangleL:
				factor = triangle_mapv[nposv[c][i]];
				break;
			default:
				EPDSwitchFault("EPDTBGenIndex");
				break;
			};
		index += factor * classptr->tb_multv[c][i];
		};

return (index);
}

/*--> EPDTBGenInvertIndex: generate a file index for a class and position (inverted) */
static
indexT
EPDTBGenInvertIndex(tbptrT classptr, posvT posv)
{
indexT index;
liT factor=0;
posvT nposv;
cT c, invc;
siT i;

for (c = c_w; c <= c_b; c++)
	{
	invc = inv_cv[c];
	for (i = 0; i < classptr->tb_mcv[invc]; i++)
		nposv[invc][i] = posv[c][i];
	};

if (classptr->tb_flags & tbf_has_pawns)
	EPDTBReflectX(classptr, nposv);

EPDTBNormalize(classptr, nposv);

index = 0;
for (c = c_w; c <= c_b; c++)
	{
	for (i = 0; i < classptr->tb_mcv[c]; i++)
		{
		switch (classptr->tb_scalev[c][i])
			{
			case sqL:
				factor = nposv[c][i];
				break;
			case ff_flankL:
				factor = flank_mapv[nposv[c][i]];
				break;
			case ff_triangleL:
				factor = triangle_mapv[nposv[c][i]];
				break;
			default:
				EPDSwitchFault("EPDTBGenInvertIndex");
				break;
			};
		index += factor * classptr->tb_multv[c][i];
		};
	};

return (index);
}

/*--> EPDTBMapFromBEV: convert to centipawns from byte evaluation */
static
cpevT
EPDTBMapFromBEV(bevT bev)
{
cpevT cpev;
siT distance;

switch (bev)
	{
	case bev_broken:
	case bev_reserved:
	case bev_unknown:
		cpev = cpev_wrck;
		break;

	case bev_draw:
		cpev = cpev_draw;
		break;

	default:
		if (tbe_mating(bev))
			{
			distance = ((bev_mi1 - bev) * 2) + 1;
			cpev = cpev_best - distance;
			}
		else
			{
			distance = -((bev_li0 - bev) * 2);
			cpev = cpev_bust + distance;
			};
		break;
	};

return (cpev);
}

/*--> EPDTBProbeFile: return a centipawn value from a probe */
static
cpevT
EPDTBProbeFile(tbidT tbid, cT c, liT index)
{
cpevT cpev;
siT i, slot;
bevT bev;
tbcT tbc;
charptrT fnptr;

/* set default return value: no evaluation found */

cpev = cpev_wrck;

/* search cache for a slot with currently open file */

slot = -1;
i = 0;
while ((slot == -1) && (i < tbcL))
	if (tbcv[i].tbc_inuse &&
		(tbcv[i].tbc_tbid == tbid) && (tbcv[i].tbc_c == c))
		slot = i;
	else
		i++;

/* if no slot was found, try to find an empty one */

if (slot == -1)
	{
	/* look for a free slot */

	i = 0;
	while ((slot == -1) && (i < tbcL))
		if (!tbcv[i].tbc_inuse)
			slot = i;
		else
			i++;

	/* if no empty slot, drop the bottom (LRU) entry */

	if (slot == -1)
		{
		slot = tbcL - 1;
		if (tbcv[slot].tbc_fptr != NULL)
			fclose(tbcv[slot].tbc_fptr);
		};

	/* initialize the slot */

	tbcv[slot].tbc_inuse = 1;
	tbcv[slot].tbc_tbid = tbid;
	tbcv[slot].tbc_c = c;
	fnptr = EPDTBClassFileName(tb_path, tbid, c);
	tbcv[slot].tbc_fptr = fopen(fnptr, "rb");
	EPDStringFree(fnptr);
	};

/* move slot entry to top */

if (slot != 0)
	{
	tbc = tbcv[slot];
	for (i = slot; i > 0; i--)
		tbcv[i] = tbcv[i - 1];
	slot = 0;
	tbcv[slot] = tbc;
	};

/* file probe */

if (tbcv[slot].tbc_fptr != NULL)
	if (fseek(tbcv[slot].tbc_fptr, index, SEEK_SET) == 0)
		if (fread(&bev, sizeof(bevT), 1, tbcv[slot].tbc_fptr) ==
			sizeof(bevT))
			cpev = EPDTBMapFromBEV(bev);

return (cpev);
}

/*--> EPDTBIsFilePresent: test if the given TB file is readable */
nonstatic
siT
EPDTBIsFilePresent(tbidT tbid, cT c)
{
siT flag;
fptrT fptr;
charptrT fnptr;

fnptr = EPDTBClassFileName(tb_path, tbid, c);
fptr = fopen(fnptr, "rb");
EPDStringFree(fnptr);
if (fptr == NULL)
	flag = 0;
else
	{
	fclose(fptr);
	flag = 1;
	};

return (flag);
}

/*--> EPDTBScore: return tablebase certain score (if possible) */
nonstatic
cpevT
EPDTBScore(void)
{
cpevT cpev;
tbidT tbid;
siT invert;
cT probe_c;
liT index;
distvT distv;
cT c;
pT p;
sqT sq;
cpT cp;
cT invc;
siT flag;
siT i;
tbptrT classptr;
posvT posv, nposv;
rbT rb;

/* set default return value: score unfound */

cpev = cpev_wrck;

/* clear the distribution */

for (c = c_w; c <= c_b; c++)
	for (p = p_p; p <= p_k; p++)
		distv[c][p] = 0;

/* calculate the distribution */

for (sq = sq_a1; sq <= sq_h8; sq++)
	if ((cp = EPDboard.rbv[sq]) != cp_v0)
		distv[cv_c_cpv[cp]][cv_p_cpv[cp]]++;

/* locate the class */

tbid = EPDTBLocateClass(distv, &invert);

/* was a class identified? */

if (tbid != tbid_nil)
	{
	/* get the class pointer */
	
	classptr = tbbaseptr + tbid;
	
	/* calculate probe color */

	if (!invert)
		probe_c = ese.ese_actc;
	else
		probe_c = inv_cv[ese.ese_actc];

	/* set up the position vector */
	
	for (sq = sq_a1; sq <= sq_h8; sq++)
		{
		cp = EPDboard.rbv[sq];
		if ((cp != cp_v0) && invert)
			cp = cv_cp_c_pv[inv_cv[cv_c_cpv[cp]]][cv_p_cpv[cp]];
		rb.rbv[sq] = cp;
		};

	for (c = c_w; c <= c_b; c++)
		for (i = 0; i < classptr->tb_mcv[c]; i++)
			{
			/* which color-piece to match? */
			
			cp = classptr->tb_cpv[c][i];
			
			/* find it */
			
			flag = 0;
			sq = sq_a1;
			while (!flag)
				if (rb.rbv[sq] == cp)
					{
					posv[c][i] = sq;
					rb.rbv[sq] = cp_v0;
					flag = 1;
					}
				else
					sq++;
			};

	/* calculate index */

	if (!invert)
		index = EPDTBGenIndex(classptr, posv);
	else
		{
		for (c = c_w; c <= c_b; c++)
			{
			invc = inv_cv[c];
			for (i = 0; i < classptr->tb_mcv[c]; i++)
				nposv[invc][i] = posv[c][i];
			};

		index = EPDTBGenInvertIndex(classptr, nposv);
		};

	/* probe */

	cpev = EPDTBProbeFile(tbid, probe_c, index);
	};

return (cpev);
}

/*--> EPDTBInitTBID: initialize an entry in the tablebase vector */
static
void
EPDTBInitTBID(tbidT tbid, charptrT name)
{
cT c;
pT p;
siT i, slot;
liT mult;
char ch;
tbptrT classptr;

classptr = tbbaseptr + tbid;

/* clear flags and set name items */

classptr->tb_flags = 0;
classptr->tb_name = EPDStringGrab(name);
classptr->tb_count = strlen(name);

/* clear various vectors */

for (c = c_w; c <= c_b; c++)
	{
	classptr->tb_mcv[c] = 0;
	for (p = p_p; p <= p_k; p++)
		classptr->tb_distv[c][p] = 0;
	};

/* scan through pieces in class names */

c = c_b;
for (i = 0; i < classptr->tb_count; i++)
	{
	/* identify current piece */

	ch = *(name + i);
	switch (ch)
		{
		case 'P':
			p = p_p;
			break;
		case 'N':
			p = p_n;
			break;
		case 'B':
			p = p_b;
			break;
		case 'R':
			p = p_r;
			break;
		case 'Q':
			p = p_q;
			break;
		case 'K':
			p = p_k;
			break;
		default:
			EPDSwitchFault("EPDTBInitTBID");
			break;
		};

	/* swap color if current piece is a king */

	if (p == p_k)
		c = inv_cv[c];

	/* set various flags if current piece is a pawn */

	if (p == p_p)
		{
		classptr->tb_flags |= tbf_has_pawns;
		if (c == c_w)
			{
			classptr->tb_flags |= tbf_has_white_pawn;
			if (classptr->tb_flags & tbf_has_black_pawn)
				classptr->tb_flags |= tbf_ep_captures;
			}
		else
			{
			classptr->tb_flags |= tbf_has_black_pawn;
			if (classptr->tb_flags & tbf_has_white_pawn)
				classptr->tb_flags |= tbf_ep_captures;
			};
		};

	/* adjust count and distribution for this color */

	classptr->tb_mcv[c]++;
	classptr->tb_distv[c][p]++;

	/* set piece and color-piece vector for this color */

	if (c == c_w)
		slot = i;
	else
		slot = i - classptr->tb_mcv[c_w];
	classptr->tb_pv[c][slot] = p;
	classptr->tb_cpv[c][slot] = cv_cp_c_pv[c][p];
	};

/* set the distribution signatures */

classptr->tb_sig0 = EPDTBGetSignature(classptr->tb_distv);
classptr->tb_sig1 = EPDTBGetInvertedSignature(classptr->tb_distv);

/* set fold mode information */

if (classptr->tb_flags & tbf_has_pawns)
	{
	classptr->tb_flags |= tbf_fold_flank;
	classptr->tb_fold = fold_flank;
	}
else
	{
	classptr->tb_flags |= tbf_fold_triangle;
	classptr->tb_fold = fold_triangle;
	};

/* calculate file length */

classptr->tb_length = 1;
for (i = 1; i < classptr->tb_count; i++)
	classptr->tb_length *= sqL;
if (classptr->tb_fold == fold_flank)
	classptr->tb_length *= ff_flankL;
else
	classptr->tb_length *= ff_triangleL;

/* calculate pivot color and slot */

if (classptr->tb_fold == fold_triangle)
	{
	classptr->tb_pivot_c = c_b;
	classptr->tb_pivot_slot = classptr->tb_mcv[c_b] - 1;
	}
else
	{
	if (classptr->tb_flags & tbf_has_black_pawn)
		{
		classptr->tb_pivot_c = c_b;
		i = classptr->tb_count - 1;
		while (*(name + i) != ascpv[p_p])
			i--;
		classptr->tb_pivot_slot = i - classptr->tb_mcv[c_w];
		}
	else
		{
		classptr->tb_pivot_c = c_w;
		i = classptr->tb_mcv[c_w] - 1;
		while (*(name + i) != ascpv[p_p])
			i--;
		classptr->tb_pivot_slot = i;
		};
	};

/* calculate scale factors */

for (c = c_w; c <= c_b; c++)
	for (i = 0; i < classptr->tb_mcv[c]; i++)
		if ((c != classptr->tb_pivot_c) || (i != classptr->tb_pivot_slot))
			classptr->tb_scalev[c][i] = sqL;
		else
			if (classptr->tb_fold == fold_triangle)
				classptr->tb_scalev[c][i] = ff_triangleL;
			else
				classptr->tb_scalev[c][i] = ff_flankL;

/* calculate multipliers */

mult = 1;
for (c = c_w; c <= c_b; c++)
	for (i = 0; i < classptr->tb_mcv[c]; i++)
		{
		classptr->tb_multv[c][i] = mult;
		mult *= classptr->tb_scalev[c][i];
		};

return;
}

/*--> EPDTBCacheFlush: flush the tablebase file pointer cache */
nonstatic
void
EPDTBCacheFlush(void)
{
siT i;

for (i = 0; i < tbcL; i++)
	if (tbcv[i].tbc_inuse)
		{
		if (tbcv[i].tbc_fptr != NULL)
			fclose(tbcv[i].tbc_fptr);
		tbcv[i].tbc_inuse = 0;
		};

return;
}

/*--> EPDTBInit: one time initialization for the tablebase subsystem */
static
void
EPDTBInit(void)
{
siT i;
sqT sq;

/* initialize the flank map */

for (sq = sq_a1; sq <= sq_h8; sq++)
	flank_mapv[sq] = -1;

flank_mapv[sq_a1] =  0;
flank_mapv[sq_b1] =  1;
flank_mapv[sq_c1] =  2;
flank_mapv[sq_d1] =  3;
flank_mapv[sq_a2] =  4;
flank_mapv[sq_b2] =  5;
flank_mapv[sq_c2] =  6;
flank_mapv[sq_d2] =  7;
flank_mapv[sq_a3] =  8;
flank_mapv[sq_b3] =  9;
flank_mapv[sq_c3] = 10;
flank_mapv[sq_d3] = 11;
flank_mapv[sq_a4] = 12;
flank_mapv[sq_b4] = 13;
flank_mapv[sq_c4] = 14;
flank_mapv[sq_d4] = 15;
flank_mapv[sq_a5] = 16;
flank_mapv[sq_b5] = 17;
flank_mapv[sq_c5] = 18;
flank_mapv[sq_d5] = 19;
flank_mapv[sq_a6] = 20;
flank_mapv[sq_b6] = 21;
flank_mapv[sq_c6] = 22;
flank_mapv[sq_d6] = 23;
flank_mapv[sq_a7] = 24;
flank_mapv[sq_b7] = 25;
flank_mapv[sq_c7] = 26;
flank_mapv[sq_d7] = 27;
flank_mapv[sq_a8] = 28;
flank_mapv[sq_b8] = 29;
flank_mapv[sq_c8] = 30;
flank_mapv[sq_d8] = 31;

/* initialize the triangle map */

for (sq = sq_a1; sq <= sq_h8; sq++)
	triangle_mapv[sq] = -1;

triangle_mapv[sq_a1] = 0;
triangle_mapv[sq_b1] = 1;
triangle_mapv[sq_c1] = 2;
triangle_mapv[sq_d1] = 3;
triangle_mapv[sq_b2] = 4;
triangle_mapv[sq_c2] = 5;
triangle_mapv[sq_d2] = 6;
triangle_mapv[sq_c3] = 7;
triangle_mapv[sq_d3] = 8;
triangle_mapv[sq_d4] = 9;

/* set the color to move tablebase file name suffixes */

ctmext_strv[c_w] = ".tbw";
ctmext_strv[c_b] = ".tbb";

/* allocate tablebase information vector */

tbbaseptr = (tbptrT) EPDMemoryGrab(sizeof(tbT) * tbidL);

/* initialize the tablebase vector */

EPDTBInitTBID(tbid_kk,    "KK");
EPDTBInitTBID(tbid_kpk,   "KPK");
EPDTBInitTBID(tbid_knk,   "KNK");
EPDTBInitTBID(tbid_kbk,   "KBK");
EPDTBInitTBID(tbid_krk,   "KRK");
EPDTBInitTBID(tbid_kqk,   "KQK");
EPDTBInitTBID(tbid_kpkp,  "KPKP");
EPDTBInitTBID(tbid_knkp,  "KNKP");
EPDTBInitTBID(tbid_knkn,  "KNKN");
EPDTBInitTBID(tbid_kbkp,  "KBKP");
EPDTBInitTBID(tbid_kbkn,  "KBKN");
EPDTBInitTBID(tbid_kbkb,  "KBKB");
EPDTBInitTBID(tbid_krkp,  "KRKP");
EPDTBInitTBID(tbid_krkn,  "KRKN");
EPDTBInitTBID(tbid_krkb,  "KRKB");
EPDTBInitTBID(tbid_krkr,  "KRKR");
EPDTBInitTBID(tbid_kqkp,  "KQKP");
EPDTBInitTBID(tbid_kqkn,  "KQKN");
EPDTBInitTBID(tbid_kqkb,  "KQKB");
EPDTBInitTBID(tbid_kqkr,  "KQKR");
EPDTBInitTBID(tbid_kqkq,  "KQKQ");
EPDTBInitTBID(tbid_kppk,  "KPPK");
EPDTBInitTBID(tbid_knpk,  "KNPK");
EPDTBInitTBID(tbid_knnk,  "KNNK");
EPDTBInitTBID(tbid_kbpk,  "KBPK");
EPDTBInitTBID(tbid_kbnk,  "KBNK");
EPDTBInitTBID(tbid_kbbk,  "KBBK");
EPDTBInitTBID(tbid_krpk,  "KRPK");
EPDTBInitTBID(tbid_krnk,  "KRNK");
EPDTBInitTBID(tbid_krbk,  "KRBK");
EPDTBInitTBID(tbid_krrk,  "KRRK");
EPDTBInitTBID(tbid_kqpk,  "KQPK");
EPDTBInitTBID(tbid_kqnk,  "KQNK");
EPDTBInitTBID(tbid_kqbk,  "KQBK");
EPDTBInitTBID(tbid_kqrk,  "KQRK");
EPDTBInitTBID(tbid_kqqk,  "KQQK");
EPDTBInitTBID(tbid_kppkp, "KPPKP");
EPDTBInitTBID(tbid_kppkn, "KPPKN");
EPDTBInitTBID(tbid_kppkb, "KPPKB");
EPDTBInitTBID(tbid_kppkr, "KPPKR");
EPDTBInitTBID(tbid_kppkq, "KPPKQ");
EPDTBInitTBID(tbid_knpkp, "KNPKP");
EPDTBInitTBID(tbid_knpkn, "KNPKN");
EPDTBInitTBID(tbid_knpkb, "KNPKB");
EPDTBInitTBID(tbid_knpkr, "KNPKR");
EPDTBInitTBID(tbid_knpkq, "KNPKQ");
EPDTBInitTBID(tbid_knnkp, "KNNKP");
EPDTBInitTBID(tbid_knnkn, "KNNKN");
EPDTBInitTBID(tbid_knnkb, "KNNKB");
EPDTBInitTBID(tbid_knnkr, "KNNKR");
EPDTBInitTBID(tbid_knnkq, "KNNKQ");
EPDTBInitTBID(tbid_kbpkp, "KBPKP");
EPDTBInitTBID(tbid_kbpkn, "KBPKN");
EPDTBInitTBID(tbid_kbpkb, "KBPKB");
EPDTBInitTBID(tbid_kbpkr, "KBPKR");
EPDTBInitTBID(tbid_kbpkq, "KBPKQ");
EPDTBInitTBID(tbid_kbnkp, "KBNKP");
EPDTBInitTBID(tbid_kbnkn, "KBNKN");
EPDTBInitTBID(tbid_kbnkb, "KBNKB");
EPDTBInitTBID(tbid_kbnkr, "KBNKR");
EPDTBInitTBID(tbid_kbnkq, "KBNKQ");
EPDTBInitTBID(tbid_kbbkp, "KBBKP");
EPDTBInitTBID(tbid_kbbkn, "KBBKN");
EPDTBInitTBID(tbid_kbbkb, "KBBKB");
EPDTBInitTBID(tbid_kbbkr, "KBBKR");
EPDTBInitTBID(tbid_kbbkq, "KBBKQ");
EPDTBInitTBID(tbid_krpkp, "KRPKP");
EPDTBInitTBID(tbid_krpkn, "KRPKN");
EPDTBInitTBID(tbid_krpkb, "KRPKB");
EPDTBInitTBID(tbid_krpkr, "KRPKR");
EPDTBInitTBID(tbid_krpkq, "KRPKQ");
EPDTBInitTBID(tbid_krnkp, "KRNKP");
EPDTBInitTBID(tbid_krnkn, "KRNKN");
EPDTBInitTBID(tbid_krnkb, "KRNKB");
EPDTBInitTBID(tbid_krnkr, "KRNKR");
EPDTBInitTBID(tbid_krnkq, "KRNKQ");
EPDTBInitTBID(tbid_krbkp, "KRBKP");
EPDTBInitTBID(tbid_krbkn, "KRBKN");
EPDTBInitTBID(tbid_krbkb, "KRBKB");
EPDTBInitTBID(tbid_krbkr, "KRBKR");
EPDTBInitTBID(tbid_krbkq, "KRBKQ");
EPDTBInitTBID(tbid_krrkp, "KRRKP");
EPDTBInitTBID(tbid_krrkn, "KRRKN");
EPDTBInitTBID(tbid_krrkb, "KRRKB");
EPDTBInitTBID(tbid_krrkr, "KRRKR");
EPDTBInitTBID(tbid_krrkq, "KRRKQ");
EPDTBInitTBID(tbid_kqpkp, "KQPKP");
EPDTBInitTBID(tbid_kqpkn, "KQPKN");
EPDTBInitTBID(tbid_kqpkb, "KQPKB");
EPDTBInitTBID(tbid_kqpkr, "KQPKR");
EPDTBInitTBID(tbid_kqpkq, "KQPKQ");
EPDTBInitTBID(tbid_kqnkp, "KQNKP");
EPDTBInitTBID(tbid_kqnkn, "KQNKN");
EPDTBInitTBID(tbid_kqnkb, "KQNKB");
EPDTBInitTBID(tbid_kqnkr, "KQNKR");
EPDTBInitTBID(tbid_kqnkq, "KQNKQ");
EPDTBInitTBID(tbid_kqbkp, "KQBKP");
EPDTBInitTBID(tbid_kqbkn, "KQBKN");
EPDTBInitTBID(tbid_kqbkb, "KQBKB");
EPDTBInitTBID(tbid_kqbkr, "KQBKR");
EPDTBInitTBID(tbid_kqbkq, "KQBKQ");
EPDTBInitTBID(tbid_kqrkp, "KQRKP");
EPDTBInitTBID(tbid_kqrkn, "KQRKN");
EPDTBInitTBID(tbid_kqrkb, "KQRKB");
EPDTBInitTBID(tbid_kqrkr, "KQRKR");
EPDTBInitTBID(tbid_kqrkq, "KQRKQ");
EPDTBInitTBID(tbid_kqqkp, "KQQKP");
EPDTBInitTBID(tbid_kqqkn, "KQQKN");
EPDTBInitTBID(tbid_kqqkb, "KQQKB");
EPDTBInitTBID(tbid_kqqkr, "KQQKR");
EPDTBInitTBID(tbid_kqqkq, "KQQKQ");
EPDTBInitTBID(tbid_kpppk, "KPPPK");
EPDTBInitTBID(tbid_knppk, "KNPPK");
EPDTBInitTBID(tbid_knnpk, "KNNPK");
EPDTBInitTBID(tbid_knnnk, "KNNNK");
EPDTBInitTBID(tbid_kbppk, "KBPPK");
EPDTBInitTBID(tbid_kbnpk, "KBNPK");
EPDTBInitTBID(tbid_kbnnk, "KBNNK");
EPDTBInitTBID(tbid_kbbpk, "KBBPK");
EPDTBInitTBID(tbid_kbbnk, "KBBNK");
EPDTBInitTBID(tbid_kbbbk, "KBBBK");
EPDTBInitTBID(tbid_krppk, "KRPPK");
EPDTBInitTBID(tbid_krnpk, "KRNPK");
EPDTBInitTBID(tbid_krnnk, "KRNNK");
EPDTBInitTBID(tbid_krbpk, "KRBPK");
EPDTBInitTBID(tbid_krbnk, "KRBNK");
EPDTBInitTBID(tbid_krbbk, "KRBBK");
EPDTBInitTBID(tbid_krrpk, "KRRPK");
EPDTBInitTBID(tbid_krrnk, "KRRNK");
EPDTBInitTBID(tbid_krrbk, "KRRBK");
EPDTBInitTBID(tbid_krrrk, "KRRRK");
EPDTBInitTBID(tbid_kqppk, "KQPPK");
EPDTBInitTBID(tbid_kqnpk, "KQNPK");
EPDTBInitTBID(tbid_kqnnk, "KQNNK");
EPDTBInitTBID(tbid_kqbpk, "KQBPK");
EPDTBInitTBID(tbid_kqbnk, "KQBNK");
EPDTBInitTBID(tbid_kqbbk, "KQBBK");
EPDTBInitTBID(tbid_kqrpk, "KQRPK");
EPDTBInitTBID(tbid_kqrnk, "KQRNK");
EPDTBInitTBID(tbid_kqrbk, "KQRBK");
EPDTBInitTBID(tbid_kqrrk, "KQRRK");
EPDTBInitTBID(tbid_kqqpk, "KQQPK");
EPDTBInitTBID(tbid_kqqnk, "KQQNK");
EPDTBInitTBID(tbid_kqqbk, "KQQBK");
EPDTBInitTBID(tbid_kqqrk, "KQQRK");
EPDTBInitTBID(tbid_kqqqk, "KQQQK");

/* clear the file pointer cache vector */

for (i = 0; i < tbcL; i++)
	tbcv[i].tbc_inuse = 0;

return;
}

/*--> EPDTBTerm: one time termination for the tablebase subsystem */
static
void
EPDTBTerm(void)
{
tbidT tbid;
tbptrT tbptr;

/* flush the file pointer cache vector */

EPDTBCacheFlush();

/* deallocate tablebase information vector entries */

tbptr = tbbaseptr;
for (tbid = 0; tbid < tbidL; tbid++)
	{
	EPDMemoryFree(tbptr->tb_name);
	tbptr++;
	};

/* deallocate tablebase information vector */

EPDMemoryFree(tbbaseptr);

return;
}

/*--> EPDInit: one time initialization for EPD */
nonstatic
void
EPDInit(void)
{
cpT cp;
sqT sq;
xsqT xsq;

/* this sets up the current position */

/* initialize ascii color conversion vector */

asccv[c_w] = 'w';
asccv[c_b] = 'b';

/* initialize ascii piece conversion vector */

ascpv[p_p] = 'P';
ascpv[p_n] = 'N';
ascpv[p_b] = 'B';
ascpv[p_r] = 'R';
ascpv[p_q] = 'Q';
ascpv[p_k] = 'K';

/* initialize ascii rank conversion vector */

ascrv[rank_1] = '1';
ascrv[rank_2] = '2';
ascrv[rank_3] = '3';
ascrv[rank_4] = '4';
ascrv[rank_5] = '5';
ascrv[rank_6] = '6';
ascrv[rank_7] = '7';
ascrv[rank_8] = '8';

/* initialize ascii file conversion vector */

ascfv[file_a] = 'a';
ascfv[file_b] = 'b';
ascfv[file_c] = 'c';
ascfv[file_d] = 'd';
ascfv[file_e] = 'e';
ascfv[file_f] = 'f';
ascfv[file_g] = 'g';
ascfv[file_h] = 'h';

/* initialize piece letter from special case move indicator */

cv_p_scmvv[scmv_reg] = p_nil;
cv_p_scmvv[scmv_epc] = p_nil;
cv_p_scmvv[scmv_cks] = p_nil;
cv_p_scmvv[scmv_cqs] = p_nil;
cv_p_scmvv[scmv_ppn] = p_n;
cv_p_scmvv[scmv_ppb] = p_b;
cv_p_scmvv[scmv_ppr] = p_r;
cv_p_scmvv[scmv_ppq] = p_q;

/* initialize various color piece conversion arrays */

for (cp = cp_wp; cp <= cp_wk; cp++)
	cv_c_cpv[cp] = c_w;
for (cp = cp_bp; cp <= cp_bk; cp++)
	cv_c_cpv[cp] = c_b;
cv_c_cpv[cp_v0] = c_v;
cv_c_cpv[cp_x0] = cv_c_cpv[cp_x1] = cv_c_cpv[cp_x2] = c_x;

cv_p_cpv[cp_wp] = cv_p_cpv[cp_bp] = p_p;
cv_p_cpv[cp_wn] = cv_p_cpv[cp_bn] = p_n;
cv_p_cpv[cp_wb] = cv_p_cpv[cp_bb] = p_b;
cv_p_cpv[cp_wr] = cv_p_cpv[cp_br] = p_r;
cv_p_cpv[cp_wq] = cv_p_cpv[cp_bq] = p_q;
cv_p_cpv[cp_wk] = cv_p_cpv[cp_bk] = p_k;
cv_p_cpv[cp_v0] = p_v;
cv_p_cpv[cp_x0] = cv_p_cpv[cp_x1] = cv_p_cpv[cp_x2] = p_x;

cv_cp_c_pv[c_w][p_p] = cp_wp;
cv_cp_c_pv[c_w][p_n] = cp_wn;
cv_cp_c_pv[c_w][p_b] = cp_wb;
cv_cp_c_pv[c_w][p_r] = cp_wr;
cv_cp_c_pv[c_w][p_q] = cp_wq;
cv_cp_c_pv[c_w][p_k] = cp_wk;
cv_cp_c_pv[c_b][p_p] = cp_bp;
cv_cp_c_pv[c_b][p_n] = cp_bn;
cv_cp_c_pv[c_b][p_b] = cp_bb;
cv_cp_c_pv[c_b][p_r] = cp_br;
cv_cp_c_pv[c_b][p_q] = cp_bq;
cv_cp_c_pv[c_b][p_k] = cp_bk;

inv_cv[c_w] = c_b;
inv_cv[c_b] = c_w;

/* initialize directional vectors */

dvv[dx_0] = dv_0;
dvv[dx_1] = dv_1;
dvv[dx_2] = dv_2;
dvv[dx_3] = dv_3;
dvv[dx_4] = dv_4;
dvv[dx_5] = dv_5;
dvv[dx_6] = dv_6;
dvv[dx_7] = dv_7;
dvv[dx_8] = dv_8;
dvv[dx_9] = dv_9;
dvv[dx_a] = dv_a;
dvv[dx_b] = dv_b;
dvv[dx_c] = dv_c;
dvv[dx_d] = dv_d;
dvv[dx_e] = dv_e;
dvv[dx_f] = dv_f;

xdvv[dx_0] = xdv_0;
xdvv[dx_1] = xdv_1;
xdvv[dx_2] = xdv_2;
xdvv[dx_3] = xdv_3;
xdvv[dx_4] = xdv_4;
xdvv[dx_5] = xdv_5;
xdvv[dx_6] = xdv_6;
xdvv[dx_7] = xdv_7;
xdvv[dx_8] = xdv_8;
xdvv[dx_9] = xdv_9;
xdvv[dx_a] = xdv_a;
xdvv[dx_b] = xdv_b;
xdvv[dx_c] = xdv_c;
xdvv[dx_d] = xdv_d;
xdvv[dx_e] = xdv_e;
xdvv[dx_f] = xdv_f;

/* initialize the extended board */

for (xsq = 0; xsq < xsqL; xsq++)
	xb.xbv[xsq] = cp_x0;

for (sq = sq_a1; sq <= sq_h8; sq++)
	xb.xbv[map_xsq_sq(sq)] = cp_v0;

/* initialize the standard opcode string vector */

epdsostrv[epdso_acd        ] = "acd";
epdsostrv[epdso_acn        ] = "acn";
epdsostrv[epdso_acs        ] = "acs";
epdsostrv[epdso_am         ] = "am";
epdsostrv[epdso_bm         ] = "bm";
epdsostrv[epdso_c0         ] = "c0";
epdsostrv[epdso_c1         ] = "c1";
epdsostrv[epdso_c2         ] = "c2";
epdsostrv[epdso_c3         ] = "c3";
epdsostrv[epdso_c4         ] = "c4";
epdsostrv[epdso_c5         ] = "c5";
epdsostrv[epdso_c6         ] = "c6";
epdsostrv[epdso_c7         ] = "c7";
epdsostrv[epdso_c8         ] = "c8";
epdsostrv[epdso_c9         ] = "c9";
epdsostrv[epdso_cc         ] = "cc";
epdsostrv[epdso_ce         ] = "ce";
epdsostrv[epdso_dm         ] = "dm";
epdsostrv[epdso_draw_accept] = "draw_accept";
epdsostrv[epdso_draw_claim ] = "draw_claim";
epdsostrv[epdso_draw_offer ] = "draw_offer";
epdsostrv[epdso_draw_reject] = "draw_reject";
epdsostrv[epdso_eco        ] = "eco";
epdsostrv[epdso_fmvn       ] = "fmvn";
epdsostrv[epdso_hmvc       ] = "hmvc";
epdsostrv[epdso_id         ] = "id";
epdsostrv[epdso_nic        ] = "nic";
epdsostrv[epdso_noop       ] = "noop";
epdsostrv[epdso_pm         ] = "pm";
epdsostrv[epdso_ptp        ] = "ptp";
epdsostrv[epdso_pv         ] = "pv";
epdsostrv[epdso_rc         ] = "rc";
epdsostrv[epdso_refcom     ] = "refcom";
epdsostrv[epdso_refreq     ] = "refreq";
epdsostrv[epdso_resign     ] = "resign";
epdsostrv[epdso_sm         ] = "sm";
epdsostrv[epdso_sv         ] = "sv";
epdsostrv[epdso_tcgs       ] = "tcgs";
epdsostrv[epdso_tcri       ] = "tcri";
epdsostrv[epdso_tcsi       ] = "tcsi";
epdsostrv[epdso_ts         ] = "ts";
epdsostrv[epdso_v0         ] = "v0";
epdsostrv[epdso_v1         ] = "v1";
epdsostrv[epdso_v2         ] = "v2";
epdsostrv[epdso_v3         ] = "v3";
epdsostrv[epdso_v4         ] = "v4";
epdsostrv[epdso_v5         ] = "v5";
epdsostrv[epdso_v6         ] = "v6";
epdsostrv[epdso_v7         ] = "v7";
epdsostrv[epdso_v8         ] = "v8";
epdsostrv[epdso_v9         ] = "v9";

/* set the EPD refcom operand strings */

refcomstrv[refcom_conclude  ] = "conclude";
refcomstrv[refcom_disconnect] = "disconnect";
refcomstrv[refcom_execute   ] = "execute";
refcomstrv[refcom_fault     ] = "fault";
refcomstrv[refcom_inform    ] = "inform";
refcomstrv[refcom_respond   ] = "respond";
refcomstrv[refcom_reset     ] = "reset";

/* set the EPD refreq operand strings */

refreqstrv[refreq_fault   ] = "fault";
refreqstrv[refreq_reply   ] = "reply";
refreqstrv[refreq_sign_on ] = "sign_on";
refreqstrv[refreq_sign_off] = "sign_off";

/* set the PGN Seven Tag Roster names */

pgnstrstrv[pgnstr_event ] = "Event";
pgnstrstrv[pgnstr_site  ] = "Site";
pgnstrstrv[pgnstr_date  ] = "Date";
pgnstrstrv[pgnstr_round ] = "Round";
pgnstrstrv[pgnstr_white ] = "White";
pgnstrstrv[pgnstr_black ] = "Black";
pgnstrstrv[pgnstr_result] = "Result";

/* set the game termination indication marker vector */

gtimstrv[gtim_w] = "1-0";
gtimstrv[gtim_b] = "0-1";
gtimstrv[gtim_d] = "1/2-1/2";
gtimstrv[gtim_u] = "*";

/* set the player name string vector */

playerstrv[c_w] = "White";
playerstrv[c_b] = "Black";

/* clear the global game chain anchor pointers */

head_gamptr = tail_gamptr = NULL;

/* clear the token chain anchor pointers */

head_tknptr = tail_tknptr = NULL;

/* clear the current ply */

ply = 0;

/* allocate the move tree */

treeptr = treebaseptr = (mptrT) EPDMemoryGrab(sizeof(mT) * treeL);

/* allocate the tree stack entry stack */

tseptr = tsebaseptr = (tseptrT) EPDMemoryGrab(sizeof(tseT) * pmhL);

/* allocate the environment stack entry stack */

eseptr = esebaseptr = (eseptrT) EPDMemoryGrab(sizeof(eseT) * pmhL);

/* set the current position to be the initial array */

EPDInitArray();

/* generation */

EPDGenMoves();

/* initialize the tablebase subsystem */

EPDTBInit();

return;
}

/*--> EPDTerm: one time termination for EPD */
nonstatic
void
EPDTerm(void)
{
/* terminate the tablebase subsystem */

EPDTBTerm();

/* release any existing game chain */

EPDReleaseGameChain();

/* release any existing token chain */

EPDReleaseTokenChain();

/* deallocate various stacks */

EPDMemoryFree(esebaseptr);
EPDMemoryFree(tsebaseptr);
EPDMemoryFree(treebaseptr);

/* "Wanna see my sprocket collection?" */

return;
}

/*<<< epd.c: EOF */
