/***************************************************************************
                          logic.cpp  -  description
                             -------------------
    begin                : Sat Sep 29 2001
    copyright            : (C) 2003 by Troy Corbin Jr.
    email                : tcorbin@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "logic.h"
#include "dlg_promote.h"
#include "command.h"
#include <stdlib.h>

///////////////////////////////////////
//
//	logic::logic
//
///////////////////////////////////////
logic::logic( resource *Rsrc, match_param *param )
{
	Resource = Rsrc;
	Param = param;
	int tmp;

	GameType = Type_Standard;
	for( tmp = 0; tmp < 64; tmp++ )
	{
		current[tmp].File = ( tmp % 8 );
		current[tmp].Rank = ( tmp >> 3 );
	}
	clearBoard();
}
///////////////////////////////////////
//
//	logic::~logic
//
///////////////////////////////////////
logic::~logic()
{
}
///////////////////////////////////////
//
//	logic::Pointer
//
///////////////////////////////////////
int logic::Pointer( const char File, const char Rank )
{
	if( ( File < 0 ) || ( File > 7 ) ) return Null;
	if( ( Rank < 0 ) || ( Rank > 7 ) ) return Null;
	return ( (  Rank << 3 ) + File );
}
///////////////////////////////////////
//
//	logic::CalcPointer
//
///////////////////////////////////////
int logic::CalcPointer( const char File, const char Rank )
{
	char tmpFile, tmpRank;

	tmpFile = chessman[ManPtr].File + File;
	tmpRank = chessman[ManPtr].Rank + Rank;
	return Pointer( tmpFile, tmpRank );
}
///////////////////////////////////////
//
//	logic::isChessman
//
///////////////////////////////////////
bool logic::isChessman( const char ChessmanPtr )
{
	char BoardPtr = Pointer( chessman[ChessmanPtr].File, chessman[ChessmanPtr].Rank );
	if( ( BoardPtr < 0 ) || ( BoardPtr > 63 ) ) return FALSE;
	if( current[BoardPtr].ManPtr != ChessmanPtr ) return FALSE;
	return TRUE;
}
///////////////////////////////////////
//
//	logic::clearBoard
//
///////////////////////////////////////
void logic::clearBoard( void )
{
	for( int tmp = 0; tmp < 64; tmp++ )
	{
		chessman[tmp].Type = Null;
		current[tmp].ManPtr = Null;
		current[tmp].Note = NOTE_NONE;
	}
}
///////////////////////////////////////
//
//	logic::Init
//
///////////////////////////////////////
void logic::Init( const int Var )
{
	GameType = Var;
	switch( GameType )
	{
		case Type_Standard:	// Fall Through
		default:
			Init_Standard();
			break;
	}
}
///////////////////////////////////////
//
//	logic::Init_Standard
//
///////////////////////////////////////
void logic::Init_Standard( void )
{
	int tmp;

	clearBoard();
	for( tmp = 0; tmp < 32; tmp++ )
	{
		if( tmp < 16 )
		{
			chessman[tmp].Army = WHITE;
			chessman[tmp].Rank = 0;
		}
		else
		{
			chessman[tmp].Army = BLACK;
			chessman[tmp].Rank = 7;
		}
		switch( tmp % 16 )
		{
			case 0:
				chessman[tmp].Type = King;
				chessman[tmp].File = 4;
				break;
			case 1:
				chessman[tmp].Type = Queen;
				chessman[tmp].File = 3;
				break;
			case 2:
				chessman[tmp].Type = Bishop;
				chessman[tmp].File = 2;
				break;
			case 3:
				chessman[tmp].Type = Bishop;
				chessman[tmp].File = 5;
				break;
			case 4:
				chessman[tmp].Type = Knight;
				chessman[tmp].File = 1;
				break;
			case 5:
				chessman[tmp].Type = Knight;
				chessman[tmp].File = 6;
				break;
			case 6:
				chessman[tmp].Type = Rook;
				chessman[tmp].File = 0;
				break;
			case 7:
				chessman[tmp].Type = Rook;
				chessman[tmp].File = 7;
				break;
			default:
				chessman[tmp].Type = Pawn;
				chessman[tmp].File = ( tmp % 16 ) - 8;
				if( chessman[tmp].Army == WHITE ) chessman[tmp].Rank = 1;
				else chessman[tmp].Rank = 6;
				break;
		}
		current[ Pointer( chessman[tmp].File, chessman[tmp].Rank ) ].ManPtr = tmp;
		CastleFlag[0] = CF_King + CF_RookQ + CF_RookK;
		CastleFlag[1] = CF_King + CF_RookQ + CF_RookK;
		OnMove = WHITE;
	}
}
///////////////////////////////////////
//
//	logic::parseCAN
//
///////////////////////////////////////
bool logic::parseCAN( const bool Army )
{
	if( chessMove.CAN == NULL ) return FALSE;
 	if( ( chessMove.CAN[0] != 'o' ) && ( chessMove.CAN[0] != 'O' ) )
 	{
		if( ( chessMove.CAN[0] < 'a' ) || ( chessMove.CAN[0] > 'h' ) ) return FALSE;
 		chessMove.fromFile = chessMove.CAN[0] - 97;
		if( ( chessMove.CAN[1] < '1' ) || ( chessMove.CAN[1] > '8' ) ) return FALSE;
 		chessMove.fromRank = chessMove.CAN[1] - 49;
		if( ( chessMove.CAN[2] < 'a' ) || ( chessMove.CAN[2] > 'h' ) ) return FALSE;
 		chessMove.toFile = chessMove.CAN[2] - 97;
		if( ( chessMove.CAN[3] < '1' ) || ( chessMove.CAN[3] > '8' ) ) return FALSE;
 		chessMove.toRank = chessMove.CAN[3] - 49;
		if( strlen( chessMove.CAN ) == 5 ) chessMove.Promote = chessMove.CAN[4];
		else chessMove.Promote = Null;
 	}
	/*
			For some reason some engines w/ CAN output
			express castling using SAN, not to name names GNUChess v4
	*/
 	else
 	{
 		chessMove.fromFile = 4;
 		if( TQString( chessMove.CAN ).lower() == "o-o" ) chessMove.toFile = 6;
 		else chessMove.toFile = 2;
 		if( Army == WHITE )
 		{
 			chessMove.fromRank = 0;
 			chessMove.toRank = 0;
 		}
 		else
 		{
 			chessMove.fromRank = 7;
 			chessMove.toRank = 7;
 		}
 	}
	return TRUE;
}
///////////////////////////////////////
//
//	logic::parseSAN
//
///////////////////////////////////////
bool logic::parseSAN( void )
{
	bool Army(OnMove);
	char Type(Pawn);
	char SANPtr(0), tmp(0);

	chessMove.fromFile = Null;
	chessMove.fromRank = Null;
	chessMove.toFile = Null;
	chessMove.toRank = Null;
	chessMove.Promote = Null;
	chessMove.ManTaken = Null;
	chessMove.NAG = 0;
	while( SANPtr < (signed)TQString( chessMove.SAN ).length() )
	{
		/* Parse a character */
		switch( chessMove.SAN[SANPtr] )
		{
			case 'K':
				if( SANPtr == 0 )
					Type = King;
				else
					chessMove.Promote = 'k';
				break;
			case 'Q':
				if( SANPtr == 0 )
					Type = Queen;
				else
					chessMove.Promote = 'q';
				break;
			case 'B':
				if( SANPtr == 0 )
					Type = Bishop;
				else
					chessMove.Promote = 'b';
				break;
			case 'N':
				if( SANPtr == 0 )
					Type = Knight;
				else
					chessMove.Promote = 'n';
				break;
			case 'R':
				if( SANPtr == 0 )
					Type = Rook;
				else
					chessMove.Promote = 'r';
				break;
			/* Parse castle */
			case 'o':
			case 'O':
				if( SANPtr != 0 )
					break;
				Type = King;
				if( Army == WHITE )
					chessMove.toRank = 0;
				else
					chessMove.toRank = 7;
				if( TQString( chessMove.SAN ).lower() == "o-o-o" )
					chessMove.toFile = 2;
				if( TQString( chessMove.SAN ).lower() == "o-o" )
					chessMove.toFile = 6;
				break;
			/* Ignore some symbols... these fall through */
			case 'x':
			case '=':
			case '#':
			case '+':
			case '-':
			case 'P':
				break;
			/* Handle annotations */
			case '!':
				chessMove.NAG = 1;
				if( chessMove.SAN[SANPtr - 1] == '!' )
					chessMove.NAG = 3;
				if( chessMove.SAN[SANPtr - 1] == '?' )
					chessMove.NAG = 6;
				break;
			case '?':
				chessMove.NAG = 2;
				if( chessMove.SAN[SANPtr - 1] == '!' )
					chessMove.NAG = 5;
				if( chessMove.SAN[SANPtr - 1] == '?' )
					chessMove.NAG = 4;
				break;
			default:
				if( ( chessMove.SAN[SANPtr] >= '1' ) && ( chessMove.SAN[SANPtr] <= '8' ) )
				{
					if( chessMove.toRank != Null )
						chessMove.fromRank = chessMove.toRank;
					chessMove.toRank = chessMove.SAN[SANPtr] - 49;
					break;
				}
				if( ( chessMove.SAN[SANPtr] >= 'a' ) && ( chessMove.SAN[SANPtr] <= 'h' ) )
				{
					if( chessMove.toFile != Null )
						chessMove.fromFile = chessMove.toFile;
					chessMove.toFile = chessMove.SAN[SANPtr] - 97;
					break;
				}
				/* Unknown symbol... Can not process this chessMove */
//				kdDebug() << "logic::ParseSAN: Unknown Symbol: " << chessMove.SAN[SANPtr] << "\n";
				return FALSE;
				break;				
		}
		SANPtr++;
	}
	for( tmp = 0; tmp < 64; tmp++ )
	{
		if( chessman[tmp].Type != Type )
			continue;
		if( chessman[tmp].Army != Army )
			continue;
		if( ( chessMove.fromFile != Null ) && ( chessman[tmp].File != chessMove.fromFile ) )
			continue;
		if( ( chessMove.fromRank != Null ) && ( chessman[tmp].Rank != chessMove.fromRank ) )
			continue;
		if( !isChessman( tmp ) )
			continue;
		HashLegal( tmp );
		if( current[ ( chessMove.toRank << 3 ) + chessMove.toFile ].Note < NOTE_MOVE )
			continue;
		chessMove.fromFile = chessman[tmp].File;
		chessMove.fromRank = chessman[tmp].Rank;
		break;
	}
	if( tmp == 64 )
	{
//		kdWarning() << "logic::ParseSAN could not make a legal chessMove out of " << TQString( chessMove.SAN ) << endl;
//		kdWarning() << (int)Army << " " << (int)Type << " " << (int)chessMove.fromFile << " " << (int)chessMove.fromRank
//							<< " " << (int)chessMove.toFile << " " << (int)chessMove.toRank << endl;
		return FALSE;
	}
	return TRUE;
}
///////////////////////////////////////
//
//	logic::writeCAN
//
///////////////////////////////////////
void logic::writeCAN( void )
{
 	chessMove.CAN[0] = chessMove.fromFile + 97;
 	chessMove.CAN[1] = chessMove.fromRank + 49;
 	chessMove.CAN[2] = chessMove.toFile + 97;
 	chessMove.CAN[3] = chessMove.toRank + 49;
 	if( chessMove.Promote == Null ) chessMove.CAN[4] = 0;
	else
	{
		chessMove.CAN[4] = chessMove.Promote;
		chessMove.CAN[5] = 0;
	}
}
///////////////////////////////////////
//
//	logic::writeSAN
//
///////////////////////////////////////
void logic::writeSAN( void )
{
	Position backup[64];
	bool SANambig(FALSE);
	bool SANambig2(FALSE);
	char tmp, manPtr, toPtr, fromPtr;
	TQString SAN;

	/*
		writeSAN calls HashLegal(), which writes on current[],
		which we need intact for Move()... so we need a backup
		copy of current[]. Removing this breaks en passant moves
	*/
	copyPositions( current, backup );
	fromPtr = ( chessMove.fromRank << 3 ) + chessMove.fromFile;
	toPtr = ( chessMove.toRank << 3 ) + chessMove.toFile;
	if( ( fromPtr > 63 ) || ( toPtr > 63 ) ) return;
	if( ( fromPtr < 0 ) || ( toPtr < 0 ) ) return;
	manPtr = current[fromPtr].ManPtr;
	if( manPtr == Null ) return;

 	/* Check ambiguity for SAN notation */
	for( tmp = 0; tmp < 64; tmp++ )
	{
		if( tmp == manPtr ) continue;
		if( !isChessman( tmp ) ) continue;
		if( chessman[tmp].Army == chessman[manPtr].Army )
		{
			if( chessman[tmp].Type == chessman[manPtr].Type )
			{
				HashLegal( tmp );
				if( current[toPtr].Note >= NOTE_MOVE )
				{
					SANambig = TRUE;
					if( chessman[tmp].File == chessman[manPtr].File )
							SANambig2 = TRUE;
				}
			}
			/*
					This IF was added to fix an ambiguity that occurs when a pawn
					on B file and a Bishop can attack the same spot
			*/
			else
				if( ( ( chessman[manPtr].Type == Bishop ) && ( chessman[tmp].Type == Pawn ) ) ||
						( ( chessman[manPtr].Type == Pawn ) && ( chessman[tmp].Type == Bishop ) ) )
						{
							if( ( chessman[manPtr].File == 1 ) || ( chessman[tmp].File == 1 ) )
							{
								HashLegal( tmp );
								if( current[toPtr].Note >= NOTE_MOVE )
								{
									SANambig = TRUE;
									SANambig2 = TRUE;
								}
							}
						}
		}
	}
	/* Go ahead and restore the backup. */
	copyPositions( backup, current );
	if( ( ( current[toPtr].Note == NOTE_ATTACK ) ||
				( current[toPtr].Note == NOTE_ENPASSANT ) ) &&
				( chessman[manPtr].Type == Pawn ) )
	{
		SANambig = TRUE;
	}
	/* Write SAN Notation */
	if( current[toPtr].Note == NOTE_CASTLE )
	{
		if( chessMove.toFile == 6 ) SAN = "O-O";
		if( chessMove.toFile == 2 ) SAN = "O-O-O";
	}
	else
	{
		switch( chessman[manPtr].Type )
		{
			case King:
				SAN += 'K';
				break;
			case Queen:
				SAN += 'Q';
				break;
			case Bishop:
				SAN += 'B';
				break;
			case Knight:
				SAN += 'N';
				break;
			case Rook:
				SAN += 'R';
				break;
			case Pawn:
//				if( SANambig2 ) SAN += 'P';
				break;
			default:
				break;
		}
		if( SANambig == TRUE )
		{
			SAN += char( chessMove.fromFile + 97 );
			if( SANambig2 ) SAN += char( chessMove.fromRank + 49 );
		}
		if( ( current[toPtr].Note == NOTE_ATTACK ) ||
				( current[toPtr].Note == NOTE_ENPASSANT ) )
				SAN += 'x';
		SAN += char( chessMove.toFile + 97 );
		SAN += char( chessMove.toRank + 49 );
 		switch( chessMove.Promote )
 		{
 			case 'q':
 				chessman[manPtr].Type = Queen;
				SAN += "=Q";
 				break;
 			case 'b':
 				chessman[manPtr].Type = Bishop;
				SAN += "=B";
 				break;
 			case 'n':
 				chessman[manPtr].Type = Knight;
				SAN += "=N";
 				break;
 			case 'r':
 				chessman[manPtr].Type = Rook;
				SAN += "=R";
 				break;
 			default:
 				break;
 		}
	}
	strcpy( chessMove.SAN, SAN.latin1() );
}
///////////////////////////////////////
//
//	logic::board
//
///////////////////////////////////////
TQString logic::board( void )
{
	TQString output;
	int tmp(0), tmpMan(0), cR(7), cF(0);

	while( tmp != 64 )
	{
		tmpMan = current[ Pointer( cF, cR ) ].ManPtr;
		if( tmpMan == Null )
			output += '-';
		else
		{
			switch( chessman[ tmpMan ].Type )
			{
				case King:
					if( chessman[ tmpMan ].Army == WHITE )
						output += 'K';
					else
						output += 'k';
					break;
				case Queen:
					if( chessman[ tmpMan ].Army == WHITE )
						output += 'Q';
					else
						output += 'q';
					break;
				case Bishop:
					if( chessman[ tmpMan ].Army == WHITE )
						output += 'B';
					else
						output += 'b';
					break;
				case Knight:
					if( chessman[ tmpMan ].Army == WHITE )
						output += 'N';
					else
						output += 'n';
					break;
				case Rook:
					if( chessman[ tmpMan ].Army == WHITE )
						output += 'R';
					else
						output += 'r';
					break;
				case Pawn:	// Fall through
				default:
					if( chessman[ tmpMan ].Army == WHITE )
						output += 'P';
					else
						output += 'p';
					break;
			}
		}
		cF++;
		tmp++;
		if( cF == 8 )
		{
			cF = 0;
			cR--;
		}
	}

	if( CastleFlag[WHITE] & ( CF_King | CF_RookK ) )
		output += '1';
	else
		output += '0';
	if( CastleFlag[WHITE] & ( CF_King | CF_RookQ ) )
		output += '1';
	else
		output += '0';
	if( CastleFlag[BLACK] & ( CF_King | CF_RookK ) )
		output += '1';
	else
		output += '0';
	if( CastleFlag[BLACK] & ( CF_King | CF_RookQ ) )
		output += '1';
	else
		output += '0';

	return output;
}
///////////////////////////////////////
//
//	logic::setBoard
//
///////////////////////////////////////
void logic::setBoard( const TQString &board, const short ppf )
{
	TQChar piece;
	int tmp(0), tmp2(0), cR(7), cF(0);

	clearBoard();
	if( board.length() < 64 )
	{
		kdWarning() << "logic::setBoard: Was passed a string that is less than 64 bytes long." << endl;
		return;
	}
	while( tmp != 64 )
	{
		piece = board.at(tmp++);
		switch( piece.lower() )
		{
			case 'k':
				if( piece == 'K' ) chessman[tmp2].Army = WHITE;
				else chessman[tmp2].Army = BLACK;
				chessman[tmp2].Type = King;
				break;
			case 'q':
				if( piece == 'Q' ) chessman[tmp2].Army = WHITE;
				else chessman[tmp2].Army = BLACK;
				chessman[tmp2].Type = Queen;
				break;
			case 'b':
				if( piece == 'B' ) chessman[tmp2].Army = WHITE;
				else chessman[tmp2].Army = BLACK;
				chessman[tmp2].Type = Bishop;
				break;
			case 'n':
				if( piece == 'N' ) chessman[tmp2].Army = WHITE;
				else chessman[tmp2].Army = BLACK;
				chessman[tmp2].Type = Knight;
				break;
			case 'r':
				if( piece == 'R' ) chessman[tmp2].Army = WHITE;
				else chessman[tmp2].Army = BLACK;
				chessman[tmp2].Type = Rook;
				break;
			case 'p':
				if( piece == 'P' ) chessman[tmp2].Army = WHITE;
				else chessman[tmp2].Army = BLACK;
				chessman[tmp2].Type = Pawn;
				break;
			default:
				break;
		}
		if( piece != '-' )
		{
			chessman[tmp2].Rank = cR;
			chessman[tmp2].File = cF;
			current[ Pointer( cF, cR ) ].ManPtr = tmp2;
			tmp2++;
		}
		cF++;
		if( cF == 8 )
		{
			cF = 0;
			cR--;
		}
	}
	CastleFlag[WHITE] = 0;
	CastleFlag[BLACK] = 0;
	if( board.at(64) == '1' ) CastleFlag[WHITE] += CF_RookK;
	if( board.at(65) == '1' ) CastleFlag[WHITE] += CF_RookQ;
	if( board.at(66) == '1' ) CastleFlag[BLACK] += CF_RookK;
	if( board.at(67) == '1' ) CastleFlag[BLACK] += CF_RookQ;
	if( CastleFlag[WHITE] ) CastleFlag[WHITE] += CF_King;
	if( CastleFlag[BLACK] ) CastleFlag[BLACK] += CF_King;
	/* Update enpassant record */
	if( ppf != -2 )
	{
		enPassant[ !OnMove ] = Null;
		if( ppf != -1 )
			enPassant[ OnMove ] = ppf + 24 + ( ( OnMove == BLACK ) << 3 );
	}
}
///////////////////////////////////////
//
//	logic::getKing
//
///////////////////////////////////////
int logic::getKing( const bool Army )
{
	int tmp;
	for( tmp = 0; tmp < 64; tmp++ )
	{
		if( ( chessman[tmp].Army == Army ) && ( chessman[tmp].Type == King ) )
			if( isChessman( tmp ) )
			{
				return Pointer( chessman[tmp].File, chessman[tmp].Rank );
			}
	}
	return Null;
}
///////////////////////////////////////
//
//	logic::isCheck
//
///////////////////////////////////////
bool logic::isCheck( const bool Army )
{
	char tmp(0), currentKing( getKing(Army) );

	for( tmp = 0; tmp < 64; tmp++ )
	{
		if( chessman[tmp].Army != Army )
			if( isChessman( tmp ) )
			{
				HashLegal( tmp );
				if( current[ currentKing ].Note == NOTE_ATTACK ) return TRUE;
			}
	}
	return FALSE;
}
///////////////////////////////////////
//
//	logic::isLegal
//
///////////////////////////////////////
bool logic::isLegal( const bool Army )
{
	int tmp(0), tmp2(0), count(0);

	for( tmp2 = 0; tmp2 < 64; tmp2++ )
	{
		if( chessman[tmp2].Army == Army )
			if( isChessman( tmp2 ) )
			{
				ManPtr = tmp2;
				_HashLegal();
				count = 0;
				tmp = 0;
				while( tmp < 64 ) count += ( current[tmp++].Note >= NOTE_MOVE );
				if( count ) return TRUE;
			}
	}
	return FALSE;
}
///////////////////////////////////////
//
//	logic::isDraw
//
///////////////////////////////////////
bool logic::isDraw( const bool Army )
{
	bool haveBishop(FALSE);
	bool haveBishopDiag(FALSE);
	bool haveKnight(FALSE);
	bool EnemyBishop(FALSE);
	bool EnemyBishopDiag(TRUE);
	bool EnemyKnight(FALSE);
	int tmp(0);

	if( !isLegal( Army ) ) return TRUE;
	for( tmp = 0; tmp < 64; tmp++ )
	{
		if( !isChessman( tmp ) ) continue;
		if( chessman[tmp].Type == Queen ) return FALSE;
		if( chessman[tmp].Type == Pawn ) return FALSE;
		if( chessman[tmp].Type == Rook ) return FALSE;
		/* Enemy guys */
		if( chessman[tmp].Army != Army )
		{
			if( chessman[tmp].Type == Bishop )
			{
				if( EnemyBishop == TRUE ) return FALSE;
				EnemyBishopDiag = abs( ( current[tmp].Rank % 2 ) - ( current[tmp].File % 2 ) );
				EnemyBishop = TRUE;
			}
			if( chessman[tmp].Type == Knight )
			{
				if( EnemyKnight == TRUE ) return FALSE;
				EnemyKnight = TRUE;
			}			
			continue;
		}
		/* Our guys */
		if( chessman[tmp].Type == Bishop )
		{
			if( haveBishop == TRUE ) return FALSE;
			haveBishopDiag = abs( ( current[tmp].Rank % 2 ) - ( current[tmp].File % 2 ) );
			haveBishop = TRUE;
		}
		if( chessman[tmp].Type == Knight )
		{
			if( haveKnight == TRUE ) return FALSE;
			haveKnight = TRUE;
		}					
	}
	if( haveKnight && EnemyKnight ) return FALSE;
	if( haveBishop && EnemyKnight ) return FALSE;
	if( haveKnight && EnemyBishop ) return FALSE;
	if( haveKnight && haveBishop ) return FALSE;
	if( EnemyKnight && EnemyBishop ) return FALSE;
	if( ( haveBishop && EnemyBishop ) && ( haveBishopDiag != EnemyBishopDiag ) ) return FALSE;
	return TRUE;
}
///////////////////////////////////////
//
//	logic::setBoardFromFen
//
///////////////////////////////////////
void logic::setBoardFromFen(const TQString &fen)
{
  clearBoard();
  int j = 0; //position in string
  int r = 7; //rank
  int f = 0; //file
  int p = 63; //chessman number
  TQChar c; //current letter

  for (j=0;j<120 && p>=0 ;j++) {
  c = fen[j];

  if (c.isLetter()) {
    //describing a piece
    if (c == 'r') {
       chessman[p].Army = BLACK;
       chessman[p].Type = Rook;

    }
    if (c=='q') {
       chessman[p].Army = BLACK;
       chessman[p].Type = Queen;
    }
    if (c=='k') {
       chessman[p].Army = BLACK;
       chessman[p].Type = King;
    }
    if (c=='p') {
       chessman[p].Army = BLACK;
       chessman[p].Type = Pawn;
    }
    if (c=='n') {
       chessman[p].Army = BLACK;
       chessman[p].Type = Knight;
    }
    if (c=='b') {
       chessman[p].Army = BLACK;
       chessman[p].Type = Bishop;
    }
    //black pieces
    if (c == 'R') {
       chessman[p].Army = WHITE;
       chessman[p].Type = Rook;

    }
    if (c=='Q') {
       chessman[p].Army = WHITE;
       chessman[p].Type = Queen;
    }
    if (c=='K') {
       chessman[p].Army = WHITE;
       chessman[p].Type = King;
    }
    if (c=='P') {
       chessman[p].Army = WHITE;
       chessman[p].Type = Pawn;
    }
    if (c=='N') {
       chessman[p].Army = WHITE;
       chessman[p].Type = Knight;
    }
    if (c=='B') {
       chessman[p].Army = WHITE;
       chessman[p].Type = Bishop;
    }
  chessman[p].Rank = r;
  chessman[p].File = f;
  current[ Pointer( chessman[p].File, chessman[p].Rank ) ].ManPtr = p;
  p--;
  f++;
  }
  if (c.isNumber()) {
  //describing blank squares

  p = p - c.digitValue();
  f = f + c.digitValue();
  }

  if (c == '/') {
  //describing new rank
  r--;
  f=0;
  }

  }

  do {
  j++;
  c = fen[j];
  } while (c == '/' || c == ' ');
  if (c=='w')
    OnMove = WHITE;
  if (c=='b')
    OnMove = BLACK;

}
///////////////////////////////////////
//
//	logic::Move
//
///////////////////////////////////////
bool logic::Move( void )
{
	dlg_promote *ProDlg;
	int tmp;
	int fromPtr, toPtr, manPtr;

	fromPtr = Pointer( chessMove.fromFile, chessMove.fromRank );
	toPtr = Pointer( chessMove.toFile, chessMove.toRank );
	if( ( fromPtr == Null ) || ( toPtr == Null ) )
		return FALSE;
	manPtr = current[fromPtr].ManPtr;
	if( manPtr == Null )
		return FALSE;

	HashLegal( manPtr );
	/* Only proceed if this is a move */
	if( current[toPtr].Note < NOTE_MOVE )
		return FALSE; // This depends on all moves being higher value than NOTE_MOVE,
									// while all non-moves are less.

	/* Take care of moving the rook in a caste */
	if( current[toPtr].Note == NOTE_CASTLE )
	{
		if( chessMove.toFile == 6 )
		{
			for( tmp = 0; tmp < 64; tmp++ )
			{
				if( ( chessman[tmp].Army == chessman[manPtr].Army ) &&
						( chessman[tmp].Type == Rook ) &&
						( chessman[tmp].File == 7 ) )
						{
							chessman[tmp].File = 5;
							current[ Pointer( 7, ( 7 * ( chessman[tmp].Army == BLACK ) ) ) ].ManPtr = Null;
							current[ Pointer( 5, ( 7 * ( chessman[tmp].Army == BLACK ) ) ) ].ManPtr = tmp;
							break;
						}
			}
		}
		if( chessMove.toFile == 2 )
		{
			for( tmp = 0; tmp < 64; tmp++ )
			{
				if( ( chessman[tmp].Army == chessman[manPtr].Army ) &&
						( chessman[tmp].Type == Rook ) &&
						( chessman[tmp].File == 0 ) )
						{
							chessman[tmp].File = 3;
							current[ Pointer( 0, ( 7 * ( chessman[tmp].Army == BLACK ) ) ) ].ManPtr = Null;
							current[ Pointer( 3, ( 7 * ( chessman[tmp].Army == BLACK ) ) ) ].ManPtr = tmp;
							break;
						}
			}
		}
	}
	/* Handle the 50 Move Rule */
	MoveCounter++;
	if( chessman[manPtr].Type == Pawn ) MoveCounter = 0;
	if( current[ toPtr ].ManPtr != Null )
	{
		MoveCounter = 0;
		chessMove.ManTaken = current[ toPtr ].ManPtr;
	}
	/* Check for Pawn Promotion */
	if( ( chessMove.toRank == ( 7 * ( chessman[manPtr].Army == WHITE ) ) ) &&
			( chessman[manPtr].Type == Pawn ) )
			{
				if( ( ( OnMove == WHITE ) && ( Param->type(WHITE) == PLAYERLOCAL ) ) ||
						( ( OnMove == BLACK ) && ( Param->type(BLACK) == PLAYERLOCAL ) ) )
						{
							if( Resource->OPTION_Auto_Queen == TRUE ) chessMove.Promote = 'q';
							else
							{
								/* Prompt user for promotion */
								ProDlg = new dlg_promote( 0, "promotedialog", Resource );
								ProDlg->Init( OnMove );
								chessMove.Promote = ProDlg->exec();
								delete ProDlg;
								/* Default to Queen if the user quit the dialog without choosing */
								if( ( chessMove.Promote != 'q' ) &&
										( chessMove.Promote != 'b' ) &&
										( chessMove.Promote != 'n' ) &&
										( chessMove.Promote != 'r' ) ) chessMove.Promote = 'q';
							}
						}
			}
	/* Write CAN & SAN Notation for this move */
	writeCAN();
	writeSAN();
	/* Make the move */
	chessman[manPtr].File = chessMove.toFile;
	chessman[manPtr].Rank = chessMove.toRank;
	current[fromPtr].ManPtr = Null;
	current[toPtr].ManPtr = manPtr;
	/* Remove pawns taken en passant */
	if( current[toPtr].Note == NOTE_ENPASSANT )
	{
		MoveCounter = 0;
		chessMove.ManTaken = current[ enPassant[ 1 - chessman[manPtr].Army ] ].ManPtr;
		current[ enPassant[ 1 - chessman[manPtr].Army ] ].ManPtr = Null;
	}
	/* Take care of en passant data */
	if( current[toPtr].Note == NOTE_PAWN_DOUBLE )
		enPassant[ chessman[manPtr].Army ] = toPtr;
	enPassant[ 1 - chessman[manPtr].Army ] = Null;
	/* Handle castle flags */
	if( chessman[manPtr].Type == King )
			CastleFlag[ chessman[manPtr].Army ] = 0;
	if( ( chessman[manPtr].Type == Rook ) && ( chessMove.fromFile == 0 ) )
			CastleFlag[ chessman[manPtr].Army ] -= CF_RookQ;
	if( ( chessman[manPtr].Type == Rook ) && ( chessMove.fromFile == 7 ) )
			CastleFlag[ chessman[manPtr].Army ] -= CF_RookK;
	return TRUE;
}
///////////////////////////////////////
//
//	logic::HashLegal
//
///////////////////////////////////////
void logic::HashLegal( const char Man, const bool Recursion )
{
	char tmp;
	tmp = ManPtr;
	ManPtr = Man;
	_HashLegal( Recursion );
	ManPtr = tmp;
}
void logic::_HashLegal( const bool Recursion )
{
	/* Used for loops and positions */
	int Ptr(0), tmp(0), tmp2(0);

	/* Used to calculate a position relative to a given position */
	int dirF(0), dirR(0);

	/* Used to monitor the King inside the Monster */
	int currentKing(0), _castleFlag(0);

	if( !isChessman(ManPtr) )
		return;

	copyPositions( current, hash );

	while( tmp < 64 )
		hash[tmp++].Note = NOTE_NONE;

	switch( chessman[ManPtr].Type )
	{
		/* ROOK & QUEEN */
		case Rook:
		case Queen:
			/* Positive Rank Movement */
			for( tmp = 1; tmp < 8; tmp++ )
			{
				Ptr = CalcPointer( 0, tmp );
				if( Ptr == Null ) break;
				if( hash[Ptr].ManPtr == Null )
				{
					hash[Ptr].Note = NOTE_MOVE;
					continue;
				}
				if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
				{
					hash[Ptr].Note = NOTE_ATTACK;
					break;
				}
				if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
				break;
			}
			/* Negitive Rank Movement */
			for( tmp = -1; tmp > -8; tmp-- )
			{
				Ptr = CalcPointer( 0, tmp );
				if( Ptr == Null ) break;
				if( hash[Ptr].ManPtr == Null )
				{
					hash[Ptr].Note = NOTE_MOVE;
					continue;
				}
				if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
				{
					hash[Ptr].Note = NOTE_ATTACK;
					break;
				}
				if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
				break;
			}
			/* Positive File Movement */
			for( tmp = 1; tmp < 8; tmp++ )
			{
				Ptr = CalcPointer( tmp, 0 );
				if( Ptr == Null ) break;
				if( hash[Ptr].ManPtr == Null )
				{
					hash[Ptr].Note = NOTE_MOVE;
					continue;
				}
				if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
				{
					hash[Ptr].Note = NOTE_ATTACK;
					break;
				}
				if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
				break;
			}
			/* Negative File Movement */
			for( tmp = -1; tmp > -8; tmp-- )
			{
				Ptr = CalcPointer( tmp, 0 );
				if( Ptr == Null ) break;
				if( hash[Ptr].ManPtr == Null )
				{
					hash[Ptr].Note = NOTE_MOVE;
					continue;
				}
				if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
				{
					hash[Ptr].Note = NOTE_ATTACK;
					break;
				}
				if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
				break;
			}
			if( chessman[ManPtr].Type == Rook ) break;
		/* Bishop & Queen */
		case Bishop:
			/* NE Movement */
			for( tmp = 1; tmp < 8; tmp++ )
			{
				Ptr = CalcPointer( tmp, tmp );
				if( Ptr == Null ) break;
				if( hash[Ptr].ManPtr == Null )
				{
					hash[Ptr].Note = NOTE_MOVE;
					continue;
				}
				if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
				{
					hash[Ptr].Note = NOTE_ATTACK;
					break;
				}
				if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
				break;
			}
			/* NW Movement */
			for( tmp = -1; tmp > -8; tmp-- )
			{
				Ptr = CalcPointer( tmp, abs(tmp) );
				if( Ptr == Null ) break;
				if( hash[Ptr].ManPtr == Null )
				{
					hash[Ptr].Note = NOTE_MOVE;
					continue;
				}
				if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
				{
					hash[Ptr].Note = NOTE_ATTACK;
					break;
				}
				if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
				break;
			}
			/* SW Movement */
			for( tmp = -1; tmp > -8; tmp-- )
			{
				Ptr = CalcPointer( tmp, tmp );
				if( Ptr == Null ) break;
				if( hash[Ptr].ManPtr == Null )
				{
					hash[Ptr].Note = NOTE_MOVE;
					continue;
				}
				if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
				{
					hash[Ptr].Note = NOTE_ATTACK;
					break;
				}
				if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
				break;
			}
			/* SE Movement */
			for( tmp = -1; tmp > -8; tmp-- )
			{
				Ptr = CalcPointer( abs(tmp), tmp );
				if( Ptr == Null ) break;
				if( hash[Ptr].ManPtr == Null )
				{
					hash[Ptr].Note = NOTE_MOVE;
					continue;
				}
				if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
				{
					hash[Ptr].Note = NOTE_ATTACK;
					break;
				}
				if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
				break;
			}
			break;
		/* Knight */
		case Knight:
			for( tmp = 0; tmp < 8; tmp++ )
			{
				switch( tmp )
				{
					case 0:
						Ptr = CalcPointer( -1, 2 );
						break;
					case 1:
						Ptr = CalcPointer( 1, 2 );
						break;
					case 2:
						Ptr = CalcPointer( 2, 1 );
						break;
					case 3:
						Ptr = CalcPointer( 2, -1 );
						break;
					case 4:
						Ptr = CalcPointer( 1, -2 );
						break;
					case 5:
						Ptr = CalcPointer( -1, -2 );
						break;
					case 6:
						Ptr = CalcPointer( -2, -1 );
						break;
					case 7:
						Ptr = CalcPointer( -2, 1 );
						break;
					default:
						break;
				}
				if( Ptr != Null )
				{
					if( hash[Ptr].ManPtr == Null )
						hash[Ptr].Note = NOTE_MOVE;
					else
					{
						if( Recursion == TRUE )
							hash[Ptr].Note = NOTE_ATTACK;
						if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army )
							hash[Ptr].Note = NOTE_ATTACK;
					}
				}
			}
			break;
		/* King */
		case King:
			dirF = -1;
			dirR = 1;
			while(1)
			{
				Ptr = CalcPointer( dirF, dirR );
				if( Ptr != Null )
				{
					if( hash[Ptr].ManPtr == Null ) hash[Ptr].Note = NOTE_MOVE;
					else
					{
						if( Recursion == TRUE ) hash[Ptr].Note = NOTE_ATTACK;
						if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) hash[Ptr].Note = NOTE_ATTACK;
					}
				}
				dirF++;
				if( dirF == 2 )
				{
					dirF = -1;
					dirR--;
				}
				if( dirR == -2 ) break;
				if( ( dirR == 0 ) && ( dirF == 0 ) ) dirF++;
			}
			/* Check for castles */
			if( Recursion == FALSE )
			{
				/* Can the King castle at all? */
				if( CastleFlag[ chessman[ ManPtr ].Army ] & CF_King )
				{
					dirR = 0;
					/* How about with the Queen's Rook? */
					if( CastleFlag[ chessman[ ManPtr ].Army ] & CF_RookQ )
					{
						if( hash[ CalcPointer( -1, dirR ) ].ManPtr == Null )
						{
							if( hash[ CalcPointer( -2, dirR ) ].ManPtr == Null )
							{
								if( hash[ CalcPointer( -3, dirR ) ].ManPtr == Null )
								{
									hash[ CalcPointer( -2, dirR ) ].Note = NOTE_CASTLE;
									_castleFlag |= CF_RookQ;
								}
							}
						}
					}
					/* King's Rook? */
					if( CastleFlag[ chessman[ ManPtr ].Army ] & CF_RookK )
					{
						if( hash[ CalcPointer( 1, dirR ) ].ManPtr == Null )
						{
							if( hash[ CalcPointer( 2, dirR ) ].ManPtr == Null )
							{
								hash[ CalcPointer( 2, dirR ) ].Note = NOTE_CASTLE;
								_castleFlag |= CF_RookK;
							}
						}
					}
				}
			}
			break;
		/* PAWN */
		default:
			/* Get direction of movement */
			if( chessman[ManPtr].Army == WHITE ) dirR = 1;
			else dirR = -1;
			if( Recursion == FALSE )
			{
				/* Forward 1 square */
				Ptr = CalcPointer( 0, dirR );
				if( ( Ptr != Null ) && ( hash[Ptr].ManPtr == Null ) )
				{
					hash[Ptr].Note = NOTE_MOVE;
					tmp = 1 + ( 5 * ( chessman[ManPtr].Army == BLACK ) );
					if( chessman[ManPtr].Rank == tmp )
					{
						/* Forward 2 squares */
						dirR = dirR << 1;
						Ptr = CalcPointer( 0, dirR );
						if( ( Ptr != Null ) && ( hash[Ptr].ManPtr == Null ) ) hash[Ptr].Note = NOTE_PAWN_DOUBLE;
						dirR = dirR >> 1;
					}
				}
			}
			if( Recursion == TRUE )
			{
				/* Attack Left */
				Ptr = CalcPointer( -1, dirR );
				if( Ptr != Null ) hash[Ptr].Note = NOTE_ATTACK;
				/* Attack Right */
				Ptr = CalcPointer( 1, dirR );
				if( Ptr != Null ) hash[Ptr].Note = NOTE_ATTACK;
			}
			else
			{
				/* Attack Left */
				Ptr = CalcPointer( -1, dirR );
				if( ( Ptr != Null ) && ( hash[Ptr].ManPtr != Null ) )
				{
					if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) hash[Ptr].Note = NOTE_ATTACK;
				}
				/* Attack Right */
				Ptr = CalcPointer( 1, dirR );
				if( ( Ptr != Null ) && ( hash[Ptr].ManPtr != Null ) )
				{
					if( chessman[hash[Ptr].ManPtr].Army != chessman[ManPtr].Army ) hash[Ptr].Note = NOTE_ATTACK;
				}
				/* Attack en Passant Left */
				Ptr = CalcPointer( -1, 0 );
				if( ( Ptr != Null ) && ( enPassant[ 1 - chessman[ManPtr].Army ] == Ptr ) )
				{
					Ptr = CalcPointer( -1, dirR );
					hash[Ptr].Note = NOTE_ENPASSANT;
				}
				/* Attack en Passant Right */
				Ptr = CalcPointer( 1, 0 );
				if( ( Ptr != Null ) && ( enPassant[ 1 - chessman[ManPtr].Army ] == Ptr ) )
				{
					Ptr = CalcPointer( 1, dirR );
					hash[Ptr].Note = NOTE_ENPASSANT;
				}
			}
			break;
	}
	/*                     THE MONSTER                      */
	/* Remove all possible moves that would either put your */
	/* king into check or wouldn't stop a check in progress */
	if( Recursion == FALSE )
	{
		/* Make Backups */
		copyPositions( hash, hashBackup );
		copyChessmen( chessman, chessmanBackup );

		/* Find the King's Position */
		currentKing = getKing( chessman[ManPtr].Army );

		/* Remove castles under specific conditions */
		if( _castleFlag )
		{
			for( tmp = 0; tmp < 64; tmp++ )
			{
				if( !isChessman( tmp ) ) continue;
				if( ( chessman[tmp].Army != chessman[ManPtr].Army ) && ( chessman[tmp].Type != Null ) )
				{
					HashLegal( tmp, TRUE );
					/* Is a check in progress? */
					if( hash[ currentKing ].Note == NOTE_ATTACK )
					{
						for( tmp2 = 0; tmp2 < 64; tmp2++ )
							if( hashBackup[tmp2].Note == NOTE_CASTLE ) hashBackup[tmp2].Note = NOTE_NONE;
						break;
					}
					else
					{
						/* Store ManPtr in dirF so we can use ManPtr */
						dirF = ManPtr;
						ManPtr = hashBackup[ currentKing ].ManPtr;
						/* Is the path to Queenside in check? */
						if( _castleFlag & CF_RookQ )
						{
							if( ( hash[ CalcPointer( -1, 0 ) ].Note == NOTE_MOVE ) ||
									( hash[ CalcPointer( -2, 0 ) ].Note == NOTE_MOVE ) )
									{
										hashBackup[ CalcPointer( -2, 0 ) ].Note = NOTE_NONE;
										_castleFlag -= CF_RookQ;
									}
						}
						/* Is the path to Kingside in check? */
						if( _castleFlag & CF_RookK )
						{
							if( ( hash[ CalcPointer( 1, 0 ) ].Note == NOTE_MOVE ) ||
									( hash[ CalcPointer( 2, 0 ) ].Note == NOTE_MOVE ) )
									{
										hashBackup[ CalcPointer( 2, 0 ) ].Note = NOTE_NONE;
										_castleFlag -= CF_RookK;
									}
						}
						/* Restore ManPtr */
						ManPtr = dirF;
					}
				}
			}
		} //    <- End Castle Checks

		/* Check all possible moves */
		for( tmp = 0; tmp < 64; tmp++ )
		{
			/* Only proceed if this is a move */
			if( hashBackup[tmp].Note < NOTE_MOVE )
				continue; // This depends on all moves being higher value than NOTE_MOVE,
									// while all non-moves are less.

			/* Pretend we moved here... what would happen? */
			current[ Pointer( chessman[ManPtr].File, chessman[ManPtr].Rank ) ].ManPtr = Null;
			chessman[ManPtr].File = hashBackup[tmp].File;
			chessman[ManPtr].Rank = hashBackup[tmp].Rank;
			current[tmp].ManPtr = ManPtr;
			if( current[tmp].Note == NOTE_ENPASSANT )
			{
				current[ enPassant[ 1 - chessman[ManPtr].Army ] ].ManPtr = Null;
			}

			/* Recalc King pos, as we may have just moved him */
			currentKing = getKing( chessman[ManPtr].Army );

			/* Rehash in new position. If King is now under check, then	*/
			/* we can't use this move and it's removed from contention */
			for( tmp2 = 0; tmp2 < 64; tmp2++ )
			{
				if( chessman[tmp2].Army != chessman[ManPtr].Army )
					if( isChessman( tmp2 ) )
					{
						HashLegal( tmp2, TRUE );
						if( hash[ currentKing ].Note == NOTE_ATTACK )
						{
							hashBackup[tmp].Note = NOTE_NONE;
						}
					}
			}

			/* Restore the playground */
			copyPositions( hashBackup, current );
			copyChessmen( chessmanBackup, chessman );
		} //    <- End of 'Check All Moves' loop

		copyPositions( hashBackup, current );
		copyPositions( hashBackup, hash );
		copyChessmen( chessmanBackup, chessman );
	}
}
