|
|
|
/***************************************************************************
|
|
|
|
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( register 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 )
|
|
|
|
{
|
|
|
|
register 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);
|
|
|
|
register 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;
|
|
|
|
register 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 )
|
|
|
|
{
|
|
|
|
register 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 )
|
|
|
|
{
|
|
|
|
register 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 )
|
|
|
|
{
|
|
|
|
register 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 */
|
|
|
|
register 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 );
|
|
|
|
}
|
|
|
|
}
|